source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java@ 13662

Last change on this file since 13662 was 13662, checked in by Don-vip, 6 years ago

use of IPrimitive interface

  • Property svn:eol-style set to native
File size: 10.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.styleelement;
3
4import java.awt.Color;
5import java.awt.Rectangle;
6import java.awt.geom.Point2D;
7import java.util.Objects;
8
9import org.openstreetmap.josm.data.osm.IPrimitive;
10import org.openstreetmap.josm.data.osm.Node;
11import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
12import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
13import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
14import org.openstreetmap.josm.gui.mappaint.Cascade;
15import org.openstreetmap.josm.gui.mappaint.Environment;
16import org.openstreetmap.josm.gui.mappaint.Keyword;
17import org.openstreetmap.josm.gui.mappaint.MultiCascade;
18import org.openstreetmap.josm.tools.CheckParameterUtil;
19
20/**
21 * Text style attached to a style with a bounding box, like an icon or a symbol.
22 */
23public class BoxTextElement extends StyleElement {
24
25 /**
26 * MapCSS text-anchor-horizontal
27 */
28 public enum HorizontalTextAlignment {
29 /**
30 * Align to the left
31 */
32 LEFT,
33 /**
34 * Align in the center
35 */
36 CENTER,
37 /**
38 * Align to the right
39 */
40 RIGHT
41 }
42
43 /**
44 * MapCSS text-anchor-vertical
45 */
46 public enum VerticalTextAlignment {
47 /**
48 * Render above the box
49 */
50 ABOVE,
51 /**
52 * Align to the top of the box
53 */
54 TOP,
55 /**
56 * Render at the center of the box
57 */
58 CENTER,
59 /**
60 * Align to the bottom of the box
61 */
62 BOTTOM,
63 /**
64 * Render below the box
65 */
66 BELOW
67 }
68
69 /**
70 * Something that provides us with a {@link BoxProviderResult}
71 * @since 10600 (functional interface)
72 */
73 @FunctionalInterface
74 public interface BoxProvider {
75 /**
76 * Compute and get the {@link BoxProviderResult}. The temporary flag is set if the result of the computation may change in the future.
77 * @return The result of the computation.
78 */
79 BoxProviderResult get();
80 }
81
82 /**
83 * A box rectangle with a flag if it is temporary.
84 */
85 public static class BoxProviderResult {
86 private final Rectangle box;
87 private final boolean temporary;
88
89 /**
90 * Create a new box provider result
91 * @param box The box
92 * @param temporary The temporary flag, will be returned by {@link #isTemporary()}
93 */
94 public BoxProviderResult(Rectangle box, boolean temporary) {
95 this.box = box;
96 this.temporary = temporary;
97 }
98
99 /**
100 * Returns the box.
101 * @return the box
102 */
103 public Rectangle getBox() {
104 return box;
105 }
106
107 /**
108 * Determines if the box can change in future calls of the {@link BoxProvider#get()} method
109 * @return {@code true} if the box can change in future calls of the {@code BoxProvider#get()} method
110 */
111 public boolean isTemporary() {
112 return temporary;
113 }
114 }
115
116 /**
117 * A {@link BoxProvider} that always returns the same non-temporary rectangle
118 */
119 public static class SimpleBoxProvider implements BoxProvider {
120 private final Rectangle box;
121
122 /**
123 * Constructs a new {@code SimpleBoxProvider}.
124 * @param box the box
125 */
126 public SimpleBoxProvider(Rectangle box) {
127 this.box = box;
128 }
129
130 @Override
131 public BoxProviderResult get() {
132 return new BoxProviderResult(box, false);
133 }
134
135 @Override
136 public int hashCode() {
137 return Objects.hash(box);
138 }
139
140 @Override
141 public boolean equals(Object obj) {
142 if (this == obj) return true;
143 if (obj == null || getClass() != obj.getClass()) return false;
144 SimpleBoxProvider that = (SimpleBoxProvider) obj;
145 return Objects.equals(box, that.box);
146 }
147 }
148
149 /**
150 * The default style a simple node should use for it's text
151 */
152 public static final BoxTextElement SIMPLE_NODE_TEXT_ELEMSTYLE;
153 static {
154 MultiCascade mc = new MultiCascade();
155 Cascade c = mc.getOrCreateCascade("default");
156 c.put(TEXT, Keyword.AUTO);
157 Node n = new Node();
158 n.put("name", "dummy");
159 SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElement.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
160 if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
161 }
162
163 /**
164 * Caches the default text color from the preferences.
165 *
166 * FIXME: the cache isn't updated if the user changes the preference during a JOSM
167 * session. There should be preference listener updating this cache.
168 */
169 private static volatile Color defaultTextColorCache;
170
171 /**
172 * The text this element should display.
173 */
174 public TextLabel text;
175 /**
176 * The x offset of the text.
177 */
178 public int xOffset;
179 /**
180 * The y offset of the text. In screen space (inverted to user space)
181 */
182 public int yOffset;
183 /**
184 * The {@link HorizontalTextAlignment} for this text.
185 */
186 public HorizontalTextAlignment hAlign;
187 /**
188 * The {@link VerticalTextAlignment} for this text.
189 */
190 public VerticalTextAlignment vAlign;
191 protected BoxProvider boxProvider;
192
193 /**
194 * Create a new {@link BoxTextElement}
195 * @param c The current cascade
196 * @param text The text to display
197 * @param boxProvider The box provider to use
198 * @param offsetX x offset, in screen space
199 * @param offsetY y offset, in screen space
200 * @param hAlign The {@link HorizontalTextAlignment}
201 * @param vAlign The {@link VerticalTextAlignment}
202 */
203 public BoxTextElement(Cascade c, TextLabel text, BoxProvider boxProvider,
204 int offsetX, int offsetY, HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
205 super(c, 5f);
206 xOffset = offsetX;
207 yOffset = offsetY;
208 CheckParameterUtil.ensureParameterNotNull(text);
209 CheckParameterUtil.ensureParameterNotNull(hAlign);
210 CheckParameterUtil.ensureParameterNotNull(vAlign);
211 this.text = text;
212 this.boxProvider = boxProvider;
213 this.hAlign = hAlign;
214 this.vAlign = vAlign;
215 }
216
217 /**
218 * Create a new {@link BoxTextElement} with a boxprovider and a box.
219 * @param env The MapCSS environment
220 * @param boxProvider The box provider.
221 * @return A new {@link BoxTextElement} or <code>null</code> if the creation failed.
222 */
223 public static BoxTextElement create(Environment env, BoxProvider boxProvider) {
224 initDefaultParameters();
225
226 TextLabel text = TextLabel.create(env, defaultTextColorCache, false);
227 if (text == null) return null;
228 // Skip any primitives that don't have text to draw. (Styles are recreated for any tag change.)
229 // The concrete text to render is not cached in this object, but computed for each
230 // repaint. This way, one BoxTextElement object can be used by multiple primitives (to save memory).
231 if (text.labelCompositionStrategy.compose(env.osm) == null) return null;
232
233 Cascade c = env.mc.getCascade(env.layer);
234
235 HorizontalTextAlignment hAlign;
236 switch (c.get(TEXT_ANCHOR_HORIZONTAL, Keyword.RIGHT, Keyword.class).val) {
237 case "left":
238 hAlign = HorizontalTextAlignment.LEFT;
239 break;
240 case "center":
241 hAlign = HorizontalTextAlignment.CENTER;
242 break;
243 case "right":
244 default:
245 hAlign = HorizontalTextAlignment.RIGHT;
246 }
247 VerticalTextAlignment vAlign;
248 switch (c.get(TEXT_ANCHOR_VERTICAL, Keyword.BOTTOM, Keyword.class).val) {
249 case "above":
250 vAlign = VerticalTextAlignment.ABOVE;
251 break;
252 case "top":
253 vAlign = VerticalTextAlignment.TOP;
254 break;
255 case "center":
256 vAlign = VerticalTextAlignment.CENTER;
257 break;
258 case "below":
259 vAlign = VerticalTextAlignment.BELOW;
260 break;
261 case "bottom":
262 default:
263 vAlign = VerticalTextAlignment.BOTTOM;
264 }
265 Point2D offset = TextLabel.getTextOffset(c);
266
267 return new BoxTextElement(c, text, boxProvider, (int) offset.getX(), (int) -offset.getY(), hAlign, vAlign);
268 }
269
270 /**
271 * Get the box in which the content should be drawn.
272 * @return The box.
273 */
274 public Rectangle getBox() {
275 return boxProvider.get().getBox();
276 }
277
278 private static void initDefaultParameters() {
279 if (defaultTextColorCache != null) return;
280 defaultTextColorCache = PaintColors.TEXT.get();
281 }
282
283 @Override
284 public void paintPrimitive(IPrimitive osm, MapPaintSettings settings, StyledMapRenderer painter,
285 boolean selected, boolean outermember, boolean member) {
286 if (osm instanceof Node) {
287 painter.drawBoxText((Node) osm, this);
288 }
289 }
290
291 @Override
292 public boolean equals(Object obj) {
293 if (this == obj) return true;
294 if (obj == null || getClass() != obj.getClass()) return false;
295 if (!super.equals(obj)) return false;
296 BoxTextElement that = (BoxTextElement) obj;
297 return hAlign == that.hAlign &&
298 vAlign == that.vAlign &&
299 xOffset == that.xOffset &&
300 yOffset == that.yOffset &&
301 Objects.equals(text, that.text) &&
302 Objects.equals(boxProvider, that.boxProvider);
303 }
304
305 @Override
306 public int hashCode() {
307 return Objects.hash(super.hashCode(), text, boxProvider, hAlign, vAlign, xOffset, yOffset);
308 }
309
310 @Override
311 public String toString() {
312 return "BoxTextElement{" + super.toString() + ' ' + text.toStringImpl()
313 + " box=" + getBox() + " hAlign=" + hAlign + " vAlign=" + vAlign + " xOffset=" + xOffset + " yOffset=" + yOffset + '}';
314 }
315}
Note: See TracBrowser for help on using the repository browser.