UI: initial support for UI editing selected items

Add basic support (holding Alt) to edit all selected objects/bones/sequences.
This commit is contained in:
Campbell Barton 2015-05-11 15:32:43 +10:00
parent 097862cb26
commit dfbb876d46
Notes: blender-bot 2023-02-14 06:00:51 +01:00
Referenced by commit 2821da5b70, Enable multi-selection editing by default (Alt-key behavior)
3 changed files with 325 additions and 5 deletions

View File

@ -981,6 +981,10 @@ void ED_button_operatortypes(void);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
int UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event);
bool UI_context_copy_to_selected_list(
struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop,
struct ListBase *r_lb, bool *r_use_path_from_id, char **r_path);
/* Helpers for Operators */
uiBut *UI_context_active_but_get(const struct bContext *C);
void UI_context_active_but_prop_get(const struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop, int *index);

View File

@ -99,6 +99,9 @@
/* support dragging multiple number buttons at once */
#define USE_DRAG_MULTINUM
/* allow dragging/editing all other selected items at once */
#define USE_ALLSELECT
/* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */
#define USE_KEYNAV_LIMIT
@ -158,6 +161,34 @@ typedef enum uiHandleButtonState {
} uiHandleButtonState;
#ifdef USE_ALLSELECT
typedef struct uiSelectContextElem {
PointerRNA ptr;
union {
bool val_b;
int val_i;
float val_f;
};
} uiSelectContextElem;
typedef struct uiSelectContextStore {
uiSelectContextElem *elems;
int elems_len;
bool do_free;
bool is_enabled;
} uiSelectContextStore;
static bool ui_selectcontext_begin(
bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data);
static void ui_selectcontext_apply(
bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data,
const double value, const double value_orig);
#define IS_ALLSELECT_EVENT(event) ((event)->alt != 0)
#endif /* USE_ALLSELECT */
#ifdef USE_DRAG_MULTINUM
/* how far to drag before we check for gesture direction (in pixels),
@ -178,6 +209,10 @@ typedef enum uiHandleButtonState {
typedef struct uiButMultiState {
double origvalue;
uiBut *but;
#ifdef USE_ALLSELECT
uiSelectContextStore select_others;
#endif
} uiButMultiState;
typedef struct uiHandleButtonMulti {
@ -210,8 +245,6 @@ typedef struct uiHandleButtonMulti {
#endif /* USE_DRAG_MULTINUM */
typedef struct uiHandleButtonData {
wmWindowManager *wm;
wmWindow *window;
@ -280,6 +313,10 @@ typedef struct uiHandleButtonData {
uiHandleButtonMulti multi_data;
#endif
#ifdef USE_ALLSELECT
uiSelectContextStore select_others;
#endif
/* post activate */
uiButtonActivateType posttype;
uiBut *postbut;
@ -909,7 +946,26 @@ static void ui_multibut_restore(uiHandleButtonData *data, uiBlock *block)
static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block)
{
#ifdef USE_ALLSELECT
if (data->multi_data.mbuts) {
LinkNode *list = data->multi_data.mbuts;
while (list) {
LinkNode *next = list->next;
uiButMultiState *mbut_state = list->link;
if (mbut_state->select_others.elems) {
MEM_freeN(mbut_state->select_others.elems);
}
MEM_freeN(list->link);
MEM_freeN(list);
list = next;
}
}
#else
BLI_linklist_freeN(data->multi_data.mbuts);
#endif
data->multi_data.mbuts = NULL;
if (data->multi_data.bs_mbuts) {
@ -1015,6 +1071,24 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl
void *active_back;
ui_but_execute_begin(C, ar, but, &active_back);
#ifdef USE_ALLSELECT
if (data->select_others.is_enabled) {
/* init once! */
if (mbut_state->select_others.elems_len == 0) {
ui_selectcontext_begin(C, but, &mbut_state->select_others);
}
if (mbut_state->select_others.elems_len == 0) {
mbut_state->select_others.elems_len = -1;
}
}
/* needed so we apply the right deltas */
but->active->origvalue = mbut_state->origvalue;
but->active->select_others = mbut_state->select_others;
but->active->select_others.do_free = false;
#endif
BLI_assert(active_back == NULL);
/* no need to check 'data->state' here */
if (data->str) {
@ -1225,6 +1299,205 @@ static bool ui_but_is_drag_toggle(const uiBut *but)
#endif /* USE_DRAG_TOGGLE */
#ifdef USE_ALLSELECT
static bool ui_selectcontext_begin(
bContext *C, uiBut *but, uiSelectContextStore *selctx_data)
{
PointerRNA ptr, lptr, idptr;
PropertyRNA *prop, *lprop;
bool success = false;
int index;
char *path = NULL;
ListBase lb = {NULL};
ptr = but->rnapoin;
prop = but->rnaprop;
index = but->rnaindex;
/* for now don't support whole colors */
if (index == -1)
return false;
/* if there is a valid property that is editable... */
if (ptr.data && prop) {
CollectionPointerLink *link;
bool use_path_from_id;
int i;
/* some facts we want to know */
const bool is_array = RNA_property_array_check(prop);
const int rna_type = RNA_property_type(prop);
if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) {
goto finally;
}
selctx_data->elems_len = BLI_listbase_count(&lb);
if (selctx_data->elems_len == 0) {
goto finally;
}
selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__);
for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) {
uiSelectContextElem *other = &selctx_data->elems[i];
/* TODO,. de-duplicate copy_to_selected_button */
if (link->ptr.data != ptr.data) {
if (use_path_from_id) {
/* Path relative to ID. */
lprop = NULL;
RNA_id_pointer_create(link->ptr.id.data, &idptr);
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
}
else if (path) {
/* Path relative to elements from list. */
lprop = NULL;
RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
}
else {
lptr = link->ptr;
lprop = prop;
}
/* lptr might not be the same as link->ptr! */
if ((lptr.data != ptr.data) &&
(lprop == prop) &&
RNA_property_editable(&lptr, lprop))
{
other->ptr = lptr;
if (is_array) {
if (rna_type == PROP_FLOAT) {
other->val_f = RNA_property_float_get_index(&lptr, lprop, index);
}
else if (rna_type == PROP_INT) {
other->val_i = RNA_property_int_get_index(&lptr, lprop, index);
}
/* ignored for now */
#if 0
else if (rna_type == PROP_BOOLEAN) {
other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index);
}
#endif
}
else {
if (rna_type == PROP_FLOAT) {
other->val_f = RNA_property_float_get(&lptr, lprop);
}
else if (rna_type == PROP_INT) {
other->val_i = RNA_property_int_get(&lptr, lprop);
}
/* ignored for now */
#if 0
else if (rna_type == PROP_BOOLEAN) {
other->val_b = RNA_property_boolean_get(&lptr, lprop);
}
else if (rna_type == PROP_ENUM) {
other->val_i = RNA_property_enum_get(&lptr, lprop);
}
#endif
}
continue;
}
}
selctx_data->elems_len -= 1;
i -= 1;
}
}
success = (selctx_data->elems_len != 0);
finally:
if (selctx_data->elems_len == 0) {
MEM_SAFE_FREE(selctx_data->elems);
}
MEM_SAFE_FREE(path);
BLI_freelistN(&lb);
/* caller can clear */
selctx_data->do_free = true;
return success;
}
static void ui_selectcontext_apply(
bContext *C, uiBut *but, uiSelectContextStore *selctx_data,
const double value, const double value_orig)
{
if (selctx_data->elems) {
PropertyRNA *prop = but->rnaprop;
PropertyRNA *lprop = but->rnaprop;
int index = but->rnaindex;
int i;
union {
bool b;
int i;
float f;
} delta;
const bool is_array = RNA_property_array_check(prop);
const int rna_type = RNA_property_type(prop);
if (rna_type == PROP_FLOAT) {
delta.f = value - value_orig;
}
else if (rna_type == PROP_INT) {
delta.i = (int)value - (int)value_orig;
}
else if (rna_type == PROP_ENUM) {
delta.i = RNA_property_enum_get(&but->rnapoin, prop); /* not a delta infact */
}
else if (rna_type == PROP_BOOLEAN) {
if (is_array) {
delta.b = RNA_property_boolean_get_index(&but->rnapoin, prop, index); /* not a delta infact */
}
else {
delta.b = RNA_property_boolean_get(&but->rnapoin, prop); /* not a delta infact */
}
}
for (i = 0; i < selctx_data->elems_len; i++) {
uiSelectContextElem *other = &selctx_data->elems[i];
PointerRNA lptr = other->ptr;
if (is_array) {
if (rna_type == PROP_FLOAT) {
RNA_property_float_set_index(&lptr, lprop, index, other->val_f + delta.f);
}
else if (rna_type == PROP_INT) {
RNA_property_int_set_index(&lptr, lprop, index, other->val_i + delta.i);
}
else if (rna_type == PROP_BOOLEAN) {
RNA_property_boolean_set_index(&lptr, lprop, index, delta.b);
}
}
else {
if (rna_type == PROP_FLOAT) {
RNA_property_float_set(&lptr, lprop, other->val_f + delta.f);
}
else if (rna_type == PROP_INT) {
RNA_property_int_set(&lptr, lprop, other->val_i + delta.i);
}
else if (rna_type == PROP_BOOLEAN) {
RNA_property_boolean_set(&lptr, lprop, delta.b);
}
else if (rna_type == PROP_ENUM) {
RNA_property_enum_set(&lptr, lprop, delta.i);
}
}
RNA_property_update(C, &lptr, prop);
}
}
}
#endif /* USE_ALLSELECT */
static bool ui_but_contains_point_px_icon(uiBut *but, ARegion *ar, const wmEvent *event)
{
rcti rect;
@ -1609,6 +1882,21 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
else if (data->applied_interactive) {
return;
}
#ifdef USE_ALLSELECT
if (data->select_others.elems_len == 0) {
wmWindow *win = CTX_wm_window(C);
/* may have been enabled before activating */
if (data->select_others.is_enabled || IS_ALLSELECT_EVENT(win->eventstate)) {
ui_selectcontext_begin(C, but, &data->select_others);
data->select_others.is_enabled = true;
}
}
if (data->select_others.elems_len == 0) {
/* dont check again */
data->select_others.elems_len = -1;
}
#endif
}
/* ensures we are writing actual values */
@ -1713,6 +2001,10 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
}
#endif
#ifdef USE_ALLSELECT
ui_selectcontext_apply(C, but, &data->select_others, data->value, data->origvalue);
#endif
but->editstr = editstr;
but->editval = editval;
but->editvec = editvec;
@ -2524,6 +2816,14 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
}
#endif
#ifdef USE_ALLSELECT
if (is_num_but) {
if (IS_ALLSELECT_EVENT(win->eventstate)) {
data->select_others.is_enabled = true;
}
}
#endif
/* retrieve string */
data->maxlen = ui_but_string_get_max_length(but);
data->str = MEM_callocN(sizeof(char) * data->maxlen + 1, "textedit str");
@ -3102,6 +3402,15 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
data->menu->popup = but->block->handle->popup;
}
#ifdef USE_ALLSELECT
{
wmWindow *win = CTX_wm_window(C);
if (IS_ALLSELECT_EVENT(win->eventstate)) {
data->select_others.is_enabled = true;
}
}
#endif
/* this makes adjacent blocks auto open from now on */
//if (but->block->auto_open == 0) but->block->auto_open = 1;
}
@ -3803,7 +4112,6 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
fac = 1.0f;
if (event->shift) fac /= 10.0f;
if (event->alt) fac /= 20.0f;
if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), snap, fac))
ui_numedit_apply(C, block, but, data);
@ -7289,6 +7597,14 @@ static void button_activate_exit(
if (data->origstr)
MEM_freeN(data->origstr);
#ifdef USE_ALLSELECT
if (data->select_others.do_free) {
if (data->select_others.elems) {
MEM_freeN(data->select_others.elems);
}
}
#endif
/* redraw (data is but->active!) */
ED_region_tag_redraw(data->region);

View File

@ -261,7 +261,7 @@ static void UI_OT_unset_property_button(wmOperatorType *ot)
/* Copy To Selected Operator ------------------------ */
static bool copy_to_selected_list(
bool UI_context_copy_to_selected_list(
bContext *C, PointerRNA *ptr, PropertyRNA *prop,
ListBase *r_lb, bool *r_use_path_from_id, char **r_path)
{
@ -325,7 +325,7 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
CollectionPointerLink *link;
ListBase lb;
if (!copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path))
if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path))
return success;
for (link = lb.first; link; link = link->next) {