Gizmo: add the ability to postpone refreshing while tweaking

This resolves a logical problem using tweak as a fallback tool.
See: T66304#828742

The select action would immediately show the gizmo underneath it,
then the tweak would be handled by the gizmo instead of moving the item
under the cursor.

Currently this works by hiding the gizmo until the tweak event ends.
While it's simpler to check if the gizmo received a mouse-down event,
it causes flickering before each drag event which feels like a glitch.

This is optional for each gizmo type because there are cases where this
can be useful to activate the gizmo immediately (mesh rip for example).
This commit is contained in:
Campbell Barton 2019-12-19 01:27:23 +11:00
parent 5467f3de3a
commit d591c8a350
9 changed files with 122 additions and 18 deletions

View File

@ -464,7 +464,8 @@ static void IMAGE_GGT_gizmo2d(wmGizmoGroupType *gzgt)
gzgt->name = "UV Transform Gizmo";
gzgt->idname = "IMAGE_GGT_gizmo2d";
gzgt->flag |= WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK);
gzgt->gzmap_params.spaceid = SPACE_IMAGE;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -481,7 +482,8 @@ static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt)
gzgt->name = "UV Translate Gizmo";
gzgt->idname = "IMAGE_GGT_gizmo2d_translate";
gzgt->flag |= WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK);
gzgt->gzmap_params.spaceid = SPACE_IMAGE;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -498,7 +500,8 @@ static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt)
gzgt->name = "UV Transform Gizmo Resize";
gzgt->idname = "IMAGE_GGT_gizmo2d_resize";
gzgt->flag |= WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK);
gzgt->gzmap_params.spaceid = SPACE_IMAGE;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -515,7 +518,8 @@ static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt)
gzgt->name = "UV Transform Gizmo Resize";
gzgt->idname = "IMAGE_GGT_gizmo2d_rotate";
gzgt->flag |= WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK);
gzgt->gzmap_params.spaceid = SPACE_IMAGE;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;

View File

@ -195,7 +195,8 @@ void VIEW3D_GGT_tool_generic_handle_normal(wmGizmoGroupType *gzgt)
gzgt->name = "Generic Tool Widget Normal";
gzgt->idname = handle_normal_id;
gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP);
gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK);
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -211,6 +212,8 @@ void VIEW3D_GGT_tool_generic_handle_free(wmGizmoGroupType *gzgt)
gzgt->name = "Generic Tool Widget Free";
gzgt->idname = handle_free_id;
/* Don't use 'WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK' here since this style of gizmo
* is better suited to being activated immediately. */
gzgt->flag |= (WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP);
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;

View File

@ -2046,7 +2046,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt)
gzgt->name = "3D View: Transform Gizmo";
gzgt->idname = "VIEW3D_GGT_xform_gizmo";
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -2081,7 +2082,7 @@ void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt)
gzgt->idname = "VIEW3D_GGT_xform_gizmo_context";
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT |
WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->poll = WIDGETGROUP_gizmo_poll_context;
gzgt->setup = WIDGETGROUP_gizmo_setup;
@ -2289,7 +2290,8 @@ void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt)
gzgt->name = "Transform Cage";
gzgt->idname = "VIEW3D_GGT_xform_cage";
gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@ -2530,7 +2532,8 @@ void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt)
gzgt->name = "Transform Shear";
gzgt->idname = "VIEW3D_GGT_xform_shear";
gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;

View File

@ -514,7 +514,8 @@ void VIEW3D_GGT_xform_extrude(struct wmGizmoGroupType *gzgt)
gzgt->name = "3D View Extrude";
gzgt->idname = "VIEW3D_GGT_xform_extrude";
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP;
gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP |
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;

View File

@ -280,7 +280,13 @@ const struct ListBase *WM_gizmomap_group_list(struct wmGizmoMap *gzmap);
struct wmGizmoGroup *WM_gizmomap_group_find(struct wmGizmoMap *gzmap, const char *idname);
struct wmGizmoGroup *WM_gizmomap_group_find_ptr(struct wmGizmoMap *gzmap,
const struct wmGizmoGroupType *gzgt);
eWM_GizmoFlagMapDrawStep WM_gizmomap_drawstep_from_gizmo_group(const struct wmGizmoGroup *gzgroup);
void WM_gizmomap_tag_refresh_drawstep(struct wmGizmoMap *gzmap,
const eWM_GizmoFlagMapDrawStep drawstep);
void WM_gizmomap_tag_refresh(struct wmGizmoMap *gzmap);
bool WM_gizmomap_tag_refresh_check(struct wmGizmoMap *gzmap);
void WM_gizmomap_draw(struct wmGizmoMap *gzmap,
const struct bContext *C,
const eWM_GizmoFlagMapDrawStep drawstep);

View File

@ -124,8 +124,21 @@ typedef enum eWM_GizmoFlagGroupTypeFlag {
/**
* This gizmo type supports using the fallback tools keymap.
* #wmGizmoGroup.use_tool_fallback will need to be set too.
*
* Often useful in combination with #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK
*/
WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 7),
/**
* Use this from a gizmos refresh callback so we can postpone the refresh operation
* until the tweak operation is finished.
* Only do this when the group doesn't have a highlighted gizmo.
*
* The result for the user is tweak events delay the gizmo from flashing under the users cursor,
* for selection operations. This means gizmos that use this check don't interfere
* with click drag events by popping up under the cursor and catching the tweak event.
*/
WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 8),
} eWM_GizmoFlagGroupTypeFlag;
/**
@ -447,6 +460,16 @@ typedef struct wmGizmoGroup {
/** Errors and warnings storage. */
struct ReportList *reports;
/** Has the same result as hiding all gizmos individually. */
struct {
/* Reasons for hiding. */
union {
uint delay_refresh_for_tweak : 1;
};
/* All, when we only want to check. */
uint any;
} hide;
bool tag_remove;
bool use_fallback_keymap;

View File

@ -284,8 +284,10 @@ void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup)
/* Refresh may be called multiple times,
* this just ensures its called at least once before we draw. */
if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_REFRESH) == 0)) {
WM_gizmo_group_refresh(C, gzgroup);
/* Clear the flag before calling refresh so the callback
* can postpone the refresh by clearing this flag. */
gzgroup->init_flag |= WM_GIZMOGROUP_INIT_REFRESH;
WM_gizmo_group_refresh(C, gzgroup);
}
}
@ -1147,6 +1149,26 @@ bool WM_gizmo_group_type_poll(const bContext *C, const wmGizmoGroupType *gzgt)
void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
const wmGizmoGroupType *gzgt = gzgroup->type;
if (gzgt->flag & WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK) {
wmGizmoMap *gzmap = gzgroup->parent_gzmap;
wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
if (!gz || gz->parent_gzgroup != gzgroup) {
wmWindow *win = CTX_wm_window(C);
if (win->tweak) {
/* We need to run refresh again. */
gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup));
gzgroup->hide.delay_refresh_for_tweak = true;
return;
}
}
gzgroup->hide.delay_refresh_for_tweak = false;
}
if (gzgroup->hide.any) {
return;
}
if (gzgt->refresh) {
gzgt->refresh(C, gzgroup);
}

View File

@ -304,16 +304,47 @@ static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C,
return hash;
}
eWM_GizmoFlagMapDrawStep WM_gizmomap_drawstep_from_gizmo_group(const wmGizmoGroup *gzgroup)
{
eWM_GizmoFlagMapDrawStep step;
if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
step = WM_GIZMOMAP_DRAWSTEP_3D;
}
else {
step = WM_GIZMOMAP_DRAWSTEP_2D;
}
return step;
}
void WM_gizmomap_tag_refresh_drawstep(wmGizmoMap *gzmap, const eWM_GizmoFlagMapDrawStep drawstep)
{
BLI_assert((uint)drawstep < WM_GIZMOMAP_DRAWSTEP_MAX);
if (gzmap) {
gzmap->update_flag[drawstep] |= (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK);
}
}
void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
{
if (gzmap) {
/* We might want only to refresh some, for tag all steps. */
for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
gzmap->update_flag[i] |= (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK);
}
}
}
bool WM_gizmomap_tag_refresh_check(wmGizmoMap *gzmap)
{
if (gzmap) {
for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
if (gzmap->update_flag[i] & (GIZMOMAP_IS_PREPARE_DRAW | GIZMOMAP_IS_REFRESH_CALLBACK)) {
return true;
}
}
}
return false;
}
static bool gizmo_prepare_drawing(wmGizmoMap *gzmap,
wmGizmo *gz,
const bContext *C,
@ -359,7 +390,8 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
/* only active gizmo needs updating */
if (gz_modal) {
if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) {
if (wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) {
if ((gz_modal->parent_gzgroup->hide.any == 0) &&
wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) {
if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) {
gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
}
@ -369,9 +401,13 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
}
}
/* Allow refresh functions to ask to be refreshed again, clear before the loop below. */
const bool do_refresh = gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK;
gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
/* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) ||
if ((gzgroup->hide.any != 0) || !wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) ||
!WM_gizmo_group_type_poll(C, gzgroup->type)) {
continue;
}
@ -379,7 +415,7 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
/* Needs to be initialized on first draw. */
/* XXX weak: Gizmo-group may skip refreshing if it's invisible
* (map gets untagged nevertheless). */
if (gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK) {
if (do_refresh) {
/* force refresh again. */
gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
}
@ -396,7 +432,7 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap,
}
}
gzmap->update_flag[drawstep] &= ~(GIZMOMAP_IS_REFRESH_CALLBACK | GIZMOMAP_IS_PREPARE_DRAW);
gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
}
/**
@ -716,7 +752,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap,
/* If it were important we could initialize here,
* but this only happens when events are handled before drawing,
* just skip to keep code-path for initializing gizmos simple. */
if ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0) {
if ((gzgroup->hide.any != 0) || ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) {
continue;
}
@ -1126,7 +1162,7 @@ void WM_gizmomap_message_subscribe(bContext *C,
struct wmMsgBus *mbus)
{
for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
if ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0 ||
if ((gzgroup->hide.any != 0) || (gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0 ||
!WM_gizmo_group_type_poll(C, gzgroup->type)) {
continue;
}

View File

@ -2811,6 +2811,12 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
BLI_assert(gzmap != NULL);
wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
/* Special case, needed so postponed refresh can respond to events,
* see #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */
if (WM_gizmomap_tag_refresh_check(gzmap)) {
ED_region_tag_redraw(region);
}
if (region->gizmo_map != handler->gizmo_map) {
WM_gizmomap_tag_refresh(handler->gizmo_map);
}