Add the abillity to add css-classes to POI and HTML-Markers and add detail field to the POI-Marker

This commit is contained in:
Lukas Rieger (Blue) 2022-12-13 21:54:16 +01:00
parent 5b4093e404
commit abd5756609
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
7 changed files with 319 additions and 79 deletions

View File

@ -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()})!

View File

@ -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.<br>
* This is the text that will be displayed on the popup when you click on this marker.
* <p>
* <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @param detail the new detail for this {@link ObjectMarker}
*/
void setDetail(String detail);
interface Builder<B> {
/**
* Sets the detail of the {@link Marker}. The detail can include html-tags.<br>
* This is the text that will be displayed on the popup when you click on the marker.
* <p>
* <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @param detail the new detail for the {@link Marker}
* @return this builder for chaining
*/
B detail(String detail);
}
}

View File

@ -27,6 +27,13 @@
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 {

View File

@ -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<String> getStyleClasses();
/**
* Sets the CSS-classes that the element of this marker will have.<br>
* All classes must match <code>-?[_a-zA-Z]+[_a-zA-Z0-9-]*</code><br>
* 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.<br>
* All classes must match <code>-?[_a-zA-Z]+[_a-zA-Z0-9-]*</code><br>
* Any existing classes on this marker will be removed.
* @param styleClasses the style-classes this element-marker will have
*/
void setStyleClasses(Collection<String> styleClasses);
/**
* Adds the CSS-classes that the element of this marker will have.<br>
* All classes must match <code>-?[_a-zA-Z]+[_a-zA-Z0-9-]*</code><br>
* @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.<br>
* All classes must match <code>-?[_a-zA-Z]+[_a-zA-Z0-9-]*</code><br>
* @param styleClasses the style-classes this element-marker will have
*/
void addStyleClasses(Collection<String> styleClasses);
interface Builder<B> {
/**
* 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);
/**
* <b>Adds</b> the CSS-classes that the element of this marker will have.<br>
* All classes must match <code>-?[_a-zA-Z]+[_a-zA-Z0-9-]*</code><br>
* @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));
}
}
}

View File

@ -29,13 +29,16 @@
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<String> classes = new HashSet<>();
private Vector2i anchor;
private String html;
@ -82,31 +85,16 @@ public HtmlMarker(String label, Vector3d position, String html, Vector2i anchor)
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 void setHtml(String html) {
this.html = Objects.requireNonNull(html, "html must not be null");
}
@Override
public Collection<String> getStyleClasses() {
return Collections.unmodifiableCollection(this.classes);
}
@Override
public void setStyleClasses(Collection<String> 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<String> 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 static Builder builder() {
return new Builder();
}
public static class Builder extends DistanceRangedMarker.Builder<HtmlMarker, Builder> {
public static class Builder extends DistanceRangedMarker.Builder<HtmlMarker, Builder>
implements ElementMarker.Builder<Builder> {
Set<String> 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 Builder html(String html) {
return this;
}
@Override
public Builder styleClasses(String... styleClasses) {
Collection<String> 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.<br>
* The minimum required settings to build this marker are:

View File

@ -31,8 +31,13 @@
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 ObjectMarker(String type, String label, Vector3d position) {
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.<br>
* This is the text that will be displayed on the popup when you click on this marker.
* <p>
* <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @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 int hashCode() {
}
public static abstract class Builder<T extends ObjectMarker, B extends ObjectMarker.Builder<T, B>>
extends DistanceRangedMarker.Builder<T, B> {
extends DistanceRangedMarker.Builder<T, B>
implements DetailMarker.Builder<B> {
String detail;
String link;
boolean newTab;
/**
* Sets the detail of the {@link Marker}. The detail can include html-tags.<br>
* This is the text that will be displayed on the popup when you click on the marker.
* <p>
* <b>Important:</b><br>
* Html-tags in the label will not be escaped, so you can use them to style the {@link Marker}-detail.<br>
* Make sure you escape all html-tags from possible user inputs to prevent possible
* <a href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS-Attacks</a> on the web-client!
* </p>
*
* @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();

View File

@ -26,13 +26,18 @@
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<String> classes = new HashSet<>();
private String detail;
private String icon;
private Vector2i anchor;
@ -72,10 +77,21 @@ public POIMarker(String label, Vector3d position) {
*/
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 String getIconAddress() {
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 void setIcon(String iconAddress, int anchorX, int anchorY) {
/**
* 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 void setIcon(String iconAddress, Vector2i anchor) {
this.anchor = Objects.requireNonNull(anchor, "anchor must not be null");
}
@Override
public Collection<String> getStyleClasses() {
return Collections.unmodifiableCollection(this.classes);
}
@Override
public void setStyleClasses(Collection<String> 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<String> 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 static Builder builder() {
return new Builder();
}
public static class Builder extends DistanceRangedMarker.Builder<POIMarker, Builder> {
public static class Builder extends DistanceRangedMarker.Builder<POIMarker, Builder>
implements DetailMarker.Builder<Builder>, ElementMarker.Builder<Builder> {
Set<String> 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 Builder icon(String iconAddress, int anchorX, int anchorY) {
/**
* 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 Builder icon(String iconAddress, Vector2i anchor) {
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 Builder defaultIcon() {
return this;
}
@Override
public Builder styleClasses(String... styleClasses) {
Collection<String> 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.<br>
* The minimum required settings to build this marker are:
@ -196,7 +268,9 @@ public POIMarker build() {
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);
}