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, &region->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(&region->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