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
path: root/source
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2013-02-22 09:56:20 +0400
committerCampbell Barton <ideasman42@gmail.com>2013-02-22 09:56:20 +0400
commita9e25ac43320a253f557d945a8e83245e414a2ea (patch)
tree646737f30921fba6dabb467435fda5295827b093 /source
parentb00c3b801bf07a67b98b604ee834e2d93dff0f39 (diff)
Toggle-Drag UI Feature
Dragging on toggle buttons can now be used to press multiple buttons at once, especially useful for layer and outliner buttons. notes: - automatically enabled for all toggle buttons (may change this if it becomes a problem). - only buttons of the same type are pressed (helps avoid annoyances eg; dragging past layer buttons onto other 3d header buttons and pressing by accident). - automatic axis locking - dragging will lock to X/Y depending on the initial drag direction, makes swipe motions work better, especially with the outliner. implementation details: - may re-implement as a region handler (currently its a modal operator). - checking buttons in-between cursor motion events could be more efficient (but currently works ok). - button execution needs to be improved (currently executing a button thats not under the mouse needed a workaround for passing uiHandleButtonData), requires further changes to UI code, will do next.
Diffstat (limited to 'source')
-rw-r--r--source/blender/editors/include/UI_interface.h3
-rw-r--r--source/blender/editors/interface/interface.c18
-rw-r--r--source/blender/editors/interface/interface_handlers.c71
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_ops.c223
5 files changed, 308 insertions, 9 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 648f8bfd8dd..78689c078c6 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -437,6 +437,7 @@ void uiButSetDragValue(uiBut *but);
void uiButSetDragImage(uiBut *but, const char *path, int icon, struct ImBuf *ima, float scale);
int UI_but_active_drop_name(struct bContext *C);
+struct uiBut *ui_but_find_mouse_over(struct ARegion *ar, int x, int y);
void uiButSetFlag(uiBut *but, int flag);
void uiButClearFlag(uiBut *but, int flag);
@@ -447,6 +448,8 @@ void uiButClearDrawFlag(uiBut *but, int flag);
/* special button case, only draw it when used actively, for outliner etc */
int uiButActiveOnly(const struct bContext *C, uiBlock *block, uiBut *but);
+void uiButExecute(const struct bContext *C, uiBut *but);
+
/* Buttons
*
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index d245349f2c4..e10d588c19f 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -713,6 +713,12 @@ int uiButActiveOnly(const bContext *C, uiBlock *block, uiBut *but)
return 1;
}
+/* simulate button click */
+void uiButExecute(const bContext *C, uiBut *but)
+{
+ ui_button_execute_do((bContext *)C, CTX_wm_region(C), but);
+}
+
/* use to check if we need to disable undo, but don't make any changes
* returns FALSE if undo needs to be disabled. */
static int ui_but_is_rna_undo(uiBut *but)
@@ -1394,6 +1400,18 @@ int ui_is_but_float(uiBut *but)
return 0;
}
+int ui_is_but_bool(uiBut *but)
+{
+ if (ELEM5(but->type, TOG, TOGN, TOGR, ICONTOG, ICONTOGN))
+ return 1;
+
+ if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_BOOLEAN)
+ return 1;
+
+ return 0;
+}
+
+
int ui_is_but_unit(uiBut *but)
{
UnitSettings *unit = but->block->unit;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 017ab559911..532f9b50725 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -85,6 +85,7 @@
/* proto */
static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to);
static void ui_add_link(bContext *C, uiBut *from, uiBut *to);
+static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, const wmEvent *event);
/***************** structs and defines ****************/
@@ -761,14 +762,27 @@ static int ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data,
WM_gestures_remove(C);
if (ABS(data->dragstartx - event->x) + ABS(data->dragstarty - event->y) > U.dragthreshold) {
- wmDrag *drag;
-
+
button_activate_state(C, but, BUTTON_STATE_EXIT);
data->cancel = TRUE;
- drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but));
- if (but->imb)
- WM_event_drag_image(drag, but->imb, but->imb_scale, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect));
+ if (ui_is_but_bool(but)) {
+ const bool is_set = (ui_get_but_val(but) != 0.0);
+ PointerRNA ptr;
+ WM_operator_properties_create(&ptr, "UI_OT_drag_toggle");
+ RNA_boolean_set(&ptr, "state", !is_set);
+ RNA_int_set(&ptr, "last_x", data->dragstartx);
+ RNA_int_set(&ptr, "last_y", data->dragstarty);
+ WM_operator_name_call(C, "UI_OT_drag_toggle", WM_OP_INVOKE_DEFAULT, &ptr);
+ WM_operator_properties_free(&ptr);
+ }
+ else {
+ wmDrag *drag;
+
+ drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but));
+ if (but->imb)
+ WM_event_drag_image(drag, but->imb, but->imb_scale, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect));
+ }
return 1;
}
@@ -2472,13 +2486,24 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa
static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
+ if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_is_but_bool(but)) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+ data->dragstartx = event->x;
+ data->dragstarty = event->y;
+ return WM_UI_HANDLER_CONTINUE;
+ }
+
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
data->togdual = event->ctrl;
data->togonly = !event->shift;
button_activate_state(C, but, BUTTON_STATE_EXIT);
- return WM_UI_HANDLER_BREAK;
+ return WM_UI_HANDLER_CONTINUE;
}
}
+ else if (data->state == BUTTON_STATE_WAIT_DRAG) {
+ /* note: the 'BUTTON_STATE_WAIT_DRAG' part of 'ui_do_but_EXIT' could be refactored into its own function */
+ return ui_do_but_EXIT(C, but, data, event);
+ }
return WM_UI_HANDLER_CONTINUE;
}
@@ -2499,6 +2524,12 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
return WM_UI_HANDLER_CONTINUE;
}
}
+ if (event->type == LEFTMOUSE && ui_is_but_bool(but)) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+ data->dragstartx = event->x;
+ data->dragstarty = event->y;
+ return WM_UI_HANDLER_CONTINUE;
+ }
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
int ret = WM_UI_HANDLER_BREAK;
@@ -3215,6 +3246,12 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
return WM_UI_HANDLER_BREAK;
}
}
+ if (event->type == LEFTMOUSE && ui_is_but_bool(but)) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
+ data->dragstartx = event->x;
+ data->dragstarty = event->y;
+ return WM_UI_HANDLER_BREAK;
+ }
/* regular open menu */
if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
@@ -5415,7 +5452,7 @@ static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
return 1;
}
-static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
+uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
{
uiBlock *block;
uiBut *but, *butover = NULL;
@@ -5784,8 +5821,10 @@ static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *b
ED_region_tag_redraw(data->region);
/* clean up button */
- MEM_freeN(but->active);
- but->active = NULL;
+ if (but->active) {
+ MEM_freeN(but->active);
+ but->active = NULL;
+ }
but->flag &= ~(UI_ACTIVE | UI_SELECT);
but->flag |= UI_BUT_LAST_ACTIVE;
if (!onfree)
@@ -6034,6 +6073,20 @@ void ui_button_activate_do(bContext *C, ARegion *ar, uiBut *but)
ui_do_button(C, but->block, but, &event);
}
+void ui_button_execute_do(struct bContext *C, struct ARegion *ar, uiBut *but)
+{
+ /* note: ideally we would not have to change 'but->active' howevwer
+ * some functions we call don't use data (as they should be doing) */
+ void *active_back = but->active;
+ uiHandleButtonData *data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData_Fake");
+ but->active = data;
+ data->region = ar;
+ ui_apply_button(C, but->block, but, data, true);
+ /* use onfree event so undo is handled by caller and apply is already done above */
+ button_activate_exit((bContext *)C, data, but, false, true);
+ but->active = active_back;
+}
+
static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
{
uiBut *oldbut;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index b1e8b7b001e..f0e59f1f935 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -406,6 +406,7 @@ extern void ui_set_but_soft_range(uiBut *but, double value);
extern void ui_check_but(uiBut *but);
extern int ui_is_but_float(uiBut *but);
+extern int ui_is_but_bool(uiBut *but);
extern int ui_is_but_unit(uiBut *but);
extern int ui_is_but_rna_valid(uiBut *but);
extern int ui_is_but_utf8(uiBut *but);
@@ -509,6 +510,7 @@ void ui_draw_but_TRACKPREVIEW(ARegion *ar, uiBut *but, struct uiWidgetColors *wc
/* interface_handlers.c */
extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val);
extern void ui_button_activate_do(struct bContext *C, struct ARegion *ar, uiBut *but);
+extern void ui_button_execute_do(struct bContext *C, struct ARegion *ar, uiBut *but);
extern void ui_button_active_free(const struct bContext *C, uiBut *but);
extern int ui_button_is_active(struct ARegion *ar);
extern int ui_button_open_menu_direction(uiBut *but);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index e03a171da18..368ecdefe2c 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1071,6 +1071,228 @@ static void UI_OT_reloadtranslation(wmOperatorType *ot)
ot->exec = reloadtranslation_exec;
}
+
+/* -------------------------------------------------------------------- */
+/* Toggle Drag Operator */
+
+typedef struct DragOpInfo {
+ bool xy_lock[2];
+ float but_cent_start[2];
+ eButType but_type_start;
+} DragOpInfo;
+
+typedef struct DragOpPlotData {
+ bContext *C;
+ ARegion *ar;
+ bool is_set;
+ eButType but_type_start;
+ bool do_draw;
+ const uiBut *but_prev;
+} DragOpPlotData;
+
+static const uiBut *ui_but_set_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
+ const int xy[2], const uiBut *but_prev)
+{
+ uiBut *but = ui_but_find_mouse_over(ar, xy[0], xy[1]);
+
+ if (but_prev == but) {
+ return but_prev;
+ }
+
+ if (but && ui_is_but_bool(but) && but->type == but_type_start) {
+ /* is it pressed? */
+ bool is_set_but = (ui_get_but_val(but) != 0.0);
+ BLI_assert(ui_is_but_bool(but) == true);
+ if (is_set_but != is_set) {
+ uiButExecute(C, but);
+ return but;
+ }
+ }
+
+ return but_prev;
+}
+
+static int ui_but_set_cb(int x, int y, void *data_v)
+{
+ DragOpPlotData *data = data_v;
+ int xy[2] = {x, y};
+ data->but_prev = ui_but_set_xy(data->C, data->ar, data->is_set, data->but_type_start, xy, data->but_prev);
+ return 1; /* keep going */
+}
+
+/* operates on buttons between 2 mouse-points */
+static bool ui_but_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start,
+ const int xy_src[2], const int xy_dst[2])
+{
+ DragOpPlotData data;
+ data.C = C;
+ data.ar = ar;
+ data.is_set = is_set;
+ data.but_type_start = but_type_start;
+ data.do_draw = false;
+ data.but_prev = NULL;
+
+
+ /* prevent dragging too fast loosing buttons */
+ plot_line_v2v2i(xy_src, xy_dst, ui_but_set_cb, &data);
+
+ return data.do_draw;
+}
+
+static void ui_drag_but_set(bContext *C, wmOperator *op, const int xy_input[2])
+{
+ ARegion *ar = CTX_wm_region(C);
+ DragOpInfo *drag_info = op->customdata;
+ bool do_draw = false;
+
+ const bool is_set = RNA_boolean_get(op->ptr, "state");
+ const int xy_last[2] = {RNA_int_get(op->ptr, "last_x"),
+ RNA_int_get(op->ptr, "last_y")};
+
+ int xy[2];
+
+ /**
+ * Initialize Locking:
+ *
+ * Check if we need to initialize the lock axis by finding if the first
+ * button we mouse over is X or Y aligned, then lock the mouse to that axis after.
+ */
+ if (drag_info->xy_lock[0] == false && drag_info->xy_lock[1] == false) {
+ ARegion *ar = CTX_wm_region(C);
+
+ /* first store the buttons original coords */
+ uiBut *but = ui_but_find_mouse_over(ar, xy_input[0], xy_input[1]);
+ if (but) {
+ const float but_cent_new[2] = {BLI_rctf_cent_x(&but->rect),
+ BLI_rctf_cent_y(&but->rect)};
+
+ /* check if this is a different button, chances are high the button wont move about :) */
+ if (len_manhattan_v2v2(drag_info->but_cent_start, but_cent_new) > 1.0f) {
+ if (fabsf(drag_info->but_cent_start[0] - but_cent_new[0]) <
+ fabsf(drag_info->but_cent_start[1] - but_cent_new[1]))
+ {
+ drag_info->xy_lock[0] = true;
+ }
+ else {
+ drag_info->xy_lock[1] = true;
+ }
+ }
+ }
+ }
+ /* done with axis locking */
+
+
+ xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : xy_last[0];
+ xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : xy_last[1];
+
+
+ /* touch all buttons between last mouse coord and this one */
+ do_draw = ui_but_set_xy_xy(C, ar, is_set, drag_info->but_type_start, xy_last, xy);
+
+ if (do_draw) {
+ ED_region_tag_redraw(ar);
+ }
+
+ RNA_int_set(op->ptr, "last_x", xy[0]);
+ RNA_int_set(op->ptr, "last_y", xy[1]);
+}
+
+static int ui_drag_toggle_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ int xy_last[2] = {RNA_int_get(op->ptr, "last_x"),
+ RNA_int_get(op->ptr, "last_y")};
+
+ float but_cent_start[2];
+ eButType but_type_start;
+ DragOpInfo *drag_info;
+
+ {
+ /* find the button where we started dragging */
+ ARegion *ar = CTX_wm_region(C);
+ uiBut *but = ui_but_find_mouse_over(ar, xy_last[0], xy_last[1]);
+ if (but) {
+ but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
+ but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
+ but_type_start = but->type;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ drag_info = op->customdata = MEM_callocN(sizeof(DragOpInfo), __func__);
+ copy_v2_v2(drag_info->but_cent_start, but_cent_start);
+ drag_info->but_type_start = but_type_start;
+
+ /* set the initial button */
+ ui_drag_but_set(C, op, xy_last);
+ ui_drag_but_set(C, op, &event->x);
+
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int ui_drag_toggle_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ bool done = false;
+
+ switch (event->type) {
+ case LEFTMOUSE:
+ {
+ if (event->val != KM_PRESS) {
+ done = true;
+ }
+ break;
+ }
+ case MOUSEMOVE:
+ {
+ ARegion *ar = CTX_wm_region(C);
+ if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
+ done = true;
+ }
+ else {
+ ui_drag_but_set(C, op, &event->x);
+ }
+ break;
+ }
+ }
+
+ if (done) {
+ MEM_freeN(op->customdata);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_RUNNING_MODAL;
+ }
+}
+
+static int ui_drag_toggle_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ MEM_freeN(op->customdata);
+ return OPERATOR_CANCELLED;
+}
+
+static void UI_OT_drag_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Button Drag Toggle";
+ ot->description = "";
+ ot->idname = "UI_OT_drag_toggle";
+
+ /* api callbacks */
+ ot->invoke = ui_drag_toggle_invoke;
+ ot->modal = ui_drag_toggle_modal;
+ ot->cancel = ui_drag_toggle_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "state", true, "State", "");
+ RNA_def_int(ot->srna, "last_x", 0, 0, INT_MAX, "X", "", 0, INT_MAX);
+ RNA_def_int(ot->srna, "last_y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX);
+}
+
/* ********************************************************* */
/* Registration */
@@ -1088,5 +1310,6 @@ void UI_buttons_operatortypes(void)
WM_operatortype_append(UI_OT_edittranslation_init);
#endif
WM_operatortype_append(UI_OT_reloadtranslation);
+ WM_operatortype_append(UI_OT_drag_toggle);
}