Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2021 James Westman <james@jwestman.net>
3 : : *
4 : : * This library is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * This library is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : : * Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with this library; if not, see <https://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : :
19 : : #include "shumate-vector-symbol-private.h"
20 : : #include "shumate-vector-utils-private.h"
21 : : #include "shumate-vector-symbol-info-private.h"
22 : : #include "shumate-vector-symbol-container-private.h"
23 : : #include "shumate-layer.h"
24 : : #include "shumate-symbol-event-private.h"
25 : : #include "../shumate-vector-sprite.h"
26 : :
27 : : #define RGBA_BLACK ((GdkRGBA){0, 0, 0, 1})
28 : :
29 : : struct _ShumateVectorSymbol
30 : : {
31 : : GtkWidget parent_instance;
32 : :
33 : : ShumateVectorSymbolInfo *symbol_info;
34 : :
35 : : GArray *glyphs;
36 : :
37 : : GskRenderNode *glyphs_node;
38 : : int layout_width, layout_height, baseline, layout_y;
39 : :
40 : : graphene_rect_t bounds;
41 : : double x, y;
42 : :
43 : : ShumateVectorPoint midpoint;
44 : : double midpoint_angle;
45 : : double line_length;
46 : :
47 : : uint8_t show_text : 1;
48 : : uint8_t show_icon : 1;
49 : : };
50 : :
51 [ # # # # ]: 0 : G_DEFINE_TYPE (ShumateVectorSymbol, shumate_vector_symbol, GTK_TYPE_WIDGET)
52 : :
53 : :
54 : : enum {
55 : : PROP_0,
56 : : PROP_SYMBOL_INFO,
57 : : N_PROPS,
58 : : };
59 : :
60 : : static GParamSpec *obj_properties[N_PROPS] = { NULL, };
61 : :
62 : : enum {
63 : : CLICKED,
64 : : LAST_SIGNAL
65 : : };
66 : :
67 : : static guint signals[LAST_SIGNAL] = { 0, };
68 : :
69 : :
70 : : typedef struct {
71 : : GskRenderNode *node;
72 : : ShumateVectorSprite *sprite;
73 : : double width;
74 : : GdkRGBA icon_color;
75 : : } Glyph;
76 : :
77 : : static void
78 : 0 : glyph_clear (Glyph *glyph)
79 : : {
80 [ # # ]: 0 : g_clear_pointer (&glyph->node, gsk_render_node_unref);
81 : 0 : }
82 : :
83 : :
84 : : ShumateVectorSymbol *
85 : 0 : shumate_vector_symbol_new (ShumateVectorSymbolInfo *symbol_info)
86 : : {
87 : 0 : return g_object_new (SHUMATE_TYPE_VECTOR_SYMBOL,
88 : : "symbol-info", symbol_info,
89 : : NULL);
90 : : }
91 : :
92 : :
93 : : static PangoAttrShape *
94 : 0 : get_shape_from_glyph_item (PangoGlyphItem *item)
95 : : {
96 [ # # ]: 0 : for (GSList *list = item->item->analysis.extra_attrs; list != NULL; list = list->next)
97 [ # # ]: 0 : if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_SHAPE)
98 : 0 : return (PangoAttrShape*)list->data;
99 : : return NULL;
100 : : }
101 : :
102 : : static void
103 : 0 : get_color_from_glyph_item (PangoGlyphItem *item, GdkRGBA *color)
104 : : {
105 : 0 : *color = RGBA_BLACK;
106 : :
107 [ # # ]: 0 : for (GSList *list = item->item->analysis.extra_attrs; list != NULL; list = list->next)
108 : : {
109 [ # # ]: 0 : if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_FOREGROUND)
110 : : {
111 : 0 : PangoColor c = ((PangoAttrColor*)list->data)->color;
112 : 0 : color->red = c.red / 65535.0;
113 : 0 : color->green = c.green / 65535.0;
114 : 0 : color->blue = c.blue / 65535.0;
115 : : }
116 [ # # ]: 0 : else if (((PangoAttribute*)list->data)->klass->type == PANGO_ATTR_FOREGROUND_ALPHA)
117 : 0 : color->alpha = ((PangoAttrInt*)list->data)->value / 65535.0;
118 : : }
119 : 0 : }
120 : :
121 : :
122 : : static void
123 : 0 : shumate_vector_symbol_constructed (GObject *object)
124 : : {
125 : 0 : ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
126 : 0 : g_autofree char *text = NULL;
127 : :
128 [ # # ]: 0 : if (self->symbol_info->details->formatted_text != NULL)
129 : : {
130 : 0 : PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (self));
131 : 0 : g_autoptr(PangoLayout) layout = pango_layout_new (context);
132 [ # # ]: 0 : g_autoptr(PangoAttrList) attrs = pango_attr_list_new ();
133 [ # # ]: 0 : g_autoptr(GString) string = g_string_new ("");
134 : 0 : PangoAttribute *attr;
135 : 0 : gboolean any_images = FALSE;
136 : 0 : PangoRectangle ink_rect;
137 : :
138 [ # # ]: 0 : if (self->symbol_info->details->text_font != NULL)
139 : : {
140 : 0 : g_autoptr(PangoFontDescription) desc = pango_font_description_from_string (self->symbol_info->details->text_font);
141 : 0 : attr = pango_attr_font_desc_new (desc);
142 [ # # ]: 0 : pango_attr_list_insert (attrs, attr);
143 : : }
144 : :
145 : 0 : attr = pango_attr_letter_spacing_new (self->symbol_info->details->text_letter_spacing * self->symbol_info->details->text_size * PANGO_SCALE);
146 : 0 : pango_attr_list_insert (attrs, attr);
147 : :
148 : 0 : attr = pango_attr_foreground_new (self->symbol_info->details->text_color.red * 65535,
149 : 0 : self->symbol_info->details->text_color.green * 65535,
150 : 0 : self->symbol_info->details->text_color.blue * 65535);
151 : 0 : pango_attr_list_insert (attrs, attr);
152 : :
153 : 0 : attr = pango_attr_foreground_alpha_new (self->symbol_info->details->text_color.alpha * 65535);
154 : 0 : pango_attr_list_insert (attrs, attr);
155 : :
156 : 0 : attr = pango_attr_size_new_absolute (self->symbol_info->details->text_size * PANGO_SCALE);
157 : 0 : pango_attr_list_insert (attrs, attr);
158 : :
159 : 0 : pango_layout_set_attributes (layout, attrs);
160 : :
161 [ # # ]: 0 : for (int i = 0, n = self->symbol_info->details->formatted_text->len; i < n; i ++)
162 : : {
163 : 0 : ShumateVectorFormatPart *part = g_ptr_array_index (self->symbol_info->details->formatted_text, i);
164 : :
165 [ # # ]: 0 : if (part->sprite != NULL)
166 : : {
167 : 0 : int width = shumate_vector_sprite_get_width (part->sprite);
168 : 0 : int height = shumate_vector_sprite_get_height (part->sprite);
169 : : /* For shapes, since we're overriding the ink and logical rects
170 : : of the glyph, we have to take letter spacing into account ourselves */
171 : 0 : double spacing = self->symbol_info->details->text_letter_spacing * self->symbol_info->details->text_size;
172 : :
173 : 0 : PangoRectangle ink_rect = {
174 : 0 : .x = spacing / 2 * PANGO_SCALE,
175 : 0 : .y = -height * PANGO_SCALE,
176 : 0 : .width = width * PANGO_SCALE,
177 : : .height = height * PANGO_SCALE,
178 : : };
179 : 0 : PangoRectangle logical_rect = {
180 : : .x = 0,
181 : : .y = -height * PANGO_SCALE,
182 : 0 : .width = (width + spacing) * PANGO_SCALE,
183 : : .height = height * PANGO_SCALE,
184 : : };
185 : :
186 : 0 : attr = pango_attr_shape_new_with_data (&ink_rect, &logical_rect, g_object_ref (part->sprite), (PangoAttrDataCopyFunc)g_object_ref, g_object_unref);
187 : 0 : attr->start_index = string->len;
188 : 0 : attr->end_index = string->len + strlen ("\uFFFC");
189 : 0 : pango_attr_list_insert (attrs, attr);
190 : :
191 [ # # ]: 0 : if (spacing != 0)
192 : : {
193 : 0 : attr = pango_attr_letter_spacing_new (0);
194 : 0 : attr->start_index = string->len;
195 : 0 : attr->end_index = string->len + strlen ("\uFFFC");
196 : 0 : pango_attr_list_insert (attrs, attr);
197 : : }
198 : :
199 [ # # ]: 0 : g_string_append (string, "\uFFFC");
200 : :
201 : 0 : any_images = TRUE;
202 : : }
203 : : else
204 : : {
205 [ # # ]: 0 : if (part->has_font_scale)
206 : : {
207 : 0 : attr = pango_attr_size_new_absolute (part->font_scale * self->symbol_info->details->text_size * PANGO_SCALE);
208 : 0 : attr->start_index = string->len;
209 : 0 : attr->end_index = string->len + strlen (part->string);
210 : 0 : pango_attr_list_insert (attrs, attr);
211 : : }
212 : :
213 [ # # ]: 0 : if (part->has_text_color)
214 : : {
215 : 0 : attr = pango_attr_foreground_new (part->text_color.red * 65535,
216 : 0 : part->text_color.green * 65535,
217 : 0 : part->text_color.blue * 65535);
218 : 0 : attr->start_index = string->len;
219 : 0 : attr->end_index = string->len + strlen (part->string);
220 : 0 : pango_attr_list_insert (attrs, attr);
221 : :
222 : 0 : attr = pango_attr_foreground_alpha_new (part->text_color.alpha * 65535);
223 : 0 : attr->start_index = string->len;
224 : 0 : attr->end_index = string->len + strlen (part->string);
225 : 0 : pango_attr_list_insert (attrs, attr);
226 : : }
227 : :
228 [ # # ]: 0 : g_string_append (string, part->string);
229 : : }
230 : : }
231 : :
232 : 0 : text = g_string_free (g_steal_pointer (&string), FALSE);
233 : 0 : pango_layout_set_text (layout, text, -1);
234 : :
235 : 0 : pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
236 : 0 : self->layout_width = ink_rect.width;
237 : 0 : self->layout_height = ink_rect.height;
238 : 0 : self->layout_y = ink_rect.y;
239 : 0 : self->baseline = pango_layout_get_baseline (layout) / (double) PANGO_SCALE;
240 : :
241 : 0 : if ((self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP
242 [ # # ]: 0 : || self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_VIEWPORT_GLYPH)
243 : 0 : && (self->symbol_info->details->symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE
244 [ # # ]: 0 : || self->symbol_info->details->symbol_placement == SHUMATE_VECTOR_PLACEMENT_LINE_CENTER))
245 : : {
246 : 0 : g_autoptr(PangoLayoutIter) iter = NULL;
247 : 0 : int i;
248 : 0 : PangoGlyphItem *current_item;
249 : :
250 : 0 : self->glyphs = g_array_new (FALSE, FALSE, sizeof (Glyph));
251 : 0 : g_array_set_clear_func (self->glyphs, (GDestroyNotify)glyph_clear);
252 : :
253 : 0 : iter = pango_layout_get_iter (layout);
254 : :
255 : 0 : do {
256 : 0 : PangoAttrShape *shape = NULL;
257 : :
258 : 0 : current_item = pango_layout_iter_get_run (iter);
259 : :
260 [ # # ]: 0 : if (current_item == NULL)
261 : 0 : continue;
262 : :
263 : 0 : shape = get_shape_from_glyph_item (current_item);
264 : :
265 [ # # ]: 0 : if (shape != NULL)
266 : : {
267 : 0 : Glyph glyph = {
268 : 0 : .sprite = g_object_ref (shape->data),
269 : : .node = NULL,
270 : 0 : .width = shape->logical_rect.width / (double) PANGO_SCALE,
271 : : };
272 : 0 : get_color_from_glyph_item (current_item, &glyph.icon_color);
273 : 0 : g_array_append_vals (self->glyphs, &glyph, 1);
274 : : }
275 : : else
276 : : {
277 [ # # ]: 0 : for (i = 0; i < current_item->glyphs->num_glyphs; i ++)
278 : : {
279 : 0 : GskRenderNode *node;
280 : 0 : Glyph glyph;
281 : 0 : PangoGlyphString *glyph_string;
282 : 0 : GdkRGBA color;
283 : :
284 : 0 : glyph_string = pango_glyph_string_new ();
285 : 0 : pango_glyph_string_set_size (glyph_string, 1);
286 : 0 : glyph_string->glyphs[0] = current_item->glyphs->glyphs[i];
287 : 0 : glyph_string->log_clusters[0] = 0;
288 : :
289 : 0 : get_color_from_glyph_item (current_item, &color);
290 : :
291 : 0 : node =
292 : 0 : gsk_text_node_new (current_item->item->analysis.font,
293 : : glyph_string,
294 : : &color,
295 : 0 : &GRAPHENE_POINT_INIT (0, 0));
296 : :
297 : 0 : glyph.node = node;
298 : 0 : glyph.sprite = NULL;
299 : 0 : glyph.width = glyph_string->glyphs[0].geometry.width / (double) PANGO_SCALE;
300 : 0 : g_array_append_vals (self->glyphs, &glyph, 1);
301 : :
302 : 0 : pango_glyph_string_free (glyph_string);
303 : : }
304 : : }
305 [ # # ]: 0 : } while (pango_layout_iter_next_run (iter));
306 : : }
307 : : else
308 : : {
309 : 0 : g_autoptr(GtkSnapshot) snapshot = gtk_snapshot_new ();
310 : :
311 : 0 : gtk_snapshot_append_layout (snapshot, layout, &RGBA_BLACK);
312 : :
313 [ # # ]: 0 : if (any_images)
314 : : {
315 : 0 : g_autoptr(PangoLayoutIter) iter = NULL;
316 : :
317 : 0 : iter = pango_layout_get_iter (layout);
318 : :
319 : 0 : do {
320 : 0 : PangoGlyphItem *current_item = pango_layout_iter_get_run (iter);
321 : 0 : PangoAttrShape *shape;
322 : :
323 [ # # ]: 0 : if (current_item == NULL)
324 : 0 : continue;
325 : :
326 : 0 : shape = get_shape_from_glyph_item (current_item);
327 [ # # ]: 0 : if (shape != NULL)
328 : : {
329 : 0 : ShumateVectorSprite *sprite = shape->data;
330 : 0 : PangoRectangle extents;
331 : 0 : GdkRGBA color;
332 : :
333 : 0 : pango_layout_iter_get_run_extents (iter, &extents, NULL);
334 : 0 : get_color_from_glyph_item (current_item, &color);
335 : :
336 : 0 : gtk_snapshot_save (snapshot);
337 : 0 : gtk_snapshot_translate (snapshot,
338 : 0 : &GRAPHENE_POINT_INIT (
339 : : PANGO_PIXELS (extents.x),
340 : : PANGO_PIXELS (extents.y)
341 : : ));
342 : 0 : gtk_symbolic_paintable_snapshot_symbolic (
343 : : GTK_SYMBOLIC_PAINTABLE (sprite),
344 : : snapshot,
345 : 0 : PANGO_PIXELS (extents.width),
346 : 0 : PANGO_PIXELS (extents.height),
347 : : &color,
348 : : 1
349 : : );
350 : 0 : gtk_snapshot_restore (snapshot);
351 : : }
352 [ # # ]: 0 : } while (pango_layout_iter_next_run (iter));
353 : : }
354 : :
355 : 0 : self->glyphs_node = gtk_snapshot_free_to_node (g_steal_pointer (&snapshot));
356 : : }
357 : : }
358 : :
359 [ # # ]: 0 : if (self->symbol_info->details->cursor != NULL)
360 : 0 : gtk_widget_set_cursor_from_name (GTK_WIDGET (self), self->symbol_info->details->cursor);
361 : :
362 [ # # ]: 0 : if (self->symbol_info->line != NULL)
363 : : {
364 : 0 : ShumateVectorPointIter iter;
365 : :
366 : 0 : shumate_vector_point_iter_init (&iter, self->symbol_info->line);
367 : 0 : shumate_vector_point_iter_advance (&iter, self->symbol_info->line_position);
368 : :
369 : 0 : shumate_vector_point_iter_get_current_point (&iter, &self->midpoint);
370 : 0 : self->midpoint.x -= self->symbol_info->x;
371 : 0 : self->midpoint.y -= self->symbol_info->y;
372 : :
373 : 0 : self->midpoint_angle = shumate_vector_point_iter_get_current_angle (&iter);
374 : :
375 : 0 : self->line_length = shumate_vector_line_string_length (self->symbol_info->line);
376 : : }
377 : :
378 : 0 : gtk_accessible_update_property (GTK_ACCESSIBLE (self),
379 : : GTK_ACCESSIBLE_PROPERTY_LABEL,
380 : : text,
381 : : -1);
382 : :
383 : 0 : G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->constructed (object);
384 : 0 : }
385 : :
386 : :
387 : : static gboolean
388 : 0 : shumate_vector_symbol_contains (GtkWidget *widget,
389 : : double x,
390 : : double y)
391 : : {
392 : 0 : GtkWidget *parent;
393 : 0 : graphene_rect_t alloc;
394 : :
395 [ # # # # ]: 0 : if (x < 0 || y < 0)
396 : : return FALSE;
397 : :
398 : 0 : parent = gtk_widget_get_parent (widget);
399 [ # # ]: 0 : if (!gtk_widget_compute_bounds (widget, parent, &alloc))
400 : : return FALSE;
401 : :
402 [ # # # # ]: 0 : if (x > alloc.size.width || y > alloc.size.height)
403 : : return FALSE;
404 : :
405 [ # # ]: 0 : if (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (parent))
406 : : {
407 : 0 : ShumateVectorCollision *collision =
408 : 0 : shumate_vector_symbol_container_get_collision ((ShumateVectorSymbolContainer *) (parent));
409 : :
410 : 0 : return shumate_vector_collision_query_point (collision,
411 : 0 : alloc.origin.x + x + collision->delta_x,
412 : 0 : alloc.origin.y + y + collision->delta_y,
413 : : widget);
414 : : }
415 : :
416 : : return TRUE;
417 : : }
418 : :
419 : :
420 : : static void
421 : 0 : shumate_vector_symbol_dispose (GObject *object)
422 : : {
423 : 0 : ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
424 : 0 : GtkWidget *child;
425 : :
426 [ # # ]: 0 : while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
427 : 0 : gtk_widget_unparent (child);
428 : :
429 [ # # ]: 0 : g_clear_pointer (&self->symbol_info, shumate_vector_symbol_info_unref);
430 [ # # ]: 0 : g_clear_pointer (&self->glyphs, g_array_unref);
431 [ # # ]: 0 : g_clear_pointer (&self->glyphs_node, gsk_render_node_unref);
432 : :
433 : 0 : G_OBJECT_CLASS (shumate_vector_symbol_parent_class)->dispose (object);
434 : 0 : }
435 : :
436 : :
437 : : static void
438 : 0 : shumate_vector_symbol_get_property (GObject *object,
439 : : guint property_id,
440 : : GValue *value,
441 : : GParamSpec *pspec)
442 : : {
443 : 0 : ShumateVectorSymbol *self = (ShumateVectorSymbol *)object;
444 : :
445 [ # # ]: 0 : switch (property_id)
446 : : {
447 : 0 : case PROP_SYMBOL_INFO:
448 : 0 : g_value_set_boxed (value, self->symbol_info);
449 : 0 : break;
450 : :
451 : 0 : default:
452 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
453 : : }
454 : 0 : }
455 : :
456 : :
457 : : static void
458 : 0 : shumate_vector_symbol_set_property (GObject *object,
459 : : guint property_id,
460 : : const GValue *value,
461 : : GParamSpec *pspec)
462 : : {
463 : 0 : ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (object);
464 : :
465 [ # # ]: 0 : switch (property_id)
466 : : {
467 : 0 : case PROP_SYMBOL_INFO:
468 [ # # ]: 0 : g_assert (self->symbol_info == NULL);
469 : 0 : self->symbol_info = g_value_dup_boxed (value);
470 : 0 : break;
471 : :
472 : 0 : default:
473 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
474 : : }
475 : 0 : }
476 : :
477 : :
478 : : static double
479 : 0 : positive_mod (double i, double n)
480 : : {
481 : 0 : return fmod (fmod (i, n) + n, n);
482 : : }
483 : :
484 : :
485 : : static void
486 : 0 : shumate_vector_symbol_measure (GtkWidget *widget,
487 : : GtkOrientation orientation,
488 : : int for_size,
489 : : int *minimum,
490 : : int *natural,
491 : : int *minimum_baseline,
492 : : int *natural_baseline)
493 : : {
494 : 0 : ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (widget);
495 : :
496 [ # # ]: 0 : if (self->symbol_info->line != NULL)
497 : : {
498 [ # # ]: 0 : if (minimum)
499 : 0 : *minimum = 0;
500 [ # # ]: 0 : if (natural)
501 : 0 : *natural = 0;
502 : : }
503 : : else
504 : : {
505 [ # # ]: 0 : if (orientation == GTK_ORIENTATION_HORIZONTAL)
506 : : {
507 [ # # ]: 0 : if (minimum)
508 : 0 : *minimum = self->layout_width;
509 [ # # ]: 0 : if (natural)
510 : 0 : *natural = self->layout_width;
511 : : }
512 [ # # ]: 0 : else if (orientation == GTK_ORIENTATION_VERTICAL)
513 : : {
514 [ # # ]: 0 : if (minimum)
515 : 0 : *minimum = self->layout_height;
516 [ # # ]: 0 : if (natural)
517 : 0 : *natural = self->layout_height;
518 : : }
519 : : }
520 : 0 : }
521 : :
522 : : static GtkSizeRequestMode
523 : 0 : shumate_vector_symbol_get_request_mode (GtkWidget *widget)
524 : : {
525 : 0 : return GTK_SIZE_REQUEST_CONSTANT_SIZE;
526 : : }
527 : :
528 : :
529 : : static void
530 : 0 : add_anchor_offset (ShumateVectorAnchor anchor,
531 : : double *offset_x,
532 : : double *offset_y,
533 : : double width,
534 : : double height)
535 : : {
536 : 0 : if (anchor == SHUMATE_VECTOR_ANCHOR_LEFT
537 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_TOP_LEFT
538 : : || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_LEFT)
539 : 0 : *offset_x += width / 2.0;
540 : :
541 : 0 : if (anchor == SHUMATE_VECTOR_ANCHOR_RIGHT
542 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_TOP_RIGHT
543 : : || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_RIGHT)
544 : 0 : *offset_x -= width / 2.0;
545 : :
546 : 0 : if (anchor == SHUMATE_VECTOR_ANCHOR_TOP
547 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_TOP_LEFT
548 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_TOP_RIGHT)
549 : 0 : *offset_y += height / 2.0;
550 : :
551 : 0 : if (anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM
552 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_LEFT
553 [ # # ]: 0 : || anchor == SHUMATE_VECTOR_ANCHOR_BOTTOM_RIGHT)
554 : 0 : *offset_y -= height / 2.0;
555 : 0 : }
556 : :
557 : :
558 : : static void
559 : 0 : shumate_vector_symbol_snapshot (GtkWidget *widget,
560 : : GtkSnapshot *snapshot)
561 : : {
562 : 0 : ShumateVectorSymbol *self = SHUMATE_VECTOR_SYMBOL (widget);
563 : 0 : double rotation = 0.0;
564 : 0 : double tile_size_for_zoom = 512.0;
565 : 0 : GtkWidget *parent = gtk_widget_get_parent (widget);
566 : 0 : ShumateVectorPointIter iter;
567 : :
568 [ # # ]: 0 : if (SHUMATE_IS_VECTOR_SYMBOL_CONTAINER (parent))
569 : : {
570 : 0 : ShumateViewport *viewport = shumate_layer_get_viewport (SHUMATE_LAYER (parent));
571 : 0 : ShumateMapSource *map_source = shumate_vector_symbol_container_get_map_source (SHUMATE_VECTOR_SYMBOL_CONTAINER (parent));
572 : 0 : double zoom_level = shumate_viewport_get_zoom_level (viewport);
573 : 0 : tile_size_for_zoom = shumate_map_source_get_tile_size (map_source) * pow (2, zoom_level - self->symbol_info->details->tile_zoom_level);
574 : 0 : rotation = shumate_viewport_get_rotation (viewport);
575 : : }
576 : :
577 : 0 : gtk_snapshot_save (snapshot);
578 : :
579 : : /* Translate so the origin is at the origin point of the symbol */
580 : 0 : gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (self->x - self->bounds.origin.x,
581 : : self->y - self->bounds.origin.y));
582 : :
583 [ # # # # : 0 : if (self->show_icon && self->symbol_info->details->icon_image && self->symbol_info->details->icon_opacity > 0.0)
# # ]
584 : : {
585 : 0 : double angle = 0;
586 : 0 : double icon_width = shumate_vector_sprite_get_width (self->symbol_info->details->icon_image)
587 : 0 : * self->symbol_info->details->icon_size;
588 : 0 : double icon_height = shumate_vector_sprite_get_height (self->symbol_info->details->icon_image)
589 : 0 : * self->symbol_info->details->icon_size;
590 : :
591 : 0 : double offset_x = self->symbol_info->details->icon_offset_x * self->symbol_info->details->icon_size;
592 : 0 : double offset_y = self->symbol_info->details->icon_offset_y * self->symbol_info->details->icon_size;
593 : :
594 : 0 : add_anchor_offset (self->symbol_info->details->icon_anchor,
595 : : &offset_x, &offset_y,
596 : : icon_width, icon_height);
597 : :
598 [ # # ]: 0 : if (self->symbol_info->details->icon_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
599 : 0 : angle = self->midpoint_angle;
600 : : else
601 : 0 : angle = -rotation;
602 : :
603 : 0 : angle += self->symbol_info->details->icon_rotate;
604 : :
605 : 0 : gtk_snapshot_save (snapshot);
606 : :
607 : 0 : gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
608 : 0 : gtk_snapshot_translate (snapshot,
609 : 0 : &GRAPHENE_POINT_INIT (
610 : : self->midpoint.x * tile_size_for_zoom,
611 : : self->midpoint.y * tile_size_for_zoom
612 : : ));
613 : 0 : gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
614 : 0 : gtk_snapshot_translate (snapshot,
615 : 0 : &GRAPHENE_POINT_INIT (
616 : : -icon_width / 2.0 + offset_x,
617 : : -icon_height / 2.0 + offset_y
618 : : ));
619 : :
620 [ # # ]: 0 : if (self->symbol_info->details->icon_opacity < 1.0)
621 : 0 : gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->icon_opacity);
622 : :
623 : 0 : gtk_symbolic_paintable_snapshot_symbolic (
624 : 0 : GTK_SYMBOLIC_PAINTABLE (self->symbol_info->details->icon_image),
625 : : snapshot,
626 : : icon_width,
627 : : icon_height,
628 : 0 : &self->symbol_info->details->icon_color,
629 : : 1
630 : : );
631 : :
632 [ # # ]: 0 : if (self->symbol_info->details->icon_opacity < 1.0)
633 : 0 : gtk_snapshot_pop (snapshot);
634 : :
635 : 0 : gtk_snapshot_restore (snapshot);
636 : : }
637 : :
638 [ # # # # : 0 : if (self->show_text && self->glyphs && self->symbol_info->details->text_opacity > 0.0)
# # ]
639 : 0 : {
640 : 0 : double length = self->layout_width / tile_size_for_zoom;
641 [ # # ]: 0 : double start_pos = MAX (0, self->symbol_info->line_position - (length / 2.0));
642 : 0 : double avg_angle;
643 : :
644 : 0 : shumate_vector_point_iter_init (&iter, self->symbol_info->line);
645 : 0 : shumate_vector_point_iter_advance (&iter, start_pos);
646 : :
647 : : /* If the label is upside down on average, draw it the other way around */
648 [ # # ]: 0 : if (self->symbol_info->details->text_keep_upright)
649 : : {
650 : 0 : avg_angle = shumate_vector_point_iter_get_average_angle (&iter, self->layout_width / tile_size_for_zoom);
651 : 0 : avg_angle = positive_mod (avg_angle + rotation, G_PI * 2.0);
652 [ # # # # ]: 0 : if (avg_angle > G_PI / 2.0 && avg_angle < 3.0 * G_PI / 2.0)
653 : : {
654 : 0 : iter.reversed = TRUE;
655 : 0 : iter.current_point = iter.num_points - 1;
656 : 0 : iter.distance = 0;
657 : 0 : shumate_vector_point_iter_advance (&iter, self->line_length - start_pos - (self->layout_width / tile_size_for_zoom));
658 : : }
659 : : }
660 : :
661 : 0 : gtk_snapshot_save (snapshot);
662 : 0 : gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
663 : :
664 [ # # ]: 0 : if (self->symbol_info->details->text_opacity < 1.0)
665 : 0 : gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->text_opacity);
666 : :
667 [ # # ]: 0 : for (int i = 0; i < self->glyphs->len; i ++)
668 : : {
669 : 0 : Glyph *glyph = &((Glyph *)self->glyphs->data)[i];
670 : 0 : ShumateVectorPoint point;
671 : :
672 : : /* Whitespace has no glyph, but still has a width that needs to be
673 : : * advanced in the point iter */
674 [ # # # # ]: 0 : if (glyph->node != NULL || glyph->sprite != NULL)
675 : 0 : {
676 : 0 : double angle;
677 : :
678 [ # # ]: 0 : if (self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_VIEWPORT_GLYPH)
679 : 0 : angle = -rotation;
680 : : else
681 : 0 : angle = shumate_vector_point_iter_get_average_angle (&iter, glyph->width / tile_size_for_zoom);
682 : :
683 : 0 : shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom / 2.0);
684 : 0 : shumate_vector_point_iter_get_current_point (&iter, &point);
685 : 0 : shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom / 2.0);
686 : :
687 : 0 : gtk_snapshot_save (snapshot);
688 : 0 : gtk_snapshot_translate (snapshot,
689 : 0 : &GRAPHENE_POINT_INIT (
690 : : (point.x - self->symbol_info->x) * tile_size_for_zoom,
691 : : (point.y - self->symbol_info->y) * tile_size_for_zoom
692 : : ));
693 : 0 : gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
694 : 0 : gtk_snapshot_translate (snapshot,
695 : 0 : &GRAPHENE_POINT_INIT (-glyph->width / 2.0,
696 : : self->baseline - self->layout_y - self->layout_height / 2.0));
697 : :
698 [ # # ]: 0 : if (glyph->node != NULL)
699 : 0 : gtk_snapshot_append_node (snapshot, glyph->node);
700 : : else
701 : : {
702 : 0 : int width = shumate_vector_sprite_get_width (glyph->sprite);
703 : 0 : int height = shumate_vector_sprite_get_height (glyph->sprite);
704 : 0 : gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, -height));
705 : 0 : gtk_symbolic_paintable_snapshot_symbolic (
706 : 0 : GTK_SYMBOLIC_PAINTABLE (glyph->sprite),
707 : : snapshot,
708 : : width,
709 : : height,
710 : 0 : &glyph->icon_color,
711 : : 1
712 : : );
713 : : }
714 : :
715 : 0 : gtk_snapshot_restore (snapshot);
716 : : }
717 : : else
718 : 0 : shumate_vector_point_iter_advance (&iter, glyph->width / tile_size_for_zoom);
719 : : }
720 : :
721 [ # # ]: 0 : if (self->symbol_info->details->text_opacity < 1.0)
722 : 0 : gtk_snapshot_pop (snapshot);
723 : :
724 : 0 : gtk_snapshot_restore (snapshot);
725 : : }
726 [ # # # # : 0 : else if (self->show_text && self->glyphs_node && self->symbol_info->details->text_opacity > 0.0)
# # ]
727 : : {
728 : 0 : double angle = 0;
729 : 0 : double offset_x = self->symbol_info->details->text_offset_x * self->symbol_info->details->text_size;
730 : 0 : double offset_y = self->symbol_info->details->text_offset_y * self->symbol_info->details->text_size;
731 : :
732 : 0 : add_anchor_offset (self->symbol_info->details->text_anchor,
733 : : &offset_x, &offset_y,
734 : 0 : self->layout_width, self->layout_height);
735 : :
736 [ # # ]: 0 : if (self->symbol_info->details->text_rotation_alignment != SHUMATE_VECTOR_ALIGNMENT_MAP)
737 : 0 : angle = -rotation;
738 : :
739 : 0 : gtk_snapshot_save (snapshot);
740 : :
741 [ # # ]: 0 : if (self->symbol_info->details->text_opacity < 1)
742 : 0 : gtk_snapshot_push_opacity (snapshot, self->symbol_info->details->text_opacity);
743 : :
744 : 0 : gtk_snapshot_rotate (snapshot, rotation * 180 / G_PI);
745 : 0 : gtk_snapshot_translate (snapshot,
746 : 0 : &GRAPHENE_POINT_INIT (
747 : : self->midpoint.x * tile_size_for_zoom,
748 : : self->midpoint.y * tile_size_for_zoom
749 : : ));
750 : 0 : gtk_snapshot_rotate (snapshot, angle * 180 / G_PI);
751 : :
752 : 0 : gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (
753 : : -self->layout_width / 2.0 + offset_x,
754 : : -self->layout_y - self->layout_height / 2.0 + offset_y
755 : : ));
756 : 0 : gtk_snapshot_append_node (snapshot, self->glyphs_node);
757 : :
758 [ # # ]: 0 : if (self->symbol_info->details->text_opacity < 1)
759 : 0 : gtk_snapshot_pop (snapshot);
760 : :
761 : 0 : gtk_snapshot_restore (snapshot);
762 : : }
763 : :
764 : 0 : gtk_snapshot_restore (snapshot);
765 : 0 : }
766 : :
767 : :
768 : : static void
769 : 0 : shumate_vector_symbol_class_init (ShumateVectorSymbolClass *klass)
770 : : {
771 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
772 : 0 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
773 : :
774 : 0 : object_class->constructed = shumate_vector_symbol_constructed;
775 : 0 : object_class->dispose = shumate_vector_symbol_dispose;
776 : 0 : object_class->get_property = shumate_vector_symbol_get_property;
777 : 0 : object_class->set_property = shumate_vector_symbol_set_property;
778 : :
779 : 0 : widget_class->contains = shumate_vector_symbol_contains;
780 : 0 : widget_class->get_request_mode = shumate_vector_symbol_get_request_mode;
781 : 0 : widget_class->snapshot = shumate_vector_symbol_snapshot;
782 : 0 : widget_class->measure = shumate_vector_symbol_measure;
783 : :
784 : 0 : obj_properties[PROP_SYMBOL_INFO] =
785 : 0 : g_param_spec_boxed ("symbol-info",
786 : : "Symbol info",
787 : : "Symbol info",
788 : : SHUMATE_TYPE_VECTOR_SYMBOL_INFO,
789 : : G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
790 : :
791 : 0 : g_object_class_install_properties (object_class, N_PROPS, obj_properties);
792 : :
793 : 0 : signals[CLICKED] =
794 : 0 : g_signal_new ("clicked",
795 : : G_OBJECT_CLASS_TYPE (object_class),
796 : : G_SIGNAL_RUN_LAST,
797 : : 0, NULL, NULL,
798 : : g_cclosure_marshal_VOID__OBJECT,
799 : : G_TYPE_NONE,
800 : : 1,
801 : : SHUMATE_TYPE_SYMBOL_EVENT);
802 : :
803 : 0 : gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL);
804 : 0 : }
805 : :
806 : : static void
807 : 0 : on_clicked (ShumateVectorSymbol *self,
808 : : gint n_press,
809 : : double x,
810 : : double y,
811 : : GtkGestureClick *click)
812 : : {
813 : 0 : g_autoptr(ShumateSymbolEvent) event = shumate_symbol_event_new (self->symbol_info->details->layer,
814 : 0 : self->symbol_info->details->source_layer,
815 : 0 : self->symbol_info->details->feature_id,
816 : 0 : self->symbol_info->details->tags);
817 [ # # ]: 0 : g_signal_emit (self, signals[CLICKED], 0, event);
818 : 0 : }
819 : :
820 : : static void
821 : 0 : shumate_vector_symbol_init (ShumateVectorSymbol *self)
822 : : {
823 : 0 : GtkGesture *click = gtk_gesture_click_new ();
824 : 0 : g_signal_connect_object (click, "released", G_CALLBACK (on_clicked), self, G_CONNECT_SWAPPED);
825 : 0 : gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (click));
826 : 0 : }
827 : :
828 : :
829 : : ShumateVectorSymbolInfo *
830 : 0 : shumate_vector_symbol_get_symbol_info (ShumateVectorSymbol *self)
831 : : {
832 [ # # ]: 0 : g_return_val_if_fail (SHUMATE_IS_VECTOR_SYMBOL (self), NULL);
833 : :
834 : 0 : return self->symbol_info;
835 : : }
836 : :
837 : : static void
838 : 0 : rotate_around_center (double *x,
839 : : double *y,
840 : : double angle)
841 : : {
842 : : /* Rotate (x, y) around (0, 0) */
843 : :
844 [ # # ]: 0 : if (angle == 0)
845 : : return;
846 : :
847 : 0 : double old_x = *x;
848 : 0 : double old_y = *y;
849 : :
850 : 0 : *x = cosf (angle) * old_x - sinf (angle) * old_y;
851 : 0 : *y = sinf (angle) * old_x + cosf (angle) * old_y;
852 : : }
853 : :
854 : :
855 : : gboolean
856 : 0 : shumate_vector_symbol_calculate_collision (ShumateVectorSymbol *self,
857 : : ShumateVectorCollision *collision,
858 : : double x,
859 : : double y,
860 : : double tile_size_for_zoom,
861 : : double rotation,
862 : : graphene_rect_t *bounds_out)
863 : : {
864 : 0 : double yextent = self->symbol_info->details->text_size / 2.0;
865 : 0 : gboolean check;
866 : 0 : ShumateVectorPointIter iter;
867 : 0 : ShumateVectorPoint point;
868 : 0 : ShumateVectorPoint midpoint = {0};
869 : 0 : int save = 0;
870 : :
871 : 0 : shumate_vector_collision_rollback_pending (collision, 0);
872 : :
873 [ # # ]: 0 : if (self->symbol_info->line != NULL)
874 : : {
875 : 0 : midpoint = self->midpoint;
876 : :
877 : 0 : rotate_around_center (&midpoint.x, &midpoint.y, rotation);
878 : 0 : midpoint.x *= tile_size_for_zoom;
879 : 0 : midpoint.y *= tile_size_for_zoom;
880 : : }
881 : :
882 [ # # ]: 0 : if (self->glyphs != NULL)
883 : : {
884 : 0 : double line_length = self->symbol_info->line_length;
885 : 0 : double length = self->layout_width / tile_size_for_zoom;
886 [ # # ]: 0 : double start_pos = MAX (0, self->symbol_info->line_position - (length / 2.0));
887 : :
888 [ # # ]: 0 : g_assert (self->symbol_info->line != NULL);
889 : :
890 [ # # ]: 0 : if (length > line_length - start_pos)
891 : : {
892 : 0 : self->show_text = FALSE;
893 : 0 : return FALSE;
894 : : }
895 : :
896 : 0 : shumate_vector_point_iter_init (&iter, self->symbol_info->line);
897 : 0 : shumate_vector_point_iter_advance (&iter, start_pos);
898 : :
899 : 0 : self->show_text = TRUE;
900 : :
901 : 0 : do
902 : : {
903 [ # # ]: 0 : double xextent = MIN (length, shumate_vector_point_iter_get_segment_length (&iter) - iter.distance) * tile_size_for_zoom / 2;
904 : :
905 [ # # ]: 0 : if (shumate_vector_point_iter_is_at_end (&iter))
906 : : return FALSE;
907 : :
908 : 0 : shumate_vector_point_iter_get_segment_center (&iter, length, &point);
909 : 0 : point.x -= self->symbol_info->x;
910 : 0 : point.y -= self->symbol_info->y;
911 : :
912 : 0 : rotate_around_center (&point.x, &point.y, rotation);
913 : 0 : point.x *= tile_size_for_zoom;
914 : 0 : point.y *= tile_size_for_zoom;
915 : :
916 : 0 : gboolean check = shumate_vector_collision_check (
917 : : collision,
918 : 0 : x + point.x,
919 : 0 : y + point.y,
920 : : xextent + self->symbol_info->details->text_padding,
921 : 0 : yextent + self->symbol_info->details->text_padding,
922 : 0 : rotation + shumate_vector_point_iter_get_current_angle (&iter),
923 : : self->symbol_info->details->text_overlap,
924 : 0 : self->symbol_info->details->text_ignore_placement,
925 : : self
926 : : );
927 : :
928 [ # # ]: 0 : if (!check)
929 : : {
930 [ # # ]: 0 : if (self->symbol_info->details->text_optional)
931 : : {
932 : 0 : shumate_vector_collision_rollback_pending (collision, save);
933 : 0 : self->show_text = FALSE;
934 : 0 : break;
935 : : }
936 : : else
937 : : return FALSE;
938 : : }
939 : : }
940 [ # # ]: 0 : while ((length -= shumate_vector_point_iter_next_segment (&iter)) > 0);
941 : : }
942 [ # # ]: 0 : else if (self->glyphs_node != NULL)
943 : : {
944 : 0 : ShumateVectorAnchor anchor = self->symbol_info->details->text_anchor;
945 : 0 : double angle = 0;
946 : 0 : double offset_x = self->symbol_info->details->text_offset_x * self->symbol_info->details->text_size;
947 : 0 : double offset_y = self->symbol_info->details->text_offset_y * self->symbol_info->details->text_size;
948 : :
949 : 0 : add_anchor_offset (anchor, &offset_x, &offset_y, self->layout_width, self->layout_height);
950 : :
951 [ # # ]: 0 : if (self->symbol_info->details->text_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
952 : 0 : angle = rotation;
953 : :
954 : 0 : rotate_around_center (&offset_x, &offset_y, angle);
955 : :
956 : 0 : self->show_text = TRUE;
957 : :
958 : 0 : check = shumate_vector_collision_check (
959 : : collision,
960 : 0 : x + midpoint.x + offset_x,
961 : 0 : y + midpoint.y + offset_y,
962 : 0 : self->layout_width / 2.0 + self->symbol_info->details->text_padding,
963 : 0 : yextent + self->symbol_info->details->text_padding,
964 : : angle,
965 : : self->symbol_info->details->text_overlap,
966 : 0 : self->symbol_info->details->text_ignore_placement,
967 : : self
968 : : );
969 : :
970 [ # # ]: 0 : if (!check)
971 : : {
972 [ # # ]: 0 : if (self->symbol_info->details->text_optional)
973 : : {
974 : 0 : shumate_vector_collision_rollback_pending (collision, save);
975 : 0 : self->show_text = FALSE;
976 : : }
977 : : else
978 : 0 : return FALSE;
979 : : }
980 : : }
981 : :
982 : 0 : save = shumate_vector_collision_save_pending (collision);
983 : :
984 [ # # ]: 0 : if (self->symbol_info->details->icon_image)
985 : : {
986 : 0 : float angle = 0;
987 : 0 : double icon_width = shumate_vector_sprite_get_width (self->symbol_info->details->icon_image)
988 : 0 : * self->symbol_info->details->icon_size;
989 : 0 : double icon_height = shumate_vector_sprite_get_height (self->symbol_info->details->icon_image)
990 : 0 : * self->symbol_info->details->icon_size;
991 : :
992 : 0 : double offset_x = self->symbol_info->details->icon_offset_x * self->symbol_info->details->icon_size;
993 : 0 : double offset_y = self->symbol_info->details->icon_offset_y * self->symbol_info->details->icon_size;
994 : :
995 : 0 : self->show_icon = TRUE;
996 : :
997 : 0 : add_anchor_offset (self->symbol_info->details->icon_anchor, &offset_x, &offset_y, icon_width, icon_height);
998 : :
999 [ # # ]: 0 : if (self->symbol_info->details->icon_rotation_alignment == SHUMATE_VECTOR_ALIGNMENT_MAP)
1000 : 0 : angle = rotation + self->midpoint_angle;
1001 : 0 : angle += self->symbol_info->details->icon_rotate;
1002 : 0 : offset_x += (self->symbol_info->details->icon_padding_right - self->symbol_info->details->icon_padding_left) / 2.0;
1003 : 0 : offset_y += (self->symbol_info->details->icon_padding_bottom - self->symbol_info->details->icon_padding_top) / 2.0;
1004 : 0 : rotate_around_center (&offset_x, &offset_y, angle);
1005 : :
1006 : 0 : check = shumate_vector_collision_check (
1007 : : collision,
1008 : 0 : x + midpoint.x + offset_x,
1009 : 0 : y + midpoint.y + offset_y,
1010 : 0 : (icon_width + self->symbol_info->details->icon_padding_left + self->symbol_info->details->icon_padding_right) / 2 ,
1011 : 0 : (icon_height + self->symbol_info->details->icon_padding_top + self->symbol_info->details->icon_padding_bottom) / 2,
1012 : : angle,
1013 : : self->symbol_info->details->icon_overlap,
1014 : 0 : self->symbol_info->details->icon_ignore_placement,
1015 : : self
1016 : : );
1017 : :
1018 [ # # ]: 0 : if (!check)
1019 : : {
1020 [ # # ]: 0 : if (self->symbol_info->details->icon_optional)
1021 : : {
1022 : 0 : shumate_vector_collision_rollback_pending (collision, save);
1023 : 0 : self->show_icon = FALSE;
1024 : : }
1025 : : else
1026 : 0 : return FALSE;
1027 : : }
1028 : : }
1029 : :
1030 [ # # ]: 0 : if (!self->show_icon && !self->show_text)
1031 : : return FALSE;
1032 : :
1033 : 0 : shumate_vector_collision_commit_pending (collision, &self->bounds);
1034 : 0 : *bounds_out = self->bounds;
1035 : 0 : self->x = x;
1036 : 0 : self->y = y;
1037 : 0 : return TRUE;
1038 : : }
1039 : :
|