Blender  V2.93
view3d_gizmo_preselect_type.c
Go to the documentation of this file.
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 
27 #include "MEM_guardedalloc.h"
28 
29 #include "BLI_math.h"
30 
31 #include "DNA_mesh_types.h"
32 
33 #include "BKE_context.h"
34 #include "BKE_editmesh.h"
35 #include "BKE_layer.h"
36 
37 #include "DEG_depsgraph.h"
38 #include "DEG_depsgraph_query.h"
39 
40 #include "RNA_access.h"
41 #include "RNA_define.h"
42 
43 #include "WM_api.h"
44 #include "WM_types.h"
45 
46 #include "bmesh.h"
47 
48 #include "ED_gizmo_library.h"
49 #include "ED_mesh.h"
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52 
53 /* -------------------------------------------------------------------- */
57 typedef struct MeshElemGizmo3D {
67 
69 {
70  MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
71  if (gz_ele->base_index != -1) {
72  Object *ob = gz_ele->bases[gz_ele->base_index]->object;
73  EDBM_preselect_elem_draw(gz_ele->psel, ob->obmat);
74  }
75 }
76 
77 static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
78 {
79  wmEvent *event = CTX_wm_window(C)->eventstate;
80  MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
81 
82  /* Hack: Switch action mode based on key input */
83  const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL;
84  const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT;
86  if (is_ctrl_pressed && !is_shift_pressed) {
88  }
89  if (!is_ctrl_pressed && is_shift_pressed) {
91  }
92 
93  struct {
94  Object *ob;
95  BMElem *ele;
96  float dist;
97  int base_index;
98  } best = {
99  .dist = ED_view3d_select_dist_px(),
100  };
101 
102  {
103  ViewLayer *view_layer = CTX_data_view_layer(C);
104  View3D *v3d = CTX_wm_view3d(C);
105  if (((gz_ele->bases)) == NULL || (gz_ele->bases[0] != view_layer->basact)) {
106  MEM_SAFE_FREE(gz_ele->bases);
108  view_layer, v3d, &gz_ele->bases_len);
109  }
110  }
111 
112  ViewContext vc;
113  em_setup_viewcontext(C, &vc);
114  copy_v2_v2_int(vc.mval, mval);
115 
116  {
117  /* TODO: support faces. */
118  int base_index_vert = -1;
119  int base_index_edge = -1;
120  int base_index_face = -1;
121  BMVert *eve_test;
122  BMEdge *eed_test;
123  BMFace *efa_test;
124 
126  gz_ele->bases,
127  gz_ele->bases_len,
128  false,
129  true,
130  &base_index_vert,
131  &base_index_edge,
132  &base_index_face,
133  &eve_test,
134  &eed_test,
135  &efa_test)) {
137  /* Delete action */
138  if (efa_test) {
139  best.ele = (BMElem *)efa_test;
140  best.base_index = base_index_face;
141  }
142  }
143 
144  else {
145  /* Transform and create action */
146  if (eed_test) {
147  best.ele = (BMElem *)eed_test;
148  best.base_index = base_index_edge;
149  }
150  }
151 
152  /* All actions use same vertex pre-selection. */
153  /* Re-topology should always prioritize edge pre-selection.
154  * Only pre-select a vertex when the cursor is really close to it. */
155  if (eve_test) {
156  BMVert *vert = (BMVert *)eve_test;
157  float vert_p_co[3], vert_co[3];
158  const float mval_f[2] = {UNPACK2(vc.mval)};
159  mul_v3_m4v3(vert_co, gz_ele->bases[base_index_vert]->object->obmat, vert->co);
160  ED_view3d_project(vc.region, vert_co, vert_p_co);
161  float len = len_v2v2(vert_p_co, mval_f);
162  if (len < 35) {
163  best.ele = (BMElem *)eve_test;
164  best.base_index = base_index_vert;
165  }
166  if (!BM_vert_is_boundary(vert) &&
168  best.ele = (BMElem *)eve_test;
169  best.base_index = base_index_vert;
170  }
171  }
172 
173  /* Check above should never fail, if it does it's an internal error. */
174  BLI_assert(best.base_index != -1);
175 
176  Base *base = gz_ele->bases[best.base_index];
177  best.ob = base->object;
178  }
179  }
180 
181  BMesh *bm = NULL;
182 
183  gz_ele->base_index = -1;
184  gz_ele->vert_index = -1;
185  gz_ele->edge_index = -1;
186  gz_ele->face_index = -1;
187 
188  if (best.ele) {
189  gz_ele->base_index = best.base_index;
190  bm = BKE_editmesh_from_object(gz_ele->bases[gz_ele->base_index]->object)->bm;
191  BM_mesh_elem_index_ensure(bm, best.ele->head.htype);
192 
193  if (best.ele->head.htype == BM_VERT) {
194  gz_ele->vert_index = BM_elem_index_get(best.ele);
195  }
196  else if (best.ele->head.htype == BM_EDGE) {
197  gz_ele->edge_index = BM_elem_index_get(best.ele);
198  }
199  else if (best.ele->head.htype == BM_FACE) {
200  gz_ele->face_index = BM_elem_index_get(best.ele);
201  }
202  }
203 
204  if (best.ele) {
205  const float(*coords)[3] = NULL;
206  {
207  Object *ob = gz_ele->bases[gz_ele->base_index]->object;
209  Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(depsgraph, ob->data);
210  if (me_eval->runtime.edit_data) {
211  coords = me_eval->runtime.edit_data->vertexCos;
212  }
213  }
214  EDBM_preselect_elem_update_from_single(gz_ele->psel, bm, best.ele, coords);
215  EDBM_preselect_elem_update_preview(gz_ele->psel, &vc, bm, best.ele, mval);
216  }
217  else {
220  }
221 
222  RNA_int_set(gz->ptr, "object_index", gz_ele->base_index);
223  RNA_int_set(gz->ptr, "vert_index", gz_ele->vert_index);
224  RNA_int_set(gz->ptr, "edge_index", gz_ele->edge_index);
225  RNA_int_set(gz->ptr, "face_index", gz_ele->face_index);
226 
227  if (best.ele) {
228  ARegion *region = CTX_wm_region(C);
230  }
231 
232  // return best.eed ? 0 : -1;
233  return -1;
234 }
235 
237 {
238  MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
239  if (gz_ele->psel == NULL) {
240  gz_ele->psel = EDBM_preselect_elem_create();
241  }
242  gz_ele->base_index = -1;
243 }
244 
246 {
247  MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz;
249  gz_ele->psel = NULL;
250  MEM_SAFE_FREE(gz_ele->bases);
251 }
252 
254  wmGizmo *UNUSED(gz),
255  const wmEvent *UNUSED(event))
256 {
257  return OPERATOR_PASS_THROUGH;
258 }
259 
261 {
262  /* identifiers */
263  gzt->idname = "GIZMO_GT_mesh_preselect_elem_3d";
264 
265  /* api callbacks */
271 
272  gzt->struct_size = sizeof(MeshElemGizmo3D);
273 
274  RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
275  RNA_def_int(gzt->srna, "vert_index", -1, -1, INT_MAX, "Vert Index", "", -1, INT_MAX);
276  RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
277  RNA_def_int(gzt->srna, "face_index", -1, -1, INT_MAX, "Face Index", "", -1, INT_MAX);
278 }
279 
282 /* -------------------------------------------------------------------- */
286 typedef struct MeshEdgeRingGizmo3D {
294 
296 {
297  MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
298  if (gz_ring->base_index != -1) {
299  Object *ob = gz_ring->bases[gz_ring->base_index]->object;
300  EDBM_preselect_edgering_draw(gz_ring->psel, ob->obmat);
301  }
302 }
303 
304 static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
305 {
306  MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
307  struct {
308  Object *ob;
309  BMEdge *eed;
310  float dist;
311  int base_index;
312  } best = {
313  .dist = ED_view3d_select_dist_px(),
314  };
315 
316  struct {
317  int base_index;
318  int edge_index;
319  } prev = {
320  .base_index = gz_ring->base_index,
321  .edge_index = gz_ring->edge_index,
322  };
323 
324  {
325  ViewLayer *view_layer = CTX_data_view_layer(C);
326  View3D *v3d = CTX_wm_view3d(C);
327  if (((gz_ring->bases)) == NULL || (gz_ring->bases[0] != view_layer->basact)) {
328  MEM_SAFE_FREE(gz_ring->bases);
330  view_layer, v3d, &gz_ring->bases_len);
331  }
332  }
333 
334  ViewContext vc;
335  em_setup_viewcontext(C, &vc);
336  copy_v2_v2_int(vc.mval, mval);
337 
338  uint base_index;
339  BMEdge *eed_test = EDBM_edge_find_nearest_ex(
340  &vc, &best.dist, NULL, false, false, NULL, gz_ring->bases, gz_ring->bases_len, &base_index);
341 
342  if (eed_test) {
343  best.ob = gz_ring->bases[base_index]->object;
344  best.eed = eed_test;
345  best.base_index = base_index;
346  }
347 
348  BMesh *bm = NULL;
349  if (best.eed) {
350  gz_ring->base_index = best.base_index;
351  bm = BKE_editmesh_from_object(gz_ring->bases[gz_ring->base_index]->object)->bm;
353  gz_ring->edge_index = BM_elem_index_get(best.eed);
354  }
355  else {
356  gz_ring->base_index = -1;
357  gz_ring->edge_index = -1;
358  }
359 
360  if ((prev.base_index == gz_ring->base_index) && (prev.edge_index == gz_ring->edge_index)) {
361  /* pass (only recalculate on change) */
362  }
363  else {
364  if (best.eed) {
365  Object *ob = gz_ring->bases[gz_ring->base_index]->object;
366  Scene *scene_eval = (Scene *)DEG_get_evaluated_id(vc.depsgraph, &vc.scene->id);
367  Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, ob);
368  BMEditMesh *em_eval = BKE_editmesh_from_object(ob_eval);
369  /* Re-allocate coords each update isn't ideal, however we can't be sure
370  * the mesh hasn't been edited since last update. */
371  bool is_alloc = false;
372  const float(*coords)[3] = BKE_editmesh_vert_coords_when_deformed(
373  vc.depsgraph, em_eval, scene_eval, ob_eval, NULL, &is_alloc);
374  EDBM_preselect_edgering_update_from_edge(gz_ring->psel, bm, best.eed, 1, coords);
375  if (is_alloc) {
376  MEM_freeN((void *)coords);
377  }
378  }
379  else {
381  }
382 
383  RNA_int_set(gz->ptr, "object_index", gz_ring->base_index);
384  RNA_int_set(gz->ptr, "edge_index", gz_ring->edge_index);
385 
386  ARegion *region = CTX_wm_region(C);
388  }
389 
390  // return best.eed ? 0 : -1;
391  return -1;
392 }
393 
395 {
396  MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
397  if (gz_ring->psel == NULL) {
399  }
400  gz_ring->base_index = -1;
401 }
402 
404 {
405  MeshEdgeRingGizmo3D *gz_ring = (MeshEdgeRingGizmo3D *)gz;
407  gz_ring->psel = NULL;
408  MEM_SAFE_FREE(gz_ring->bases);
409 }
410 
412  wmGizmo *UNUSED(gz),
413  const wmEvent *UNUSED(event))
414 {
415  return OPERATOR_PASS_THROUGH;
416 }
417 
419 {
420  /* identifiers */
421  gzt->idname = "GIZMO_GT_mesh_preselect_edgering_3d";
422 
423  /* api callbacks */
429 
430  gzt->struct_size = sizeof(MeshEdgeRingGizmo3D);
431 
432  RNA_def_int(gzt->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", -1, INT_MAX);
433  RNA_def_int(gzt->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", -1, INT_MAX);
434 }
435 
438 /* -------------------------------------------------------------------- */
443 {
446 }
447 
450 /* -------------------------------------------------------------------- */
458  wmGizmo *gz,
459  Base **r_base,
460  BMElem **r_ele)
461 {
462  ViewLayer *view_layer = CTX_data_view_layer(C);
463 
464  const int object_index = RNA_int_get(gz->ptr, "object_index");
465 
466  /* weak, allocate an array just to access the index. */
467  Base *base = NULL;
468  Object *obedit = NULL;
469  {
470  uint bases_len;
472  view_layer, CTX_wm_view3d(C), &bases_len);
473  if (object_index < bases_len) {
474  base = bases[object_index];
475  obedit = base->object;
476  }
477  MEM_freeN(bases);
478  }
479 
480  *r_base = base;
481  *r_ele = NULL;
482 
483  if (obedit) {
484  BMEditMesh *em = BKE_editmesh_from_object(obedit);
485  BMesh *bm = em->bm;
486  PropertyRNA *prop;
487 
488  /* Ring select only defines edge, check properties exist first. */
489  prop = RNA_struct_find_property(gz->ptr, "vert_index");
490  const int vert_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
491  prop = RNA_struct_find_property(gz->ptr, "edge_index");
492  const int edge_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
493  prop = RNA_struct_find_property(gz->ptr, "face_index");
494  const int face_index = prop ? RNA_property_int_get(gz->ptr, prop) : -1;
495 
496  if (vert_index != -1) {
497  *r_ele = (BMElem *)BM_vert_at_index_find(bm, vert_index);
498  }
499  else if (edge_index != -1) {
500  *r_ele = (BMElem *)BM_edge_at_index_find(bm, edge_index);
501  }
502  else if (face_index != -1) {
503  *r_ele = (BMElem *)BM_face_at_index_find(bm, face_index);
504  }
505  }
506 }
typedef float(TangentPoint)[2]
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1424
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:760
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
const float(* BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph, struct BMEditMesh *em, struct Scene *scene, struct Object *obedit, int *r_vert_len, bool *r_is_alloc))[3]
Definition: editmesh.c:231
BMEditMesh * BKE_editmesh_from_object(struct Object *ob)
Return the BMEditMesh for a given object.
Definition: editmesh.c:85
#define BKE_view_layer_array_from_bases_in_edit_mode(view_layer, v3d, r_len)
Definition: BKE_layer.h:423
#define BLI_assert(a)
Definition: BLI_assert.h:58
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:742
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
Definition: BLI_sys_types.h:83
#define UNPACK2(a)
#define UNUSED(x)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
struct ID * DEG_get_evaluated_id(const struct Depsgraph *depsgraph, struct ID *id)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ OPERATOR_PASS_THROUGH
void EDBM_preselect_elem_update_preview(struct EditMesh_PreSelElem *psel, struct ViewContext *vc, struct BMesh *bm, struct BMElem *ele, const int mval[2])
void EDBM_preselect_elem_destroy(struct EditMesh_PreSelElem *psel)
void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matrix[4][4])
void EDBM_preselect_edgering_destroy(struct EditMesh_PreSelEdgeRing *psel)
bool EDBM_unified_findnearest_from_raycast(struct ViewContext *vc, struct Base **bases, const uint bases_len, bool use_boundary_vertices, bool use_boundary_edges, int *r_base_index_vert, int *r_base_index_edge, int *r_base_index_face, struct BMVert **r_eve, struct BMEdge **r_eed, struct BMFace **r_efa)
@ PRESELECT_ACTION_CREATE
Definition: ED_mesh.h:274
@ PRESELECT_ACTION_DELETE
Definition: ED_mesh.h:275
@ PRESELECT_ACTION_TRANSFORM
Definition: ED_mesh.h:273
void EDBM_preselect_edgering_clear(struct EditMesh_PreSelEdgeRing *psel)
void EDBM_preselect_action_set(struct EditMesh_PreSelElem *psel, eEditMesh_PreSelPreviewAction action)
eEditMesh_PreSelPreviewAction EDBM_preselect_action_get(struct EditMesh_PreSelElem *psel)
void em_setup_viewcontext(struct bContext *C, struct ViewContext *vc)
void EDBM_preselect_elem_clear(struct EditMesh_PreSelElem *psel)
struct EditMesh_PreSelEdgeRing * EDBM_preselect_edgering_create(void)
void EDBM_preselect_elem_update_from_single(struct EditMesh_PreSelElem *psel, struct BMesh *bm, struct BMElem *ele, const float(*coords)[3])
void EDBM_preselect_preview_clear(struct EditMesh_PreSelElem *psel)
struct BMEdge * EDBM_edge_find_nearest_ex(struct ViewContext *vc, float *dist_px_manhattan, float *r_dist_center, const bool use_select_bias, bool use_cycle, struct BMEdge **r_eed_zbuf, struct Base **bases, uint bases_len, uint *r_base_index)
void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const float matrix[4][4])
struct EditMesh_PreSelElem * EDBM_preselect_elem_create(void)
void EDBM_preselect_edgering_update_from_edge(struct EditMesh_PreSelEdgeRing *psel, struct BMesh *bm, struct BMEdge *eed_start, int previewlines, const float(*coords)[3])
void ED_region_tag_redraw_editor_overlays(struct ARegion *region)
Definition: area.c:706
void ED_view3d_project(const struct ARegion *region, const float world[3], float r_region_co[3])
float ED_view3d_select_dist_px(void)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition: RandGen.cpp:39
#define KM_SHIFT
Definition: WM_types.h:221
#define KM_CTRL
Definition: WM_types.h:222
@ BM_FACE
Definition: bmesh_class.h:386
@ BM_VERT
Definition: bmesh_class.h:383
@ BM_EDGE
Definition: bmesh_class.h:384
#define BM_elem_index_get(ele)
Definition: bmesh_inline.h:124
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_face_at_index_find(BMesh *bm, const int index)
Definition: bmesh_mesh.c:2386
BMEdge * BM_edge_at_index_find(BMesh *bm, const int index)
Definition: bmesh_mesh.c:2381
BMVert * BM_vert_at_index_find(BMesh *bm, const int index)
Definition: bmesh_mesh.c:2376
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
Definition: bmesh_mesh.c:2152
bool BM_vert_is_boundary(const BMVert *v)
Definition: bmesh_query.c:1168
const Depsgraph * depsgraph
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:6319
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:866
int RNA_property_int_get(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:2607
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3585
struct BMesh * bm
Definition: BKE_editmesh.h:52
float co[3]
Definition: bmesh_class.h:99
struct Object * object
const float(* vertexCos)[3]
struct EditMesh_PreSelEdgeRing * psel
struct EditMesh_PreSelElem * psel
struct EditMeshData * edit_data
Mesh_Runtime runtime
float obmat[4][4]
void * data
struct Depsgraph * depsgraph
Definition: ED_view3d.h:75
int mval[2]
Definition: ED_view3d.h:85
struct Scene * scene
Definition: ED_view3d.h:76
struct ARegion * region
Definition: ED_view3d.h:80
struct Base * basact
wmGizmoFnDraw draw
wmGizmoFnSetup setup
const char * idname
wmGizmoFnTestSelect test_select
struct StructRNA * srna
wmGizmoFnInvoke invoke
wmGizmoFnFree free
struct PointerRNA * ptr
struct wmEvent * eventstate
uint len
static void gizmo_preselect_elem_draw(const bContext *UNUSED(C), wmGizmo *gz)
static int gizmo_preselect_elem_invoke(bContext *UNUSED(C), wmGizmo *UNUSED(gz), const wmEvent *UNUSED(event))
static void GIZMO_GT_mesh_preselect_elem_3d(wmGizmoType *gzt)
static void gizmo_preselect_elem_setup(wmGizmo *gz)
static void gizmo_preselect_edgering_setup(wmGizmo *gz)
void ED_gizmotypes_preselect_3d(void)
static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const int mval[2])
struct MeshElemGizmo3D MeshElemGizmo3D
static void gizmo_preselect_elem_free(wmGizmo *gz)
struct MeshEdgeRingGizmo3D MeshEdgeRingGizmo3D
void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C, wmGizmo *gz, Base **r_base, BMElem **r_ele)
static void GIZMO_GT_mesh_preselect_edgering_3d(wmGizmoType *gzt)
static void gizmo_preselect_edgering_draw(const bContext *UNUSED(C), wmGizmo *gz)
static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int mval[2])
static int gizmo_preselect_edgering_invoke(bContext *UNUSED(C), wmGizmo *UNUSED(gz), const wmEvent *UNUSED(event))
static void gizmo_preselect_edgering_free(wmGizmo *gz)
int WM_event_modifier_flag(const wmEvent *event)
void WM_gizmotype_append(void(*gtfunc)(struct wmGizmoType *))