1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2014 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 */
23
24 #include <string.h>
25
26 #include "BLI_buffer.h"
27 #include "BLI_ghash.h"
28 #include "BLI_listbase.h"
29 #include "BLI_math.h"
30 #include "BLI_math_bits.h"
31 #include "BLI_rect.h"
32
33 #include "BKE_context.h"
34 #include "BKE_global.h"
35 #include "BKE_main.h"
36
37 #include "ED_screen.h"
38 #include "ED_select_utils.h"
39 #include "ED_view3d.h"
40
41 #include "GPU_matrix.h"
42 #include "GPU_select.h"
43 #include "GPU_state.h"
44
45 #include "MEM_guardedalloc.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49 #include "wm_event_system.h"
50
51 /* for tool-tips */
52 #include "UI_interface.h"
53
54 #include "DEG_depsgraph.h"
55
56 /* own includes */
57 #include "wm_gizmo_intern.h"
58 #include "wm_gizmo_wmapi.h"
59
60 /**
61 * Store all gizmo-maps here. Anyone who wants to register a gizmo for a certain
62 * area type can query the gizmo-map to do so.
63 */
64 static ListBase gizmomaptypes = {NULL, NULL};
65
66 /**
67 * Update when gizmo-map types change.
68 */
69 /* so operator removal can trigger update */
70 typedef enum eWM_GizmoFlagGroupTypeGlobalFlag {
71 /** Initialize by #wmGroupType.type_update_flag. */
72 WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
73 /** Remove by #wmGroupType.type_update_flag. */
74 WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
75
76 /** Remove by #wmGroup.tag_remove. */
77 WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE = (1 << 2),
78 } eWM_GizmoFlagGroupTypeGlobalFlag;
79
80 static eWM_GizmoFlagGroupTypeGlobalFlag wm_gzmap_type_update_flag = 0;
81
82 /**
83 * Gizmo-map update tagging.
84 */
85 enum {
86 /** #gizmomap_prepare_drawing has run */
87 GIZMOMAP_IS_PREPARE_DRAW = (1 << 0),
88 GIZMOMAP_IS_REFRESH_CALLBACK = (1 << 1),
89 };
90
91 /* -------------------------------------------------------------------- */
92 /** \name wmGizmoMap Selection Array API
93 *
94 * Just handle ``wm_gizmomap_select_array_*``, not flags or callbacks.
95 *
96 * \{ */
97
wm_gizmomap_select_array_ensure_len_alloc(wmGizmoMap * gzmap,int len)98 static void wm_gizmomap_select_array_ensure_len_alloc(wmGizmoMap *gzmap, int len)
99 {
100 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
101 if (len <= msel->len_alloc) {
102 return;
103 }
104 msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
105 msel->len_alloc = len;
106 }
107
wm_gizmomap_select_array_clear(wmGizmoMap * gzmap)108 void wm_gizmomap_select_array_clear(wmGizmoMap *gzmap)
109 {
110 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
111 MEM_SAFE_FREE(msel->items);
112 msel->len = 0;
113 msel->len_alloc = 0;
114 }
115
wm_gizmomap_select_array_shrink(wmGizmoMap * gzmap,int len_subtract)116 void wm_gizmomap_select_array_shrink(wmGizmoMap *gzmap, int len_subtract)
117 {
118 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
119 msel->len -= len_subtract;
120 if (msel->len <= 0) {
121 wm_gizmomap_select_array_clear(gzmap);
122 }
123 else {
124 if (msel->len < msel->len_alloc / 2) {
125 msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
126 msel->len_alloc = msel->len;
127 }
128 }
129 }
130
wm_gizmomap_select_array_push_back(wmGizmoMap * gzmap,wmGizmo * gz)131 void wm_gizmomap_select_array_push_back(wmGizmoMap *gzmap, wmGizmo *gz)
132 {
133 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
134 BLI_assert(msel->len <= msel->len_alloc);
135 if (msel->len == msel->len_alloc) {
136 msel->len_alloc = (msel->len + 1) * 2;
137 msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
138 }
139 msel->items[msel->len++] = gz;
140 }
141
wm_gizmomap_select_array_remove(wmGizmoMap * gzmap,wmGizmo * gz)142 void wm_gizmomap_select_array_remove(wmGizmoMap *gzmap, wmGizmo *gz)
143 {
144 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
145 /* remove gizmo from selected_gizmos array */
146 for (int i = 0; i < msel->len; i++) {
147 if (msel->items[i] == gz) {
148 for (int j = i; j < (msel->len - 1); j++) {
149 msel->items[j] = msel->items[j + 1];
150 }
151 wm_gizmomap_select_array_shrink(gzmap, 1);
152 break;
153 }
154 }
155 }
156
157 /** \} */
158
159 /* -------------------------------------------------------------------- */
160 /** \name wmGizmoMap
161 *
162 * \{ */
163
wm_gizmomap_new_from_type_ex(struct wmGizmoMapType * gzmap_type,wmGizmoMap * gzmap)164 static wmGizmoMap *wm_gizmomap_new_from_type_ex(struct wmGizmoMapType *gzmap_type,
165 wmGizmoMap *gzmap)
166 {
167 gzmap->type = gzmap_type;
168 gzmap->is_init = true;
169 WM_gizmomap_tag_refresh(gzmap);
170
171 /* create all gizmo-groups for this gizmo-map. We may create an empty one
172 * too in anticipation of gizmos from operators etc */
173 LISTBASE_FOREACH (wmGizmoGroupTypeRef *, gzgt_ref, &gzmap_type->grouptype_refs) {
174 wm_gizmogroup_new_from_type(gzmap, gzgt_ref->type);
175 }
176
177 return gzmap;
178 }
179
180 /**
181 * Creates a gizmo-map with all registered gizmos for that type
182 */
WM_gizmomap_new_from_type(const struct wmGizmoMapType_Params * gzmap_params)183 wmGizmoMap *WM_gizmomap_new_from_type(const struct wmGizmoMapType_Params *gzmap_params)
184 {
185 wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(gzmap_params);
186 wmGizmoMap *gzmap = MEM_callocN(sizeof(wmGizmoMap), "GizmoMap");
187 wm_gizmomap_new_from_type_ex(gzmap_type, gzmap);
188 return gzmap;
189 }
190
wm_gizmomap_free_data(wmGizmoMap * gzmap)191 static void wm_gizmomap_free_data(wmGizmoMap *gzmap)
192 {
193 /* Clear first so further calls don't waste time trying to maintain correct array state. */
194 wm_gizmomap_select_array_clear(gzmap);
195
196 for (wmGizmoGroup *gzgroup = gzmap->groups.first, *gzgroup_next; gzgroup;
197 gzgroup = gzgroup_next) {
198 gzgroup_next = gzgroup->next;
199 BLI_assert(gzgroup->parent_gzmap == gzmap);
200 wm_gizmogroup_free(NULL, gzgroup);
201 }
202 BLI_assert(BLI_listbase_is_empty(&gzmap->groups));
203 }
204
wm_gizmomap_remove(wmGizmoMap * gzmap)205 void wm_gizmomap_remove(wmGizmoMap *gzmap)
206 {
207 wm_gizmomap_free_data(gzmap);
208 MEM_freeN(gzmap);
209 }
210
211 /** Re-create the gizmos (use when changing theme settings). */
WM_gizmomap_reinit(wmGizmoMap * gzmap)212 void WM_gizmomap_reinit(wmGizmoMap *gzmap)
213 {
214 wmGizmoMapType *gzmap_type = gzmap->type;
215 wm_gizmomap_free_data(gzmap);
216 memset(gzmap, 0x0, sizeof(*gzmap));
217 wm_gizmomap_new_from_type_ex(gzmap_type, gzmap);
218 }
219
WM_gizmomap_group_find(struct wmGizmoMap * gzmap,const char * idname)220 wmGizmoGroup *WM_gizmomap_group_find(struct wmGizmoMap *gzmap, const char *idname)
221 {
222 wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
223 if (gzgt) {
224 return WM_gizmomap_group_find_ptr(gzmap, gzgt);
225 }
226 return NULL;
227 }
228
WM_gizmomap_group_find_ptr(struct wmGizmoMap * gzmap,const struct wmGizmoGroupType * gzgt)229 wmGizmoGroup *WM_gizmomap_group_find_ptr(struct wmGizmoMap *gzmap,
230 const struct wmGizmoGroupType *gzgt)
231 {
232 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
233 if (gzgroup->type == gzgt) {
234 return gzgroup;
235 }
236 }
237 return NULL;
238 }
239
WM_gizmomap_group_list(wmGizmoMap * gzmap)240 const ListBase *WM_gizmomap_group_list(wmGizmoMap *gzmap)
241 {
242 return &gzmap->groups;
243 }
244
WM_gizmomap_is_any_selected(const wmGizmoMap * gzmap)245 bool WM_gizmomap_is_any_selected(const wmGizmoMap *gzmap)
246 {
247 return gzmap->gzmap_context.select.len != 0;
248 }
249
250 /**
251 * \note We could use a callback to define bounds, for now just use matrix location.
252 */
WM_gizmomap_minmax(const wmGizmoMap * gzmap,bool UNUSED (use_hidden),bool use_select,float r_min[3],float r_max[3])253 bool WM_gizmomap_minmax(const wmGizmoMap *gzmap,
254 bool UNUSED(use_hidden),
255 bool use_select,
256 float r_min[3],
257 float r_max[3])
258 {
259 if (use_select) {
260 int i;
261 for (i = 0; i < gzmap->gzmap_context.select.len; i++) {
262 minmax_v3v3_v3(r_min, r_max, gzmap->gzmap_context.select.items[i]->matrix_basis[3]);
263 }
264 return i != 0;
265 }
266
267 bool ok = false;
268 BLI_assert(!"TODO");
269 return ok;
270 }
271
272 /**
273 * Creates and returns idname hash table for (visible) gizmos in \a gzmap
274 *
275 * \param poll: Polling function for excluding gizmos.
276 * \param data: Custom data passed to \a poll
277 *
278 * TODO(campbell): this uses unreliable order,
279 * best we use an iterator function instead of a hash.
280 */
WM_gizmomap_gizmo_hash_new(const bContext * C,wmGizmoMap * gzmap,bool (* poll)(const wmGizmo *,void *),void * data,const eWM_GizmoFlag flag_exclude)281 static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C,
282 wmGizmoMap *gzmap,
283 bool (*poll)(const wmGizmo *, void *),
284 void *data,
285 const eWM_GizmoFlag flag_exclude)
286 {
287 GHash *hash = BLI_ghash_ptr_new(__func__);
288
289 /* collect gizmos */
290 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
291 if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
292 LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
293 if (((flag_exclude == 0) || ((gz->flag & flag_exclude) == 0)) &&
294 (!poll || poll(gz, data))) {
295 BLI_ghash_insert(hash, gz, gz);
296 }
297 }
298 }
299 }
300
301 return hash;
302 }
303
WM_gizmomap_drawstep_from_gizmo_group(const wmGizmoGroup * gzgroup)304 eWM_GizmoFlagMapDrawStep WM_gizmomap_drawstep_from_gizmo_group(const wmGizmoGroup *gzgroup)
305 {
306 eWM_GizmoFlagMapDrawStep step;
307 if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
308 step = WM_GIZMOMAP_DRAWSTEP_3D;
309 }
310 else {
311 step = WM_GIZMOMAP_DRAWSTEP_2D;
312 }
313 return step;
314 }
315
WM_gizmomap_tag_refresh_drawstep(wmGizmoMap * gzmap,const eWM_GizmoFlagMapDrawStep drawstep)316 void WM_gizmomap_tag_refresh_drawstep(wmGizmoMap *gzmap, const eWM_GizmoFlagMapDrawStep drawstep)
317 {
318 BLI_assert((uint)drawstep < WM_GIZMOMAP_DRAWSTEP_MAX);
319 if (gzmap) {
320 gzmap->update_flag[drawstep] |= (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK);
321 }
322 }
323
WM_gizmomap_tag_refresh(wmGizmoMap * gzmap)324 void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
325 {
326 if (gzmap) {
327 for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
328 gzmap->update_flag[i] |= (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK);
329 }
330 }
331 }
332
WM_gizmomap_tag_delay_refresh_for_tweak_check(wmGizmoMap * gzmap)333 bool WM_gizmomap_tag_delay_refresh_for_tweak_check(wmGizmoMap *gzmap)
334 {
335 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
336 if (gzgroup->hide.delay_refresh_for_tweak) {
337 return true;
338 }
339 }
340 return false;
341 }
342
gizmo_prepare_drawing(wmGizmoMap * gzmap,wmGizmo * gz,const bContext * C,ListBase * draw_gizmos,const eWM_GizmoFlagMapDrawStep drawstep)343 static bool gizmo_prepare_drawing(wmGizmoMap *gzmap,
344 wmGizmo *gz,
345 const bContext *C,
346 ListBase *draw_gizmos,
347 const eWM_GizmoFlagMapDrawStep drawstep)
348 {
349 int do_draw = wm_gizmo_is_visible(gz);
350 if (do_draw == 0) {
351 /* skip */
352 }
353 else {
354 /* Ensure we get RNA updates */
355 if (do_draw & WM_GIZMO_IS_VISIBLE_UPDATE) {
356 /* hover gizmos need updating, even if we don't draw them */
357 wm_gizmo_update(gz, C, (gzmap->update_flag[drawstep] & GIZMOMAP_IS_PREPARE_DRAW) != 0);
358 }
359 if (do_draw & WM_GIZMO_IS_VISIBLE_DRAW) {
360 BLI_addhead(draw_gizmos, BLI_genericNodeN(gz));
361 }
362 return true;
363 }
364
365 return false;
366 }
367
368 /**
369 * Update gizmos of \a gzmap to prepare for drawing. Adds all gizmos that
370 * should be drawn to list \a draw_gizmos, note that added items need freeing.
371 */
gizmomap_prepare_drawing(wmGizmoMap * gzmap,const bContext * C,ListBase * draw_gizmos,const eWM_GizmoFlagMapDrawStep drawstep)372 static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
373 const bContext *C,
374 ListBase *draw_gizmos,
375 const eWM_GizmoFlagMapDrawStep drawstep)
376 {
377 if (!gzmap || BLI_listbase_is_empty(&gzmap->groups)) {
378 return;
379 }
380
381 gzmap->is_init = false;
382
383 wmGizmo *gz_modal = gzmap->gzmap_context.modal;
384
385 /* only active gizmo needs updating */
386 if (gz_modal) {
387 if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) {
388 if ((gz_modal->parent_gzgroup->hide.any == 0) &&
389 wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) {
390 if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) {
391 gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
392 }
393 }
394 /* don't draw any other gizmos */
395 return;
396 }
397 }
398
399 /* Allow refresh functions to ask to be refreshed again, clear before the loop below. */
400 const bool do_refresh = gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK;
401 gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
402
403 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
404 /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
405 if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) ||
406 !WM_gizmo_group_type_poll(C, gzgroup->type)) {
407 continue;
408 }
409
410 /* Needs to be initialized on first draw. */
411 /* XXX weak: Gizmo-group may skip refreshing if it's invisible
412 * (map gets untagged nevertheless). */
413 if (do_refresh) {
414 /* force refresh again. */
415 gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
416 }
417 /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
418 WM_gizmogroup_ensure_init(C, gzgroup);
419
420 /* Check after ensure which can run refresh and update this value. */
421 if (gzgroup->hide.any != 0) {
422 continue;
423 }
424
425 /* prepare drawing */
426 if (gzgroup->type->draw_prepare) {
427 gzgroup->type->draw_prepare(C, gzgroup);
428 }
429
430 LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
431 gizmo_prepare_drawing(gzmap, gz, C, draw_gizmos, drawstep);
432 }
433 }
434
435 gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
436 }
437
438 /**
439 * Draw all visible gizmos in \a gzmap.
440 * Uses global draw_gizmos listbase.
441 */
gizmos_draw_list(const wmGizmoMap * gzmap,const bContext * C,ListBase * draw_gizmos)442 static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBase *draw_gizmos)
443 {
444 /* Can be empty if we're dynamically added and removed. */
445 if ((gzmap == NULL) || BLI_listbase_is_empty(&gzmap->groups)) {
446 return;
447 }
448
449 /* TODO(campbell): This will need it own shader probably?
450 * Don't think it can be handled from that point though. */
451 /* const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */
452
453 bool is_depth_prev = false;
454
455 /* draw_gizmos contains all visible gizmos - draw them */
456 for (LinkData *link = draw_gizmos->first, *link_next; link; link = link_next) {
457 wmGizmo *gz = link->data;
458 link_next = link->next;
459
460 bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
461
462 /* Weak! since we don't 100% support depth yet (select ignores depth)
463 * always show highlighted. */
464 if (is_depth && (gz->state & WM_GIZMO_STATE_HIGHLIGHT)) {
465 is_depth = false;
466 }
467
468 if (is_depth == is_depth_prev) {
469 /* pass */
470 }
471 else {
472 if (is_depth) {
473 GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
474 }
475 else {
476 GPU_depth_test(GPU_DEPTH_NONE);
477 }
478 is_depth_prev = is_depth;
479 }
480
481 /* XXX force AntiAlias Gizmos. */
482 GPU_line_smooth(true);
483 GPU_polygon_smooth(true);
484
485 gz->type->draw(C, gz);
486
487 GPU_line_smooth(false);
488 GPU_polygon_smooth(false);
489
490 /* free/remove gizmo link after drawing */
491 BLI_freelinkN(draw_gizmos, link);
492 }
493
494 if (is_depth_prev) {
495 GPU_depth_test(GPU_DEPTH_NONE);
496 }
497 }
498
WM_gizmomap_draw(wmGizmoMap * gzmap,const bContext * C,const eWM_GizmoFlagMapDrawStep drawstep)499 void WM_gizmomap_draw(wmGizmoMap *gzmap,
500 const bContext *C,
501 const eWM_GizmoFlagMapDrawStep drawstep)
502 {
503 if (!WM_gizmo_context_check_drawstep(C, drawstep)) {
504 return;
505 }
506
507 ListBase draw_gizmos = {NULL};
508
509 gizmomap_prepare_drawing(gzmap, C, &draw_gizmos, drawstep);
510 gizmos_draw_list(gzmap, C, &draw_gizmos);
511 BLI_assert(BLI_listbase_is_empty(&draw_gizmos));
512 }
513
gizmo_draw_select_3d_loop(const bContext * C,wmGizmo ** visible_gizmos,const int visible_gizmos_len,bool * r_use_select_bias)514 static void gizmo_draw_select_3d_loop(const bContext *C,
515 wmGizmo **visible_gizmos,
516 const int visible_gizmos_len,
517 bool *r_use_select_bias)
518 {
519
520 /* TODO(campbell): this depends on depth buffer being written to,
521 * currently broken for the 3D view. */
522 bool is_depth_prev = false;
523 bool is_depth_skip_prev = false;
524
525 for (int select_id = 0; select_id < visible_gizmos_len; select_id++) {
526 wmGizmo *gz = visible_gizmos[select_id];
527 if (gz->type->draw_select == NULL) {
528 continue;
529 }
530
531 bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
532 if (is_depth == is_depth_prev) {
533 /* pass */
534 }
535 else {
536 if (is_depth) {
537 GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
538 }
539 else {
540 GPU_depth_test(GPU_DEPTH_NONE);
541 }
542 is_depth_prev = is_depth;
543 }
544 bool is_depth_skip = (gz->flag & WM_GIZMO_SELECT_BACKGROUND) != 0;
545 if (is_depth_skip == is_depth_skip_prev) {
546 /* pass */
547 }
548 else {
549 GPU_depth_mask(!is_depth_skip);
550 is_depth_skip_prev = is_depth_skip;
551 }
552
553 if (gz->select_bias != 0.0) {
554 *r_use_select_bias = true;
555 }
556
557 /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */
558
559 gz->type->draw_select(C, gz, select_id << 8);
560 }
561
562 if (is_depth_prev) {
563 GPU_depth_test(GPU_DEPTH_NONE);
564 }
565 if (is_depth_skip_prev) {
566 GPU_depth_mask(true);
567 }
568 }
569
gizmo_find_intersected_3d_intern(wmGizmo ** visible_gizmos,const int visible_gizmos_len,const bContext * C,const int co[2],const int hotspot)570 static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
571 const int visible_gizmos_len,
572 const bContext *C,
573 const int co[2],
574 const int hotspot)
575 {
576 const wmWindowManager *wm = CTX_wm_manager(C);
577 ScrArea *area = CTX_wm_area(C);
578 ARegion *region = CTX_wm_region(C);
579 View3D *v3d = area->spacedata.first;
580 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
581 rcti rect;
582 /* Almost certainly overkill, but allow for many custom gizmos. */
583 uint buffer[MAXPICKBUF];
584 short hits;
585
586 BLI_rcti_init_pt_radius(&rect, co, hotspot);
587
588 ED_view3d_draw_setup_view(
589 wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, &rect);
590
591 bool use_select_bias = false;
592
593 /* TODO: waiting for the GPU in the middle of the event loop for every
594 * mouse move is bad for performance, we need to find a solution to not
595 * use the GPU or draw something once. (see T61474) */
596 GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
597 /* do the drawing */
598 gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias);
599
600 hits = GPU_select_end();
601
602 if (hits > 0) {
603 GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
604 gizmo_draw_select_3d_loop(C, visible_gizmos, visible_gizmos_len, &use_select_bias);
605 GPU_select_end();
606 }
607
608 ED_view3d_draw_setup_view(
609 wm, CTX_wm_window(C), depsgraph, CTX_data_scene(C), region, v3d, NULL, NULL, NULL);
610
611 if (use_select_bias && (hits > 1)) {
612 float co_direction[3];
613 float co_screen[3] = {co[0], co[1], 0.0f};
614 ED_view3d_win_to_vector(region, (float[2]){UNPACK2(co)}, co_direction);
615
616 RegionView3D *rv3d = region->regiondata;
617 const int viewport[4] = {0, 0, region->winx, region->winy};
618 float co_3d_origin[3];
619
620 /* Avoid multiple calculations. */
621 struct GPUMatrixUnproject_Precalc unproj_precalc;
622 GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
623
624 GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
625
626 uint *buf_iter = buffer;
627 int hit_found = -1;
628 float dot_best = FLT_MAX;
629
630 for (int i = 0; i < hits; i++, buf_iter += 4) {
631 BLI_assert(buf_iter[3] != -1);
632 wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
633 float co_3d[3];
634 co_screen[2] = int_as_float(buf_iter[1]);
635 GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d);
636 float select_bias = gz->select_bias;
637 if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
638 select_bias *= gz->scale_final;
639 }
640 sub_v3_v3(co_3d, co_3d_origin);
641 const float dot_test = dot_v3v3(co_3d, co_direction) - select_bias;
642 if (dot_best > dot_test) {
643 dot_best = dot_test;
644 hit_found = buf_iter[3];
645 }
646 }
647 return hit_found;
648 }
649
650 const uint *hit_near = GPU_select_buffer_near(buffer, hits);
651 return hit_near ? hit_near[3] : -1;
652 }
653
654 /**
655 * Try to find a 3D gizmo at screen-space coordinate \a co. Uses OpenGL picking.
656 */
gizmo_find_intersected_3d(bContext * C,const int co[2],wmGizmo ** visible_gizmos,const int visible_gizmos_len,int * r_part)657 static wmGizmo *gizmo_find_intersected_3d(bContext *C,
658 const int co[2],
659 wmGizmo **visible_gizmos,
660 const int visible_gizmos_len,
661 int *r_part)
662 {
663 wmGizmo *result = NULL;
664 int visible_gizmos_len_trim = visible_gizmos_len;
665 int hit = -1;
666
667 *r_part = 0;
668
669 /* set up view matrices */
670 view3d_operator_needs_opengl(C);
671
672 /* Search for 3D gizmo's that use the 2D callback for checking intersections. */
673 bool has_3d = false;
674 {
675 for (int select_id = 0; select_id < visible_gizmos_len; select_id++) {
676 wmGizmo *gz = visible_gizmos[select_id];
677 /* With both defined, favor the 3D, in case the gizmo can be used in 2D or 3D views. */
678 if (gz->type->test_select && (gz->type->draw_select == NULL)) {
679 if ((*r_part = gz->type->test_select(C, gz, co)) != -1) {
680 hit = select_id;
681 result = gz;
682 /* Don't search past this when checking intersections. */
683 visible_gizmos_len_trim = select_id;
684 break;
685 }
686 }
687 else if (gz->type->draw_select != NULL) {
688 has_3d = true;
689 }
690 }
691 }
692
693 /* Search for 3D intersections if they're before 2D that have been found (if any).
694 * This way we always use the first hit. */
695 if (has_3d) {
696 const int hotspot_radii[] = {
697 3 * U.pixelsize,
698 /* This runs on mouse move, careful doing too many tests! */
699 10 * U.pixelsize,
700 };
701 for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
702 hit = gizmo_find_intersected_3d_intern(
703 visible_gizmos, visible_gizmos_len_trim, C, co, hotspot_radii[i]);
704 if (hit != -1) {
705 break;
706 }
707 }
708
709 if (hit != -1) {
710 const int select_id = hit >> 8;
711 const int select_part = hit & 0xff;
712 BLI_assert(select_id < visible_gizmos_len);
713 *r_part = select_part;
714 result = visible_gizmos[select_id];
715 }
716 }
717
718 return result;
719 }
720
721 /**
722 * Try to find a gizmo under the mouse position. 2D intersections have priority over
723 * 3D ones (could check for smallest screen-space distance but not needed right now).
724 */
wm_gizmomap_highlight_find(wmGizmoMap * gzmap,bContext * C,const wmEvent * event,int * r_part)725 wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
726 bContext *C,
727 const wmEvent *event,
728 int *r_part)
729 {
730 wmWindowManager *wm = CTX_wm_manager(C);
731 wmGizmo *gz = NULL;
732 BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128);
733 bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
734
735 int mval[2] = {UNPACK2(event->mval)};
736
737 /* Ensure for drag events we use the location where the user clicked.
738 * Without this click-dragging on a gizmo can accidentally act on the wrong gizmo. */
739 if (ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG)) {
740 mval[0] += event->x - event->prevclickx;
741 mval[1] += event->y - event->prevclicky;
742 }
743
744 for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
745 do_step[i] = WM_gizmo_context_check_drawstep(C, i);
746 }
747
748 const int event_modifier = WM_event_modifier_flag(event);
749
750 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
751
752 /* If it were important we could initialize here,
753 * but this only happens when events are handled before drawing,
754 * just skip to keep code-path for initializing gizmos simple. */
755 if ((gzgroup->hide.any != 0) || ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) {
756 continue;
757 }
758
759 if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
760 eWM_GizmoFlagMapDrawStep step;
761 if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
762 step = WM_GIZMOMAP_DRAWSTEP_3D;
763 }
764 else {
765 step = WM_GIZMOMAP_DRAWSTEP_2D;
766 }
767
768 if (do_step[step]) {
769 if (gzmap->update_flag[step] & GIZMOMAP_IS_REFRESH_CALLBACK) {
770 WM_gizmo_group_refresh(C, gzgroup);
771 /* cleared below */
772 }
773 if (step == WM_GIZMOMAP_DRAWSTEP_3D) {
774 wm_gizmogroup_intersectable_gizmos_to_list(
775 wm, gzgroup, event_modifier, &visible_3d_gizmos);
776 }
777 else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
778 if ((gz = wm_gizmogroup_find_intersected_gizmo(
779 wm, gzgroup, C, event_modifier, mval, r_part))) {
780 break;
781 }
782 }
783 }
784 }
785 }
786
787 if (visible_3d_gizmos.count) {
788 /* 2D gizmos get priority. */
789 if (gz == NULL) {
790 gz = gizmo_find_intersected_3d(
791 C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part);
792 }
793 }
794 BLI_buffer_free(&visible_3d_gizmos);
795
796 gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_3D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
797 gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_2D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
798
799 return gz;
800 }
801
WM_gizmomap_add_handlers(ARegion * region,wmGizmoMap * gzmap)802 void WM_gizmomap_add_handlers(ARegion *region, wmGizmoMap *gzmap)
803 {
804 LISTBASE_FOREACH (wmEventHandler *, handler_base, ®ion->handlers) {
805 if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
806 wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
807 if (handler->gizmo_map == gzmap) {
808 return;
809 }
810 }
811 }
812
813 wmEventHandler_Gizmo *handler = MEM_callocN(sizeof(*handler), __func__);
814 handler->head.type = WM_HANDLER_TYPE_GIZMO;
815 BLI_assert(gzmap == region->gizmo_map);
816 handler->gizmo_map = gzmap;
817 BLI_addtail(®ion->handlers, handler);
818 }
819
wm_gizmomaps_handled_modal_update(bContext * C,wmEvent * event,wmEventHandler_Op * handler)820 void wm_gizmomaps_handled_modal_update(bContext *C, wmEvent *event, wmEventHandler_Op *handler)
821 {
822 const bool modal_running = (handler->op != NULL);
823
824 /* happens on render or when joining areas */
825 if (!handler->context.region || !handler->context.region->gizmo_map) {
826 return;
827 }
828
829 wmGizmoMap *gzmap = handler->context.region->gizmo_map;
830 wmGizmo *gz = wm_gizmomap_modal_get(gzmap);
831 ScrArea *area = CTX_wm_area(C);
832 ARegion *region = CTX_wm_region(C);
833
834 wm_gizmomap_handler_context_op(C, handler);
835
836 /* regular update for running operator */
837 if (modal_running) {
838 wmGizmoOpElem *gzop = gz ? WM_gizmo_operator_get(gz, gz->highlight_part) : NULL;
839 if (gz && gzop && (gzop->type != NULL) && (gzop->type == handler->op->type)) {
840 wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
841 if (modal_fn != NULL) {
842 int retval = modal_fn(C, gz, event, 0);
843 /* The gizmo is tried to the operator, we can't choose when to exit. */
844 BLI_assert(retval & OPERATOR_RUNNING_MODAL);
845 UNUSED_VARS_NDEBUG(retval);
846 }
847 }
848 }
849 /* operator not running anymore */
850 else {
851 wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
852 if (gz) {
853 /* This isn't defined if it ends because of success of cancel, we may want to change. */
854 bool cancel = true;
855 if (gz->type->exit) {
856 gz->type->exit(C, gz, cancel);
857 }
858 wm_gizmomap_modal_set(gzmap, C, gz, NULL, false);
859 }
860 }
861
862 /* restore the area */
863 CTX_wm_area_set(C, area);
864 CTX_wm_region_set(C, region);
865 }
866
867 /**
868 * Deselect all selected gizmos in \a gzmap.
869 * \return if selection has changed.
870 */
wm_gizmomap_deselect_all(wmGizmoMap * gzmap)871 bool wm_gizmomap_deselect_all(wmGizmoMap *gzmap)
872 {
873 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
874
875 if (msel->items == NULL || msel->len == 0) {
876 return false;
877 }
878
879 for (int i = 0; i < msel->len; i++) {
880 wm_gizmo_select_set_ex(gzmap, msel->items[i], false, false, true);
881 }
882
883 wm_gizmomap_select_array_clear(gzmap);
884
885 /* always return true, we already checked
886 * if there's anything to deselect */
887 return true;
888 }
889
gizmo_selectable_poll(const wmGizmo * gz,void * UNUSED (data))890 BLI_INLINE bool gizmo_selectable_poll(const wmGizmo *gz, void *UNUSED(data))
891 {
892 return (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SELECT);
893 }
894
895 /**
896 * Select all selectable gizmos in \a gzmap.
897 * \return if selection has changed.
898 */
wm_gizmomap_select_all_intern(bContext * C,wmGizmoMap * gzmap)899 static bool wm_gizmomap_select_all_intern(bContext *C, wmGizmoMap *gzmap)
900 {
901 wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
902 /* GHash is used here to avoid having to loop over all gizmos twice (once to
903 * get tot_sel for allocating, once for actually selecting). Instead we collect
904 * selectable gizmos in hash table and use this to get tot_sel and do selection */
905
906 GHash *hash = WM_gizmomap_gizmo_hash_new(
907 C, gzmap, gizmo_selectable_poll, NULL, WM_GIZMO_HIDDEN | WM_GIZMO_HIDDEN_SELECT);
908 GHashIterator gh_iter;
909 int i;
910 bool changed = false;
911
912 wm_gizmomap_select_array_ensure_len_alloc(gzmap, BLI_ghash_len(hash));
913
914 GHASH_ITER_INDEX (gh_iter, hash, i) {
915 wmGizmo *gz_iter = BLI_ghashIterator_getValue(&gh_iter);
916 WM_gizmo_select_set(gzmap, gz_iter, true);
917 }
918 /* highlight first gizmo */
919 wm_gizmomap_highlight_set(gzmap, C, msel->items[0], msel->items[0]->highlight_part);
920
921 BLI_assert(BLI_ghash_len(hash) == msel->len);
922
923 BLI_ghash_free(hash, NULL, NULL);
924 return changed;
925 }
926
927 /**
928 * Select/Deselect all selectable gizmos in \a gzmap.
929 * \return if selection has changed.
930 *
931 * TODO select all by type
932 */
WM_gizmomap_select_all(bContext * C,wmGizmoMap * gzmap,const int action)933 bool WM_gizmomap_select_all(bContext *C, wmGizmoMap *gzmap, const int action)
934 {
935 bool changed = false;
936
937 switch (action) {
938 case SEL_SELECT:
939 changed = wm_gizmomap_select_all_intern(C, gzmap);
940 break;
941 case SEL_DESELECT:
942 changed = wm_gizmomap_deselect_all(gzmap);
943 break;
944 default:
945 BLI_assert(0);
946 break;
947 }
948
949 if (changed) {
950 WM_event_add_mousemove(CTX_wm_window(C));
951 }
952
953 return changed;
954 }
955
956 /**
957 * Prepare context for gizmo handling (but only if area/region is
958 * part of screen). Version of #wm_handler_op_context for gizmos.
959 */
wm_gizmomap_handler_context_op(bContext * C,wmEventHandler_Op * handler)960 void wm_gizmomap_handler_context_op(bContext *C, wmEventHandler_Op *handler)
961 {
962 bScreen *screen = CTX_wm_screen(C);
963
964 if (screen) {
965 ScrArea *area;
966
967 for (area = screen->areabase.first; area; area = area->next) {
968 if (area == handler->context.area) {
969 break;
970 }
971 }
972 if (area == NULL) {
973 /* when changing screen layouts with running modal handlers (like render display), this
974 * is not an error to print */
975 printf("internal error: modal gizmo-map handler has invalid area\n");
976 }
977 else {
978 ARegion *region;
979 CTX_wm_area_set(C, area);
980 for (region = area->regionbase.first; region; region = region->next) {
981 if (region == handler->context.region) {
982 break;
983 }
984 }
985 /* XXX no warning print here, after full-area and back regions are remade */
986 if (region) {
987 CTX_wm_region_set(C, region);
988 }
989 }
990 }
991 }
992
wm_gizmomap_handler_context_gizmo(bContext * UNUSED (C),wmEventHandler_Gizmo * UNUSED (handler))993 void wm_gizmomap_handler_context_gizmo(bContext *UNUSED(C), wmEventHandler_Gizmo *UNUSED(handler))
994 {
995 /* pass */
996 }
997
WM_gizmomap_cursor_set(const wmGizmoMap * gzmap,wmWindow * win)998 bool WM_gizmomap_cursor_set(const wmGizmoMap *gzmap, wmWindow *win)
999 {
1000 wmGizmo *gz = gzmap->gzmap_context.highlight;
1001 if (gz && gz->type->cursor_get) {
1002 WM_cursor_set(win, gz->type->cursor_get(gz));
1003 return true;
1004 }
1005
1006 return false;
1007 }
1008
wm_gizmomap_highlight_set(wmGizmoMap * gzmap,const bContext * C,wmGizmo * gz,int part)1009 bool wm_gizmomap_highlight_set(wmGizmoMap *gzmap, const bContext *C, wmGizmo *gz, int part)
1010 {
1011 if ((gz != gzmap->gzmap_context.highlight) || (gz && part != gz->highlight_part)) {
1012 const bool init_last_cursor = !(gzmap->gzmap_context.highlight &&
1013 gzmap->gzmap_context.last_cursor != -1);
1014 if (gzmap->gzmap_context.highlight) {
1015 gzmap->gzmap_context.highlight->state &= ~WM_GIZMO_STATE_HIGHLIGHT;
1016 gzmap->gzmap_context.highlight->highlight_part = -1;
1017 }
1018
1019 gzmap->gzmap_context.highlight = gz;
1020
1021 if (gz) {
1022 gz->state |= WM_GIZMO_STATE_HIGHLIGHT;
1023 gz->highlight_part = part;
1024 if (init_last_cursor) {
1025 gzmap->gzmap_context.last_cursor = -1;
1026 }
1027
1028 if (C && gz->type->cursor_get) {
1029 wmWindow *win = CTX_wm_window(C);
1030 if (init_last_cursor) {
1031 gzmap->gzmap_context.last_cursor = win->cursor;
1032 }
1033 WM_cursor_set(win, gz->type->cursor_get(gz));
1034 }
1035 }
1036 else {
1037 if (C && gzmap->gzmap_context.last_cursor != -1) {
1038 wmWindow *win = CTX_wm_window(C);
1039 WM_cursor_set(win, gzmap->gzmap_context.last_cursor);
1040 }
1041 gzmap->gzmap_context.last_cursor = -1;
1042 }
1043
1044 /* tag the region for redraw */
1045 if (C) {
1046 ARegion *region = CTX_wm_region(C);
1047 ED_region_tag_redraw_editor_overlays(region);
1048 }
1049
1050 return true;
1051 }
1052
1053 return false;
1054 }
1055
wm_gizmomap_highlight_get(wmGizmoMap * gzmap)1056 wmGizmo *wm_gizmomap_highlight_get(wmGizmoMap *gzmap)
1057 {
1058 return gzmap->gzmap_context.highlight;
1059 }
1060
1061 /**
1062 * Caller should call exit when (enable == False).
1063 */
wm_gizmomap_modal_set(wmGizmoMap * gzmap,bContext * C,wmGizmo * gz,const wmEvent * event,bool enable)1064 void wm_gizmomap_modal_set(
1065 wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, const wmEvent *event, bool enable)
1066 {
1067 if (enable) {
1068 BLI_assert(gzmap->gzmap_context.modal == NULL);
1069 wmWindow *win = CTX_wm_window(C);
1070
1071 WM_tooltip_clear(C, win);
1072
1073 /* Use even if we don't have invoke, so we can setup data before an operator runs. */
1074 if (gz->parent_gzgroup->type->invoke_prepare) {
1075 gz->parent_gzgroup->type->invoke_prepare(C, gz->parent_gzgroup, gz, event);
1076 }
1077
1078 if (gz->type->invoke && (gz->type->modal || gz->custom_modal)) {
1079 const int retval = gz->type->invoke(C, gz, event);
1080 if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
1081 return;
1082 }
1083 }
1084
1085 gz->state |= WM_GIZMO_STATE_MODAL;
1086 gzmap->gzmap_context.modal = gz;
1087
1088 if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) {
1089 WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL);
1090 copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x);
1091 gzmap->gzmap_context.event_grabcursor = win->grabcursor;
1092 }
1093 else {
1094 gzmap->gzmap_context.event_xy[0] = INT_MAX;
1095 }
1096
1097 struct wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, gz->highlight_part);
1098 if (gzop && gzop->type) {
1099 const int retval = WM_gizmo_operator_invoke(C, gz, gzop);
1100 if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
1101 wm_gizmomap_modal_set(gzmap, C, gz, event, false);
1102 }
1103
1104 /* we failed to hook the gizmo to the operator handler or operator was cancelled, return */
1105 if (!gzmap->gzmap_context.modal) {
1106 gz->state &= ~WM_GIZMO_STATE_MODAL;
1107 MEM_SAFE_FREE(gz->interaction_data);
1108 }
1109 return;
1110 }
1111 }
1112 else {
1113 BLI_assert(ELEM(gzmap->gzmap_context.modal, NULL, gz));
1114
1115 /* deactivate, gizmo but first take care of some stuff */
1116 if (gz) {
1117 gz->state &= ~WM_GIZMO_STATE_MODAL;
1118 MEM_SAFE_FREE(gz->interaction_data);
1119 }
1120 gzmap->gzmap_context.modal = NULL;
1121
1122 if (C) {
1123 wmWindow *win = CTX_wm_window(C);
1124 if (gzmap->gzmap_context.event_xy[0] != INT_MAX) {
1125 /* Check if some other part of Blender (typically operators)
1126 * have adjusted the grab mode since it was set.
1127 * If so: warp, so we have a predictable outcome. */
1128 if (gzmap->gzmap_context.event_grabcursor == win->grabcursor) {
1129 WM_cursor_grab_disable(win, gzmap->gzmap_context.event_xy);
1130 }
1131 else {
1132 WM_cursor_warp(win, UNPACK2(gzmap->gzmap_context.event_xy));
1133 }
1134 }
1135 ED_region_tag_redraw_editor_overlays(CTX_wm_region(C));
1136 WM_event_add_mousemove(win);
1137 }
1138
1139 gzmap->gzmap_context.event_xy[0] = INT_MAX;
1140 }
1141 }
1142
wm_gizmomap_modal_get(wmGizmoMap * gzmap)1143 wmGizmo *wm_gizmomap_modal_get(wmGizmoMap *gzmap)
1144 {
1145 return gzmap->gzmap_context.modal;
1146 }
1147
wm_gizmomap_selected_get(wmGizmoMap * gzmap,int * r_selected_len)1148 wmGizmo **wm_gizmomap_selected_get(wmGizmoMap *gzmap, int *r_selected_len)
1149 {
1150 *r_selected_len = gzmap->gzmap_context.select.len;
1151 return gzmap->gzmap_context.select.items;
1152 }
1153
wm_gizmomap_groups_get(wmGizmoMap * gzmap)1154 ListBase *wm_gizmomap_groups_get(wmGizmoMap *gzmap)
1155 {
1156 return &gzmap->groups;
1157 }
1158
WM_gizmomap_message_subscribe(bContext * C,wmGizmoMap * gzmap,ARegion * region,struct wmMsgBus * mbus)1159 void WM_gizmomap_message_subscribe(bContext *C,
1160 wmGizmoMap *gzmap,
1161 ARegion *region,
1162 struct wmMsgBus *mbus)
1163 {
1164 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) {
1165 if ((gzgroup->hide.any != 0) || (gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0 ||
1166 !WM_gizmo_group_type_poll(C, gzgroup->type)) {
1167 continue;
1168 }
1169 LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
1170 if (gz->flag & WM_GIZMO_HIDDEN) {
1171 continue;
1172 }
1173 WM_gizmo_target_property_subscribe_all(gz, mbus, region);
1174 }
1175 if (gzgroup->type->message_subscribe != NULL) {
1176 gzgroup->type->message_subscribe(C, gzgroup, mbus);
1177 }
1178 }
1179 }
1180
1181 /** \} */ /* wmGizmoMap */
1182
1183 /* -------------------------------------------------------------------- */
1184 /** \name Tooltip Handling
1185 *
1186 * \{ */
1187
WM_gizmomap_tooltip_init(struct bContext * C,struct ARegion * region,int * UNUSED (r_pass),double * UNUSED (pass_delay),bool * r_exit_on_event)1188 struct ARegion *WM_gizmomap_tooltip_init(struct bContext *C,
1189 struct ARegion *region,
1190 int *UNUSED(r_pass),
1191 double *UNUSED(pass_delay),
1192 bool *r_exit_on_event)
1193 {
1194 wmGizmoMap *gzmap = region->gizmo_map;
1195 *r_exit_on_event = false;
1196 if (gzmap) {
1197 wmGizmo *gz = gzmap->gzmap_context.highlight;
1198 if (gz) {
1199 wmGizmoGroup *gzgroup = gz->parent_gzgroup;
1200 if ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0) {
1201 /* On screen area of 3D gizmos may be large, exit on cursor motion. */
1202 *r_exit_on_event = true;
1203 }
1204 return UI_tooltip_create_from_gizmo(C, gz);
1205 }
1206 }
1207 return NULL;
1208 }
1209
1210 /** \} */ /* wmGizmoMapType */
1211
1212 /* -------------------------------------------------------------------- */
1213 /** \name wmGizmoMapType
1214 *
1215 * \{ */
1216
WM_gizmomaptype_find(const struct wmGizmoMapType_Params * gzmap_params)1217 wmGizmoMapType *WM_gizmomaptype_find(const struct wmGizmoMapType_Params *gzmap_params)
1218 {
1219 LISTBASE_FOREACH (wmGizmoMapType *, gzmap_type, &gizmomaptypes) {
1220 if (gzmap_type->spaceid == gzmap_params->spaceid &&
1221 gzmap_type->regionid == gzmap_params->regionid) {
1222 return gzmap_type;
1223 }
1224 }
1225
1226 return NULL;
1227 }
1228
WM_gizmomaptype_ensure(const struct wmGizmoMapType_Params * gzmap_params)1229 wmGizmoMapType *WM_gizmomaptype_ensure(const struct wmGizmoMapType_Params *gzmap_params)
1230 {
1231 wmGizmoMapType *gzmap_type = WM_gizmomaptype_find(gzmap_params);
1232
1233 if (gzmap_type) {
1234 return gzmap_type;
1235 }
1236
1237 gzmap_type = MEM_callocN(sizeof(wmGizmoMapType), "gizmotype list");
1238 gzmap_type->spaceid = gzmap_params->spaceid;
1239 gzmap_type->regionid = gzmap_params->regionid;
1240 BLI_addhead(&gizmomaptypes, gzmap_type);
1241
1242 return gzmap_type;
1243 }
1244
wm_gizmomaptypes_free(void)1245 void wm_gizmomaptypes_free(void)
1246 {
1247 for (wmGizmoMapType *gzmap_type = gizmomaptypes.first, *gzmap_type_next; gzmap_type;
1248 gzmap_type = gzmap_type_next) {
1249 gzmap_type_next = gzmap_type->next;
1250 for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_next; gzgt_ref;
1251 gzgt_ref = gzgt_next) {
1252 gzgt_next = gzgt_ref->next;
1253 WM_gizmomaptype_group_free(gzgt_ref);
1254 }
1255 MEM_freeN(gzmap_type);
1256 }
1257 }
1258
1259 /**
1260 * Initialize keymaps for all existing gizmo-groups
1261 */
wm_gizmos_keymap(wmKeyConfig * keyconf)1262 void wm_gizmos_keymap(wmKeyConfig *keyconf)
1263 {
1264 /* we add this item-less keymap once and use it to group gizmo-group keymaps into it */
1265 WM_keymap_ensure(keyconf, "Gizmos", 0, 0);
1266
1267 LISTBASE_FOREACH (wmGizmoMapType *, gzmap_type, &gizmomaptypes) {
1268 LISTBASE_FOREACH (wmGizmoGroupTypeRef *, gzgt_ref, &gzmap_type->grouptype_refs) {
1269 wm_gizmogrouptype_setup_keymap(gzgt_ref->type, keyconf);
1270 }
1271 }
1272
1273 wm_gizmogroup_tweak_modal_keymap(keyconf);
1274 }
1275
1276 /** \} */ /* wmGizmoMapType */
1277
1278 /* -------------------------------------------------------------------- */
1279 /** \name Updates for Dynamic Type Registration
1280 *
1281 * \{ */
1282
WM_gizmoconfig_update_tag_group_type_init(wmGizmoMapType * gzmap_type,wmGizmoGroupType * gzgt)1283 void WM_gizmoconfig_update_tag_group_type_init(wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
1284 {
1285 /* tag for update on next use */
1286 gzmap_type->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
1287 gzgt->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
1288
1289 wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
1290 }
1291
WM_gizmoconfig_update_tag_group_type_remove(wmGizmoMapType * gzmap_type,wmGizmoGroupType * gzgt)1292 void WM_gizmoconfig_update_tag_group_type_remove(wmGizmoMapType *gzmap_type,
1293 wmGizmoGroupType *gzgt)
1294 {
1295 /* tag for update on next use */
1296 gzmap_type->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1297 gzgt->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1298
1299 wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
1300 }
1301
WM_gizmoconfig_update_tag_group_remove(wmGizmoMap * gzmap)1302 void WM_gizmoconfig_update_tag_group_remove(wmGizmoMap *gzmap)
1303 {
1304 gzmap->tag_remove_group = true;
1305
1306 wm_gzmap_type_update_flag |= WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE;
1307 }
1308
1309 /**
1310 * Run in case new types have been added (runs often, early exit where possible).
1311 * Follows #WM_keyconfig_update conventions.
1312 */
WM_gizmoconfig_update(struct Main * bmain)1313 void WM_gizmoconfig_update(struct Main *bmain)
1314 {
1315 if (G.background) {
1316 return;
1317 }
1318
1319 if (wm_gzmap_type_update_flag == 0) {
1320 return;
1321 }
1322
1323 if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1324 LISTBASE_FOREACH (wmGizmoMapType *, gzmap_type, &gizmomaptypes) {
1325 if (gzmap_type->type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
1326 gzmap_type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1327 for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_ref_next;
1328 gzgt_ref;
1329 gzgt_ref = gzgt_ref_next) {
1330 gzgt_ref_next = gzgt_ref->next;
1331 if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_REMOVE) {
1332 gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
1333 WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt_ref->type);
1334 }
1335 }
1336 }
1337 }
1338
1339 wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
1340 }
1341
1342 if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT) {
1343 LISTBASE_FOREACH (wmGizmoMapType *, gzmap_type, &gizmomaptypes) {
1344 const uchar type_update_all = WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT;
1345 if (gzmap_type->type_update_flag & type_update_all) {
1346 gzmap_type->type_update_flag &= ~type_update_all;
1347 LISTBASE_FOREACH (wmGizmoGroupTypeRef *, gzgt_ref, &gzmap_type->grouptype_refs) {
1348 if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_KEYMAP_INIT) {
1349 WM_gizmomaptype_group_init_runtime_keymap(bmain, gzgt_ref->type);
1350 gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_KEYMAP_INIT;
1351 }
1352
1353 if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_INIT) {
1354 WM_gizmomaptype_group_init_runtime(bmain, gzmap_type, gzgt_ref->type);
1355 gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_INIT;
1356 }
1357 }
1358 }
1359 }
1360
1361 wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
1362 }
1363
1364 if (wm_gzmap_type_update_flag & WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE) {
1365 for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
1366 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1367 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
1368 ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
1369 &sl->regionbase;
1370 LISTBASE_FOREACH (ARegion *, region, regionbase) {
1371 wmGizmoMap *gzmap = region->gizmo_map;
1372 if (gzmap != NULL && gzmap->tag_remove_group) {
1373 gzmap->tag_remove_group = false;
1374
1375 for (wmGizmoGroup *gzgroup = gzmap->groups.first, *gzgroup_next; gzgroup;
1376 gzgroup = gzgroup_next) {
1377 gzgroup_next = gzgroup->next;
1378 if (gzgroup->tag_remove) {
1379 wm_gizmogroup_free(NULL, gzgroup);
1380 ED_region_tag_redraw_editor_overlays(region);
1381 }
1382 }
1383 }
1384 }
1385 }
1386 }
1387 }
1388 wm_gzmap_type_update_flag &= ~WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE;
1389 }
1390 }
1391
1392 /** \} */
1393
1394 /* -------------------------------------------------------------------- */
1395 /** \name Recreate All Gizmos
1396 *
1397 * Use when adjusting themes.
1398 *
1399 * \{ */
1400
WM_reinit_gizmomap_all(Main * bmain)1401 void WM_reinit_gizmomap_all(Main *bmain)
1402 {
1403 for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
1404 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
1405 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
1406 ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
1407 LISTBASE_FOREACH (ARegion *, region, regionbase) {
1408 wmGizmoMap *gzmap = region->gizmo_map;
1409 if ((gzmap != NULL) && (gzmap->is_init == false)) {
1410 WM_gizmomap_reinit(gzmap);
1411 }
1412 }
1413 }
1414 }
1415 }
1416 }
1417
1418 /** \} */
1419