Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2019-12-18 17:27:23 +0300
committerCampbell Barton <ideasman42@gmail.com>2019-12-18 17:53:15 +0300
commitd591c8a350310e69d4db23759847fb0df2ff23ae (patch)
treedc11974b50e0d2d06cdfc5a9fe8f2a2ff735f983
parent5467f3de3a4aca99e678f16356c1d54cda5fb42f (diff)
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).
-rw-r--r--source/blender/editors/space_image/space_image.c12
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c5
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c11
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c3
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h6
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h23
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c24
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c50
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c6
9 files changed, 122 insertions, 18 deletions
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index c0cdce1b000..2f93be8ae38 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -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;
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
index f761a44fe16..8cd5ed7a478 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c
@@ -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;
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index f19147baa89..b3c1fbd3aad 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -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;
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index da6b0285a5c..fb33471cf3f 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -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;
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
index a81a4596733..c2fbaaaa83c 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -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);
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
index d3aa333daea..fc876b56de7 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -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;
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index d2638ae148e..73bee883cf8 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -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);
}
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 97bb203bcf6..f176f0e5df4 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -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;
}
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 4117c036dcb..c1b56c0b9ae 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -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);
}