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 
17 /** \file
18  * \ingroup edtransform
19  *
20  * \name 2D Transform Gizmo
21  *
22  * Used for UV/Image Editor
23  */
24 
25 #include "MEM_guardedalloc.h"
26 
27 #include "BLI_math.h"
28 
29 #include "DNA_object_types.h"
30 #include "DNA_screen_types.h"
31 #include "DNA_space_types.h"
32 #include "DNA_view3d_types.h"
33 
34 #include "BKE_context.h"
35 #include "BKE_layer.h"
36 
37 #include "RNA_access.h"
38 
39 #include "UI_resources.h"
40 #include "UI_view2d.h"
41 
42 #include "WM_api.h"
43 #include "WM_message.h"
44 #include "WM_types.h"
45 
46 #include "ED_gizmo_library.h"
47 #include "ED_gizmo_utils.h"
48 #include "ED_image.h"
49 #include "ED_screen.h"
50 #include "ED_uvedit.h"
51 
52 #include "transform.h" /* own include */
53 
54 /* -------------------------------------------------------------------- */
55 /** \name Shared Callback's
56  */
57 
gizmo2d_generic_poll(const bContext * C,wmGizmoGroupType * gzgt)58 static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt)
59 {
60   if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
61     return false;
62   }
63 
64   if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) {
65     return false;
66   }
67 
68   ScrArea *area = CTX_wm_area(C);
69   switch (area->spacetype) {
70     case SPACE_IMAGE: {
71       SpaceImage *sima = area->spacedata.first;
72       Object *obedit = CTX_data_edit_object(C);
73       if (!ED_space_image_show_uvedit(sima, obedit)) {
74         return false;
75       }
76     }
77   }
78 
79   return true;
80 }
81 
gizmo2d_pivot_point_message_subscribe(struct wmGizmoGroup * gzgroup,struct wmMsgBus * mbus,bScreen * screen,ScrArea * area,ARegion * region)82 static void gizmo2d_pivot_point_message_subscribe(struct wmGizmoGroup *gzgroup,
83                                                   struct wmMsgBus *mbus,
84                                                   /* Additional args. */
85                                                   bScreen *screen,
86                                                   ScrArea *area,
87                                                   ARegion *region)
88 {
89   wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
90       .owner = region,
91       .user_data = gzgroup->parent_gzmap,
92       .notify = WM_gizmo_do_msg_notify_tag_refresh,
93   };
94 
95   switch (area->spacetype) {
96     case SPACE_IMAGE: {
97       SpaceImage *sima = area->spacedata.first;
98       PointerRNA ptr;
99       RNA_pointer_create(&screen->id, &RNA_SpaceImageEditor, sima, &ptr);
100       {
101         extern PropertyRNA rna_SpaceImageEditor_pivot_point;
102         extern PropertyRNA rna_SpaceImageEditor_cursor_location;
103         const PropertyRNA *props[] = {
104             &rna_SpaceImageEditor_pivot_point,
105             (sima->around == V3D_AROUND_CURSOR) ? &rna_SpaceImageEditor_cursor_location : NULL,
106         };
107         for (int i = 0; i < ARRAY_SIZE(props); i++) {
108           if (props[i] == NULL) {
109             continue;
110           }
111           WM_msg_subscribe_rna(mbus, &ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
112         }
113       }
114       break;
115     }
116   }
117 }
118 
119 /** \} */
120 
121 /* -------------------------------------------------------------------- */
122 /** \name Arrow / Cage Gizmo Group
123  *
124  * Defines public functions, not the gizmo itself:
125  *
126  * - #ED_widgetgroup_gizmo2d_xform_callbacks_set
127  * - #ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set
128  *
129  * \{ */
130 
131 /* axes as index */
132 enum {
133   MAN2D_AXIS_TRANS_X = 0,
134   MAN2D_AXIS_TRANS_Y,
135 
136   MAN2D_AXIS_LAST,
137 };
138 
139 typedef struct GizmoGroup2D {
140   wmGizmo *translate_xy[3];
141   wmGizmo *cage;
142 
143   /* Current origin in view space, used to update widget origin for possible view changes */
144   float origin[2];
145   float min[2];
146   float max[2];
147 
148   bool no_cage;
149 
150 } GizmoGroup2D;
151 
152 /* **************** Utilities **************** */
153 
gizmo2d_get_axis_color(const int axis_idx,float * r_col,float * r_col_hi)154 static void gizmo2d_get_axis_color(const int axis_idx, float *r_col, float *r_col_hi)
155 {
156   const float alpha = 0.6f;
157   const float alpha_hi = 1.0f;
158   int col_id;
159 
160   switch (axis_idx) {
161     case MAN2D_AXIS_TRANS_X:
162       col_id = TH_AXIS_X;
163       break;
164     case MAN2D_AXIS_TRANS_Y:
165       col_id = TH_AXIS_Y;
166       break;
167     default:
168       BLI_assert(0);
169       col_id = TH_AXIS_Y;
170       break;
171   }
172 
173   UI_GetThemeColor4fv(col_id, r_col);
174 
175   copy_v4_v4(r_col_hi, r_col);
176   r_col[3] *= alpha;
177   r_col_hi[3] *= alpha_hi;
178 }
179 
gizmogroup2d_init(wmGizmoGroup * gzgroup)180 static GizmoGroup2D *gizmogroup2d_init(wmGizmoGroup *gzgroup)
181 {
182   const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
183   const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_2d", true);
184   const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
185 
186   GizmoGroup2D *ggd = MEM_callocN(sizeof(GizmoGroup2D), __func__);
187 
188   ggd->translate_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
189   ggd->translate_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
190   ggd->translate_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
191   ggd->cage = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL);
192 
193   RNA_enum_set(ggd->cage->ptr,
194                "transform",
195                ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE |
196                    ED_GIZMO_CAGE2D_XFORM_FLAG_ROTATE);
197 
198   return ggd;
199 }
200 
201 /**
202  * Calculates origin in view space, use with #gizmo2d_origin_to_region.
203  */
gizmo2d_calc_bounds(const bContext * C,float * r_center,float * r_min,float * r_max)204 static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max)
205 {
206   float min_buf[2], max_buf[2];
207   if (r_min == NULL) {
208     r_min = min_buf;
209   }
210   if (r_max == NULL) {
211     r_max = max_buf;
212   }
213 
214   ScrArea *area = CTX_wm_area(C);
215   bool changed = false;
216   if (area->spacetype == SPACE_IMAGE) {
217     Scene *scene = CTX_data_scene(C);
218     ViewLayer *view_layer = CTX_data_view_layer(C);
219     uint objects_len = 0;
220     Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
221         view_layer, NULL, &objects_len);
222     if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) {
223       changed = true;
224     }
225     MEM_freeN(objects);
226   }
227 
228   if (changed == false) {
229     zero_v2(r_min);
230     zero_v2(r_max);
231   }
232 
233   mid_v2_v2v2(r_center, r_min, r_max);
234   return changed;
235 }
236 
gizmo2d_calc_center(const bContext * C,float r_center[2])237 static bool gizmo2d_calc_center(const bContext *C, float r_center[2])
238 {
239   ScrArea *area = CTX_wm_area(C);
240   bool has_select = false;
241   zero_v2(r_center);
242   if (area->spacetype == SPACE_IMAGE) {
243     SpaceImage *sima = area->spacedata.first;
244     Scene *scene = CTX_data_scene(C);
245     ViewLayer *view_layer = CTX_data_view_layer(C);
246     ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select);
247   }
248   return has_select;
249 }
250 
251 /**
252  * Convert origin (or any other point) from view to region space.
253  */
gizmo2d_origin_to_region(ARegion * region,float * r_origin)254 BLI_INLINE void gizmo2d_origin_to_region(ARegion *region, float *r_origin)
255 {
256   UI_view2d_view_to_region_fl(&region->v2d, r_origin[0], r_origin[1], &r_origin[0], &r_origin[1]);
257 }
258 
259 /**
260  * Custom handler for gizmo widgets
261  */
gizmo2d_modal(bContext * C,wmGizmo * widget,const wmEvent * UNUSED (event),eWM_GizmoFlagTweak UNUSED (tweak_flag))262 static int gizmo2d_modal(bContext *C,
263                          wmGizmo *widget,
264                          const wmEvent *UNUSED(event),
265                          eWM_GizmoFlagTweak UNUSED(tweak_flag))
266 {
267   ARegion *region = CTX_wm_region(C);
268   float origin[3];
269 
270   gizmo2d_calc_center(C, origin);
271   gizmo2d_origin_to_region(region, origin);
272   WM_gizmo_set_matrix_location(widget, origin);
273 
274   ED_region_tag_redraw_editor_overlays(region);
275 
276   return OPERATOR_RUNNING_MODAL;
277 }
278 
gizmo2d_xform_setup(const bContext * UNUSED (C),wmGizmoGroup * gzgroup)279 static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
280 {
281   wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
282   GizmoGroup2D *ggd = gizmogroup2d_init(gzgroup);
283   gzgroup->customdata = ggd;
284 
285   for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
286     wmGizmo *gz = ggd->translate_xy[i];
287 
288     /* custom handler! */
289     WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
290 
291     if (i < 2) {
292       float color[4], color_hi[4];
293       gizmo2d_get_axis_color(i, color, color_hi);
294 
295       /* set up widget data */
296       RNA_float_set(gz->ptr, "length", 0.8f);
297       float axis[3] = {0.0f};
298       axis[i] = 1.0f;
299       WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis);
300 
301       float offset[3] = {0, 0, 0};
302       offset[2] = 0.18f;
303       WM_gizmo_set_matrix_offset_location(gz, offset);
304       gz->flag |= WM_GIZMO_DRAW_OFFSET_SCALE;
305 
306       WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH);
307       WM_gizmo_set_color(gz, color);
308       WM_gizmo_set_color_highlight(gz, color_hi);
309 
310       WM_gizmo_set_scale(gz, 1.0f);
311     }
312     else {
313       float color[4], color_hi[4];
314       UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
315       copy_v4_v4(color_hi, color);
316       color[3] *= 0.6f;
317 
318       PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
319       RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
320 
321       RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
322       /* Make the center low alpha. */
323       WM_gizmo_set_line_width(gz, 2.0f);
324       RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
325       WM_gizmo_set_color(gz, color);
326       WM_gizmo_set_color_highlight(gz, color_hi);
327 
328       WM_gizmo_set_scale(gz, 0.2f);
329     }
330 
331     /* Assign operator. */
332     PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_translate, NULL);
333     if (i < 2) {
334       bool constraint[3] = {false};
335       constraint[i] = true;
336       if (RNA_struct_find_property(ptr, "constraint_axis")) {
337         RNA_boolean_set_array(ptr, "constraint_axis", constraint);
338       }
339     }
340 
341     RNA_boolean_set(ptr, "release_confirm", 1);
342   }
343 
344   {
345     wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
346     wmOperatorType *ot_rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
347     PointerRNA *ptr;
348 
349     /* assign operator */
350     ptr = WM_gizmo_operator_set(ggd->cage, 0, ot_translate, NULL);
351     RNA_boolean_set(ptr, "release_confirm", 1);
352 
353     const bool constraint_x[3] = {1, 0, 0};
354     const bool constraint_y[3] = {0, 1, 0};
355 
356     ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL);
357     PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
358     PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
359     RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
360     RNA_property_boolean_set(ptr, prop_release_confirm, true);
361     ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL);
362     RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
363     RNA_property_boolean_set(ptr, prop_release_confirm, true);
364     ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL);
365     RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
366     RNA_property_boolean_set(ptr, prop_release_confirm, true);
367     ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL);
368     RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
369     RNA_property_boolean_set(ptr, prop_release_confirm, true);
370 
371     ptr = WM_gizmo_operator_set(
372         ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL);
373     RNA_property_boolean_set(ptr, prop_release_confirm, true);
374     ptr = WM_gizmo_operator_set(
375         ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y, ot_resize, NULL);
376     RNA_property_boolean_set(ptr, prop_release_confirm, true);
377     ptr = WM_gizmo_operator_set(
378         ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y, ot_resize, NULL);
379     RNA_property_boolean_set(ptr, prop_release_confirm, true);
380     ptr = WM_gizmo_operator_set(
381         ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y, ot_resize, NULL);
382     RNA_property_boolean_set(ptr, prop_release_confirm, true);
383     ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE, ot_rotate, NULL);
384     RNA_property_boolean_set(ptr, prop_release_confirm, true);
385   }
386 }
387 
gizmo2d_xform_setup_no_cage(const bContext * C,wmGizmoGroup * gzgroup)388 static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup)
389 {
390   gizmo2d_xform_setup(C, gzgroup);
391   GizmoGroup2D *ggd = gzgroup->customdata;
392   ggd->no_cage = true;
393 }
394 
gizmo2d_xform_refresh(const bContext * C,wmGizmoGroup * gzgroup)395 static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup)
396 {
397   GizmoGroup2D *ggd = gzgroup->customdata;
398   float origin[3];
399   bool has_select;
400   if (ggd->no_cage) {
401     has_select = gizmo2d_calc_center(C, origin);
402   }
403   else {
404     has_select = gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max);
405   }
406   copy_v2_v2(ggd->origin, origin);
407   bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max);
408 
409   if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) {
410     Scene *scene = CTX_data_scene(C);
411     if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) {
412       gzgroup->use_fallback_keymap = true;
413     }
414     else {
415       gzgroup->use_fallback_keymap = false;
416     }
417   }
418 
419   if (has_select == false) {
420     for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
421       ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN;
422     }
423     ggd->cage->flag |= WM_GIZMO_HIDDEN;
424   }
425   else {
426     if (show_cage) {
427       ggd->cage->flag &= ~WM_GIZMO_HIDDEN;
428       for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
429         wmGizmo *gz = ggd->translate_xy[i];
430         gz->flag |= WM_GIZMO_HIDDEN;
431       }
432     }
433     else {
434       ggd->cage->flag |= WM_GIZMO_HIDDEN;
435       for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
436         wmGizmo *gz = ggd->translate_xy[i];
437         gz->flag &= ~WM_GIZMO_HIDDEN;
438       }
439     }
440 
441     if (show_cage) {
442       wmGizmoOpElem *gzop;
443       float mid[2];
444       const float *min = ggd->min;
445       const float *max = ggd->max;
446       mid_v2_v2v2(mid, min, max);
447 
448       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X);
449       PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override");
450       RNA_property_float_set_array(
451           &gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f});
452       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X);
453       RNA_property_float_set_array(
454           &gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f});
455       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y);
456       RNA_property_float_set_array(
457           &gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f});
458       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y);
459       RNA_property_float_set_array(
460           &gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f});
461 
462       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y);
463       RNA_property_float_set_array(
464           &gzop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f});
465       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y);
466       RNA_property_float_set_array(
467           &gzop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f});
468       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y);
469       RNA_property_float_set_array(
470           &gzop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f});
471       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y);
472       RNA_property_float_set_array(
473           &gzop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f});
474 
475       gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE);
476       RNA_property_float_set_array(
477           &gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f});
478     }
479   }
480 }
481 
gizmo2d_xform_draw_prepare(const bContext * C,wmGizmoGroup * gzgroup)482 static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
483 {
484   ARegion *region = CTX_wm_region(C);
485   GizmoGroup2D *ggd = gzgroup->customdata;
486   float origin[3] = {UNPACK2(ggd->origin), 0.0f};
487   const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f};
488 
489   gizmo2d_origin_to_region(region, origin);
490 
491   for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) {
492     wmGizmo *gz = ggd->translate_xy[i];
493     WM_gizmo_set_matrix_location(gz, origin);
494   }
495 
496   UI_view2d_view_to_region_m4(&region->v2d, ggd->cage->matrix_space);
497   WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa);
498   ggd->cage->matrix_offset[0][0] = (ggd->max[0] - ggd->min[0]);
499   ggd->cage->matrix_offset[1][1] = (ggd->max[1] - ggd->min[1]);
500 }
501 
gizmo2d_xform_no_cage_message_subscribe(const struct bContext * C,struct wmGizmoGroup * gzgroup,struct wmMsgBus * mbus)502 static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C,
503                                                     struct wmGizmoGroup *gzgroup,
504                                                     struct wmMsgBus *mbus)
505 {
506   bScreen *screen = CTX_wm_screen(C);
507   ScrArea *area = CTX_wm_area(C);
508   ARegion *region = CTX_wm_region(C);
509   gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
510 }
511 
ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType * gzgt)512 void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt)
513 {
514   gzgt->poll = gizmo2d_generic_poll;
515   gzgt->setup = gizmo2d_xform_setup;
516   gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
517   gzgt->refresh = gizmo2d_xform_refresh;
518   gzgt->draw_prepare = gizmo2d_xform_draw_prepare;
519 }
520 
ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType * gzgt)521 void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt)
522 {
523   ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt);
524   gzgt->setup = gizmo2d_xform_setup_no_cage;
525   gzgt->message_subscribe = gizmo2d_xform_no_cage_message_subscribe;
526 }
527 
528 /** \} */
529 
530 /* -------------------------------------------------------------------- */
531 /** \name Scale Handles
532  *
533  * Defines public functions, not the gizmo itself:
534  *
535  * - #ED_widgetgroup_gizmo2d_resize_callbacks_set
536  *
537  * \{ */
538 
539 typedef struct GizmoGroup_Resize2D {
540   wmGizmo *gizmo_xy[3];
541   float origin[2];
542 } GizmoGroup_Resize2D;
543 
gizmogroup2d_resize_init(wmGizmoGroup * gzgroup)544 static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup)
545 {
546   const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
547   const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
548 
549   GizmoGroup_Resize2D *ggd = MEM_callocN(sizeof(GizmoGroup_Resize2D), __func__);
550 
551   ggd->gizmo_xy[0] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
552   ggd->gizmo_xy[1] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
553   ggd->gizmo_xy[2] = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
554 
555   return ggd;
556 }
557 
gizmo2d_resize_refresh(const bContext * C,wmGizmoGroup * gzgroup)558 static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup)
559 {
560   GizmoGroup_Resize2D *ggd = gzgroup->customdata;
561   float origin[3];
562   const bool has_select = gizmo2d_calc_center(C, origin);
563 
564   if (has_select == false) {
565     for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
566       ggd->gizmo_xy[i]->flag |= WM_GIZMO_HIDDEN;
567     }
568   }
569   else {
570     for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
571       ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN;
572     }
573     copy_v2_v2(ggd->origin, origin);
574   }
575 }
576 
gizmo2d_resize_draw_prepare(const bContext * C,wmGizmoGroup * gzgroup)577 static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
578 {
579   ARegion *region = CTX_wm_region(C);
580   GizmoGroup_Resize2D *ggd = gzgroup->customdata;
581   float origin[3] = {UNPACK2(ggd->origin), 0.0f};
582 
583   if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) {
584     Scene *scene = CTX_data_scene(C);
585     if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) {
586       gzgroup->use_fallback_keymap = true;
587     }
588     else {
589       gzgroup->use_fallback_keymap = false;
590     }
591   }
592 
593   gizmo2d_origin_to_region(region, origin);
594 
595   for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
596     wmGizmo *gz = ggd->gizmo_xy[i];
597     WM_gizmo_set_matrix_location(gz, origin);
598   }
599 }
600 
gizmo2d_resize_setup(const bContext * UNUSED (C),wmGizmoGroup * gzgroup)601 static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
602 {
603 
604   wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
605   GizmoGroup_Resize2D *ggd = gizmogroup2d_resize_init(gzgroup);
606   gzgroup->customdata = ggd;
607 
608   for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) {
609     wmGizmo *gz = ggd->gizmo_xy[i];
610 
611     /* custom handler! */
612     WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
613 
614     if (i < 2) {
615       float color[4], color_hi[4];
616       gizmo2d_get_axis_color(i, color, color_hi);
617 
618       /* set up widget data */
619       RNA_float_set(gz->ptr, "length", 1.0f);
620       float axis[3] = {0.0f};
621       axis[(i + 1) % 2] = 1.0f;
622       WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis);
623 
624       RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
625 
626       WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH);
627       WM_gizmo_set_color(gz, color);
628       WM_gizmo_set_color_highlight(gz, color_hi);
629 
630       WM_gizmo_set_scale(gz, 1.0f);
631     }
632     else {
633       float color[4], color_hi[4];
634       UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
635       copy_v4_v4(color_hi, color);
636       color[3] *= 0.6f;
637 
638       PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
639       RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
640 
641       RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
642       /* Make the center low alpha. */
643       WM_gizmo_set_line_width(gz, 2.0f);
644       RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
645       WM_gizmo_set_color(gz, color);
646       WM_gizmo_set_color_highlight(gz, color_hi);
647 
648       WM_gizmo_set_scale(gz, 1.2f);
649     }
650 
651     /* Assign operator. */
652     PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL);
653     if (i < 2) {
654       bool constraint[3] = {0};
655       constraint[(i + 1) % 2] = 1;
656       if (RNA_struct_find_property(ptr, "constraint_axis")) {
657         RNA_boolean_set_array(ptr, "constraint_axis", constraint);
658       }
659     }
660     RNA_boolean_set(ptr, "release_confirm", true);
661   }
662 }
663 
gizmo2d_resize_message_subscribe(const struct bContext * C,struct wmGizmoGroup * gzgroup,struct wmMsgBus * mbus)664 static void gizmo2d_resize_message_subscribe(const struct bContext *C,
665                                              struct wmGizmoGroup *gzgroup,
666                                              struct wmMsgBus *mbus)
667 {
668   bScreen *screen = CTX_wm_screen(C);
669   ScrArea *area = CTX_wm_area(C);
670   ARegion *region = CTX_wm_region(C);
671   gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
672 }
673 
ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType * gzgt)674 void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt)
675 {
676   gzgt->poll = gizmo2d_generic_poll;
677   gzgt->setup = gizmo2d_resize_setup;
678   gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
679   gzgt->refresh = gizmo2d_resize_refresh;
680   gzgt->draw_prepare = gizmo2d_resize_draw_prepare;
681   gzgt->message_subscribe = gizmo2d_resize_message_subscribe;
682 }
683 
684 /** \} */
685 
686 /* -------------------------------------------------------------------- */
687 /** \name Rotate Handles
688  *
689  * Defines public functions, not the gizmo itself:
690  *
691  * - #ED_widgetgroup_gizmo2d_rotate_setup
692  *
693  * \{ */
694 
695 typedef struct GizmoGroup_Rotate2D {
696   wmGizmo *gizmo;
697   float origin[2];
698 } GizmoGroup_Rotate2D;
699 
gizmogroup2d_rotate_init(wmGizmoGroup * gzgroup)700 static GizmoGroup_Rotate2D *gizmogroup2d_rotate_init(wmGizmoGroup *gzgroup)
701 {
702   const wmGizmoType *gzt_button = WM_gizmotype_find("GIZMO_GT_button_2d", true);
703 
704   GizmoGroup_Rotate2D *ggd = MEM_callocN(sizeof(GizmoGroup_Rotate2D), __func__);
705 
706   ggd->gizmo = WM_gizmo_new_ptr(gzt_button, gzgroup, NULL);
707 
708   return ggd;
709 }
710 
gizmo2d_rotate_refresh(const bContext * C,wmGizmoGroup * gzgroup)711 static void gizmo2d_rotate_refresh(const bContext *C, wmGizmoGroup *gzgroup)
712 {
713   GizmoGroup_Rotate2D *ggd = gzgroup->customdata;
714   float origin[3];
715   const bool has_select = gizmo2d_calc_center(C, origin);
716 
717   if (has_select == false) {
718     ggd->gizmo->flag |= WM_GIZMO_HIDDEN;
719   }
720   else {
721     ggd->gizmo->flag &= ~WM_GIZMO_HIDDEN;
722     copy_v2_v2(ggd->origin, origin);
723   }
724 }
725 
gizmo2d_rotate_draw_prepare(const bContext * C,wmGizmoGroup * gzgroup)726 static void gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
727 {
728   ARegion *region = CTX_wm_region(C);
729   GizmoGroup_Rotate2D *ggd = gzgroup->customdata;
730   float origin[3] = {UNPACK2(ggd->origin), 0.0f};
731 
732   if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) {
733     Scene *scene = CTX_data_scene(C);
734     if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) {
735       gzgroup->use_fallback_keymap = true;
736     }
737     else {
738       gzgroup->use_fallback_keymap = false;
739     }
740   }
741 
742   gizmo2d_origin_to_region(region, origin);
743 
744   wmGizmo *gz = ggd->gizmo;
745   WM_gizmo_set_matrix_location(gz, origin);
746 }
747 
gizmo2d_rotate_setup(const bContext * UNUSED (C),wmGizmoGroup * gzgroup)748 static void gizmo2d_rotate_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
749 {
750 
751   wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_rotate", true);
752   GizmoGroup_Rotate2D *ggd = gizmogroup2d_rotate_init(gzgroup);
753   gzgroup->customdata = ggd;
754 
755   /* Other setup functions iterate over axis. */
756   {
757     wmGizmo *gz = ggd->gizmo;
758 
759     /* custom handler! */
760     WM_gizmo_set_fn_custom_modal(gz, gizmo2d_modal);
761     WM_gizmo_set_scale(gz, 1.2f);
762 
763     {
764       float color[4];
765       UI_GetThemeColor4fv(TH_GIZMO_VIEW_ALIGN, color);
766 
767       PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
768       RNA_property_enum_set(gz->ptr, prop, ICON_NONE);
769 
770       RNA_enum_set(gz->ptr, "draw_options", ED_GIZMO_BUTTON_SHOW_BACKDROP);
771       /* Make the center low alpha. */
772       WM_gizmo_set_line_width(gz, 2.0f);
773       RNA_float_set(gz->ptr, "backdrop_fill_alpha", 0.0);
774       WM_gizmo_set_color(gz, color);
775       WM_gizmo_set_color_highlight(gz, color);
776     }
777 
778     /* Assign operator. */
779     PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_resize, NULL);
780     RNA_boolean_set(ptr, "release_confirm", true);
781   }
782 }
783 
gizmo2d_rotate_message_subscribe(const struct bContext * C,struct wmGizmoGroup * gzgroup,struct wmMsgBus * mbus)784 static void gizmo2d_rotate_message_subscribe(const struct bContext *C,
785                                              struct wmGizmoGroup *gzgroup,
786                                              struct wmMsgBus *mbus)
787 {
788   bScreen *screen = CTX_wm_screen(C);
789   ScrArea *area = CTX_wm_area(C);
790   ARegion *region = CTX_wm_region(C);
791   gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region);
792 }
793 
ED_widgetgroup_gizmo2d_rotate_callbacks_set(wmGizmoGroupType * gzgt)794 void ED_widgetgroup_gizmo2d_rotate_callbacks_set(wmGizmoGroupType *gzgt)
795 {
796   gzgt->poll = gizmo2d_generic_poll;
797   gzgt->setup = gizmo2d_rotate_setup;
798   gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
799   gzgt->refresh = gizmo2d_rotate_refresh;
800   gzgt->draw_prepare = gizmo2d_rotate_draw_prepare;
801   gzgt->message_subscribe = gizmo2d_rotate_message_subscribe;
802 }
803 
804 /** \} */
805