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

Last change on this file was 17744, checked in by simon04, 5 years ago

see #20739 - Extract Environment.getCascade

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