From abd5756609275cbc37c1400da3e24f2fdcadd291 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Tue, 13 Dec 2022 21:54:16 +0100 Subject: [PATCH] Add the abillity to add css-classes to POI and HTML-Markers and add detail field to the POI-Marker --- .../de/bluecolored/bluemap/api/WebApp.java | 2 + .../bluemap/api/markers/DetailMarker.java | 50 ++++++++ .../api/markers/DistanceRangedMarker.java | 7 ++ .../bluemap/api/markers/ElementMarker.java | 113 ++++++++++++++++++ .../bluemap/api/markers/HtmlMarker.java | 86 +++++++------ .../bluemap/api/markers/ObjectMarker.java | 42 ++----- .../bluemap/api/markers/POIMarker.java | 98 +++++++++++++-- 7 files changed, 319 insertions(+), 79 deletions(-) create mode 100644 src/main/java/de/bluecolored/bluemap/api/markers/DetailMarker.java create mode 100644 src/main/java/de/bluecolored/bluemap/api/markers/ElementMarker.java diff --git a/src/main/java/de/bluecolored/bluemap/api/WebApp.java b/src/main/java/de/bluecolored/bluemap/api/WebApp.java index 39a215f..ce35634 100644 --- a/src/main/java/de/bluecolored/bluemap/api/WebApp.java +++ b/src/main/java/de/bluecolored/bluemap/api/WebApp.java @@ -94,6 +94,8 @@ public interface WebApp { */ void registerScript(String url); + // ------ + /** * @deprecated You should use the {@link #getWebRoot()} method to create the image-files you need, or store map/marker * specific images in the map's storage (See: {@link BlueMapMap#getAssetStorage()})! diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/DetailMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/DetailMarker.java new file mode 100644 index 0000000..bc3bb88 --- /dev/null +++ b/src/main/java/de/bluecolored/bluemap/api/markers/DetailMarker.java @@ -0,0 +1,50 @@ +package de.bluecolored.bluemap.api.markers; + +/** + * @see POIMarker + * @see ShapeMarker + * @see ExtrudeMarker + * @see LineMarker + */ +public interface DetailMarker { + + /** + * Getter for the detail of this marker. The label can include html-tags. + * @return the detail of this {@link Marker} + */ + String getDetail(); + + /** + * Sets the detail of this {@link Marker}. The detail can include html-tags.
+ * This is the text that will be displayed on the popup when you click on this marker. + *

+ * Important:
+ * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
+ * Make sure you escape all html-tags from possible user inputs to prevent possible + * XSS-Attacks on the web-client! + *

+ * + * @param detail the new detail for this {@link ObjectMarker} + */ + void setDetail(String detail); + + interface Builder { + + /** + * Sets the detail of the {@link Marker}. The detail can include html-tags.
+ * This is the text that will be displayed on the popup when you click on the marker. + *

+ * Important:
+ * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
+ * Make sure you escape all html-tags from possible user inputs to prevent possible + * XSS-Attacks on the web-client! + *

+ * + * @param detail the new detail for the {@link Marker} + * @return this builder for chaining + */ + B detail(String detail); + + } + +} diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java index 4fcbbf6..c8d1c24 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/DistanceRangedMarker.java @@ -27,6 +27,13 @@ package de.bluecolored.bluemap.api.markers; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.api.debug.DebugDump; +/** + * @see HtmlMarker + * @see POIMarker + * @see ShapeMarker + * @see ExtrudeMarker + * @see LineMarker + */ @DebugDump public abstract class DistanceRangedMarker extends Marker { diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/ElementMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/ElementMarker.java new file mode 100644 index 0000000..10bfc64 --- /dev/null +++ b/src/main/java/de/bluecolored/bluemap/api/markers/ElementMarker.java @@ -0,0 +1,113 @@ +package de.bluecolored.bluemap.api.markers; + +import com.flowpowered.math.vector.Vector2i; + +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Pattern; + +/** + * @see HtmlMarker + * @see POIMarker + */ +public interface ElementMarker { + + Pattern STYLE_CLASS_PATTERN = Pattern.compile("-?[_a-zA-Z]+[_a-zA-Z0-9-]*"); + + /** + * Getter for the position (in pixels) where the element is anchored to the map. + * @return the anchor-position in pixels + */ + Vector2i getAnchor(); + + /** + * Sets the position (in pixels) where the element is anchored to the map. + * @param anchor the anchor-position in pixels + */ + void setAnchor(Vector2i anchor); + + /** + * Sets the position (in pixels) where the element is anchored to the map. + * @param x the anchor-x-position in pixels + * @param y the anchor-y-position in pixels + */ + default void setAnchor(int x, int y) { + setAnchor(new Vector2i(x, y)); + } + + /** + * Getter for an (unmodifiable) collection of CSS-classed that the element of this marker will have. + * @return the style-classes of this element-marker + */ + Collection getStyleClasses(); + + /** + * Sets the CSS-classes that the element of this marker will have.
+ * All classes must match -?[_a-zA-Z]+[_a-zA-Z0-9-]*
+ * Any existing classes on this marker will be removed. + * @param styleClasses the style-classes this element-marker will have + */ + default void setStyleClasses(String... styleClasses) { + this.setStyleClasses(Arrays.asList(styleClasses)); + } + + /** + * Sets the CSS-classes that the element of this marker will have.
+ * All classes must match -?[_a-zA-Z]+[_a-zA-Z0-9-]*
+ * Any existing classes on this marker will be removed. + * @param styleClasses the style-classes this element-marker will have + */ + void setStyleClasses(Collection styleClasses); + + /** + * Adds the CSS-classes that the element of this marker will have.
+ * All classes must match -?[_a-zA-Z]+[_a-zA-Z0-9-]*
+ * @param styleClasses the style-classes this element-marker will have + */ + default void addStyleClasses(String... styleClasses) { + this.setStyleClasses(Arrays.asList(styleClasses)); + } + + /** + * Adds the CSS-classes that the element of this marker will have.
+ * All classes must match -?[_a-zA-Z]+[_a-zA-Z0-9-]*
+ * @param styleClasses the style-classes this element-marker will have + */ + void addStyleClasses(Collection styleClasses); + + interface Builder { + + /** + * Sets the position (in pixels) where the element is anchored to the map. + * @param anchor the anchor-position in pixels + * @return this builder for chaining + */ + B anchor(Vector2i anchor); + + /** + * Adds the CSS-classes that the element of this marker will have.
+ * All classes must match -?[_a-zA-Z]+[_a-zA-Z0-9-]*
+ * @return this builder for chaining + */ + B styleClasses(String... styleClasses); + + /** + * Removes any existing style-classes from this builder. + * @see #styleClasses(String...) + * @return this builder for chaining + */ + B clearStyleClasses(); + + /** + * Sets the position (in pixels) where the element is anchored to the map. + * @param x the anchor-x-position in pixels + * @param y the anchor-y-position in pixels + * @return this builder for chaining + */ + default B anchor(int x, int y) { + return anchor(new Vector2i(x, y)); + } + + } + +} diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java index a8d4d03..8b6a806 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/HtmlMarker.java @@ -29,13 +29,16 @@ import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3d; import de.bluecolored.bluemap.api.debug.DebugDump; -import java.util.Objects; +import java.util.*; /** * A marker that is a html-element placed somewhere on the map. */ +@SuppressWarnings("FieldMayBeFinal") @DebugDump -public class HtmlMarker extends DistanceRangedMarker { +public class HtmlMarker extends DistanceRangedMarker implements ElementMarker { + + private Set classes = new HashSet<>(); private Vector2i anchor; private String html; @@ -82,31 +85,16 @@ public class HtmlMarker extends DistanceRangedMarker { this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); } - /** - * Getter for the position (in pixels) where the html-element is anchored to the map. - * @return the anchor-position in pixels - */ + @Override public Vector2i getAnchor() { return anchor; } - /** - * Sets the position (in pixels) where the html-element is anchored to the map. - * @param anchor the anchor-position in pixels - */ + @Override public void setAnchor(Vector2i anchor) { this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); } - /** - * Sets the position (in pixels) where the html-element is anchored to the map. - * @param x the anchor-x-position in pixels - * @param y the anchor-y-position in pixels - */ - public void setAnchor(int x, int y) { - setAnchor(new Vector2i(x, y)); - } - /** * Getter for the html-code of this HTML marker * @return the html-code @@ -130,6 +118,28 @@ public class HtmlMarker extends DistanceRangedMarker { this.html = Objects.requireNonNull(html, "html must not be null"); } + @Override + public Collection getStyleClasses() { + return Collections.unmodifiableCollection(this.classes); + } + + @Override + public void setStyleClasses(Collection styleClasses) { + if (!styleClasses.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.clear(); + this.classes.addAll(styleClasses); + } + + @Override + public void addStyleClasses(Collection styleClasses) { + if (!styleClasses.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.addAll(styleClasses); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -158,32 +168,20 @@ public class HtmlMarker extends DistanceRangedMarker { return new Builder(); } - public static class Builder extends DistanceRangedMarker.Builder { + public static class Builder extends DistanceRangedMarker.Builder + implements ElementMarker.Builder { + + Set classes = new HashSet<>(); Vector2i anchor; String html; - /** - * Sets the position (in pixels) where the html-element is anchored to the map. - * @param anchor the anchor-position in pixels - * @return this builder for chaining - */ + @Override public Builder anchor(Vector2i anchor) { this.anchor = anchor; return this; } - /** - * Sets the position (in pixels) where the html-element is anchored to the map. - * @param x the anchor-x-position in pixels - * @param y the anchor-y-position in pixels - * @return this builder for chaining - */ - public Builder anchor(int x, int y) { - this.anchor = new Vector2i(x, y); - return this; - } - /** * Sets the html for the {@link HtmlMarker}. * @@ -200,6 +198,22 @@ public class HtmlMarker extends DistanceRangedMarker { return this; } + @Override + public Builder styleClasses(String... styleClasses) { + Collection styleClassesCollection = Arrays.asList(styleClasses); + if (!styleClassesCollection.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.addAll(styleClassesCollection); + return this; + } + + @Override + public Builder clearStyleClasses() { + this.classes.clear(); + return this; + } + /** * Creates a new {@link HtmlMarker} with the current builder-settings.
* The minimum required settings to build this marker are: diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java index a2b07df..ae35e85 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/ObjectMarker.java @@ -31,8 +31,13 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.Optional; +/** + * @see ShapeMarker + * @see ExtrudeMarker + * @see LineMarker + */ @DebugDump -public abstract class ObjectMarker extends DistanceRangedMarker { +public abstract class ObjectMarker extends DistanceRangedMarker implements DetailMarker { private String detail; @@ -45,26 +50,12 @@ public abstract class ObjectMarker extends DistanceRangedMarker { this.detail = Objects.requireNonNull(label, "label must not be null"); } - /** - * Getter for the detail of this marker. The label can include html-tags. - * @return the detail of this {@link Marker} - */ + @Override public String getDetail() { return detail; } - /** - * Sets the detail of this {@link Marker}. The detail can include html-tags.
- * This is the text that will be displayed on the popup when you click on this marker. - *

- * Important:
- * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
- * Make sure you escape all html-tags from possible user inputs to prevent possible - * XSS-Attacks on the web-client! - *

- * - * @param detail the new detail for this {@link ObjectMarker} - */ + @Override public void setDetail(String detail) { this.detail = Objects.requireNonNull(detail); } @@ -131,25 +122,14 @@ public abstract class ObjectMarker extends DistanceRangedMarker { } public static abstract class Builder> - extends DistanceRangedMarker.Builder { + extends DistanceRangedMarker.Builder + implements DetailMarker.Builder { String detail; String link; boolean newTab; - /** - * Sets the detail of the {@link Marker}. The detail can include html-tags.
- * This is the text that will be displayed on the popup when you click on the marker. - *

- * Important:
- * Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.
- * Make sure you escape all html-tags from possible user inputs to prevent possible - * XSS-Attacks on the web-client! - *

- * - * @param detail the new detail for the {@link Marker} - * @return this builder for chaining - */ + @Override public B detail(String detail) { this.detail = detail; return self(); diff --git a/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java b/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java index b5b5aa8..a4a6d97 100644 --- a/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java +++ b/src/main/java/de/bluecolored/bluemap/api/markers/POIMarker.java @@ -26,13 +26,18 @@ package de.bluecolored.bluemap.api.markers; import com.flowpowered.math.vector.Vector2i; import com.flowpowered.math.vector.Vector3d; -import de.bluecolored.bluemap.api.WebApp; +import de.bluecolored.bluemap.api.BlueMapMap; import de.bluecolored.bluemap.api.debug.DebugDump; -import java.util.Objects; +import java.util.*; +@SuppressWarnings("FieldMayBeFinal") @DebugDump -public class POIMarker extends DistanceRangedMarker { +public class POIMarker extends DistanceRangedMarker implements DetailMarker, ElementMarker { + + private Set classes = new HashSet<>(); + + private String detail; private String icon; private Vector2i anchor; @@ -72,10 +77,21 @@ public class POIMarker extends DistanceRangedMarker { */ public POIMarker(String label, Vector3d position, String iconAddress, Vector2i anchor) { super("poi", label, position); + this.detail = Objects.requireNonNull(label, "label must not be null"); this.icon = Objects.requireNonNull(iconAddress, "iconAddress must not be null"); this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); } + @Override + public String getDetail() { + return detail; + } + + @Override + public void setDetail(String detail) { + this.detail = detail; + } + /** * Getter for the relative address of the icon used to display this {@link POIMarker} * @return the relative web-address of the icon @@ -84,18 +100,20 @@ public class POIMarker extends DistanceRangedMarker { return icon; } - /** - * Getter for the position (in pixels) where the icon is anchored to the map. - * @return the anchor-position in pixels - */ + @Override public Vector2i getAnchor() { return anchor; } + @Override + public void setAnchor(Vector2i anchor) { + this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); + } + /** * Sets the icon for this {@link POIMarker}. * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. - * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * (See: {@link BlueMapMap#getAssetStorage()}) * @param anchorX the x-position of the position (in pixels) where the icon is anchored to the map * @param anchorY the y-position of the position (in pixels) where the icon is anchored to the map */ @@ -106,7 +124,7 @@ public class POIMarker extends DistanceRangedMarker { /** * Sets the icon for this {@link POIMarker}. * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. - * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * (See: {@link BlueMapMap#getAssetStorage()}) * @param anchor the position of the position (in pixels) where the icon is anchored to the map */ public void setIcon(String iconAddress, Vector2i anchor) { @@ -114,6 +132,28 @@ public class POIMarker extends DistanceRangedMarker { this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); } + @Override + public Collection getStyleClasses() { + return Collections.unmodifiableCollection(this.classes); + } + + @Override + public void setStyleClasses(Collection styleClasses) { + if (!styleClasses.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.clear(); + this.classes.addAll(styleClasses); + } + + @Override + public void addStyleClasses(Collection styleClasses) { + if (!styleClasses.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.addAll(styleClasses); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -142,15 +182,25 @@ public class POIMarker extends DistanceRangedMarker { return new Builder(); } - public static class Builder extends DistanceRangedMarker.Builder { + public static class Builder extends DistanceRangedMarker.Builder + implements DetailMarker.Builder, ElementMarker.Builder { + Set classes = new HashSet<>(); + + String detail; String icon; Vector2i anchor; + @Override + public Builder detail(String detail) { + this.detail = detail; + return this; + } + /** * Sets the icon for the {@link POIMarker}. * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. - * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * (See: {@link BlueMapMap#getAssetStorage()}) * @param anchorX the x-position of the position (in pixels) where the icon is anchored to the map * @param anchorY the y-position of the position (in pixels) where the icon is anchored to the map * @return this builder for chaining @@ -162,7 +212,7 @@ public class POIMarker extends DistanceRangedMarker { /** * Sets the icon for the {@link POIMarker}. * @param iconAddress the web-address of the icon-image. Can be an absolute or relative. - * You can also use an address returned by {@link WebApp#createImage(java.awt.image.BufferedImage, String)}. + * (See: {@link BlueMapMap#getAssetStorage()}) * @param anchor the position of the position (in pixels) where the icon is anchored to the map * @return this builder for chaining */ @@ -172,6 +222,12 @@ public class POIMarker extends DistanceRangedMarker { return this; } + @Override + public Builder anchor(Vector2i anchor) { + this.anchor = Objects.requireNonNull(anchor, "anchor must not be null"); + return this; + } + /** * The {@link POIMarker} will use the default icon. (See: {@link #icon(String, Vector2i)}) * @return this builder for chaining @@ -182,6 +238,22 @@ public class POIMarker extends DistanceRangedMarker { return this; } + @Override + public Builder styleClasses(String... styleClasses) { + Collection styleClassesCollection = Arrays.asList(styleClasses); + if (!styleClassesCollection.stream().allMatch(STYLE_CLASS_PATTERN.asMatchPredicate())) + throw new IllegalArgumentException("One of the provided style-classes has an invalid format!"); + + this.classes.addAll(styleClassesCollection); + return this; + } + + @Override + public Builder clearStyleClasses() { + this.classes.clear(); + return this; + } + /** * Creates a new {@link POIMarker} with the current builder-settings.
* The minimum required settings to build this marker are: @@ -196,7 +268,9 @@ public class POIMarker extends DistanceRangedMarker { checkNotNull(label, "label"), checkNotNull(position, "position") ); + if (detail != null) marker.setDetail(detail); if (icon != null) marker.setIcon(icon, anchor); + else if (anchor != null) marker.setAnchor(anchor); return build(marker); }