Manipulator: refactor/fix selection logic

- Cleanup array access, move into functions.
- Store allocated size to avoid realloc's on every add/remove.
- Make select editable from Python.
- Rename select callback to select_refresh
  (collided with select boolean).
- Call select_refresh when de-selecting as well as selection.
This commit is contained in:
Campbell Barton 2017-07-26 08:12:46 +10:00
parent dee19b8cb7
commit 54bc49e6f9
8 changed files with 170 additions and 126 deletions

View File

@ -210,21 +210,19 @@ static void rna_manipulator_exit_cb(
RNA_parameter_list_free(&list);
}
static void rna_manipulator_select_cb(
struct bContext *C, struct wmManipulator *mpr, int action)
static void rna_manipulator_select_refresh_cb(
struct wmManipulator *mpr)
{
extern FunctionRNA rna_Manipulator_select_func;
extern FunctionRNA rna_Manipulator_select_refresh_func;
wmManipulatorGroup *mgroup = mpr->parent_mgroup;
PointerRNA mpr_ptr;
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, mpr->type->ext.srna, mpr, &mpr_ptr);
/* RNA_struct_find_function(&mpr_ptr, "select"); */
func = &rna_Manipulator_select_func;
/* RNA_struct_find_function(&mpr_ptr, "select_refresh"); */
func = &rna_Manipulator_select_refresh_func;
RNA_parameter_list_create(&list, &mpr_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
RNA_parameter_set_lookup(&list, "action", &action);
mgroup->type->ext.call((bContext *)C, &mpr_ptr, func, &list);
mgroup->type->ext.call((bContext *)NULL, &mpr_ptr, func, &list);
RNA_parameter_list_free(&list);
}
@ -375,6 +373,13 @@ RNA_MANIPULATOR_FLAG_RO_DEF(state_is_highlight, state, WM_MANIPULATOR_STATE_HIGH
RNA_MANIPULATOR_FLAG_RO_DEF(state_is_modal, state, WM_MANIPULATOR_STATE_MODAL);
RNA_MANIPULATOR_FLAG_RO_DEF(state_select, state, WM_MANIPULATOR_STATE_SELECT);
static void rna_Manipulator_state_select_set(struct PointerRNA *ptr, int value)
{
wmManipulator *mpr = ptr->data;
wmManipulatorGroup *mgroup = mpr->parent_mgroup;
WM_manipulator_select_set(mgroup->parent_mmap, mpr, value);
}
static void rna_Manipulator_name_get(PointerRNA *ptr, char *value)
{
wmManipulator *mpr = ptr->data;
@ -464,7 +469,7 @@ static StructRNA *rna_Manipulator_register(
dummywt.setup = (have_function[i++]) ? rna_manipulator_setup_cb : NULL;
dummywt.invoke = (have_function[i++]) ? rna_manipulator_invoke_cb : NULL;
dummywt.exit = (have_function[i++]) ? rna_manipulator_exit_cb : NULL;
dummywt.select = (have_function[i++]) ? rna_manipulator_select_cb : NULL;
dummywt.select_refresh = (have_function[i++]) ? rna_manipulator_select_refresh_cb : NULL;
BLI_assert(i == ARRAY_SIZE(have_function));
}
@ -974,22 +979,10 @@ static void rna_def_manipulator(BlenderRNA *brna, PropertyRNA *cprop)
/* wmManipulator.cursor_get */
/* TODO */
/* wmManipulator.select */
/* TODO, de-duplicate! */
static EnumPropertyItem select_actions[] = {
{SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
{SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
{SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
{SEL_INVERT, "INVERT", 0, "Invert", "Invert selection of all elements"},
{0, NULL, 0, NULL, NULL}
};
func = RNA_def_function(srna, "select", NULL);
/* wmManipulator.select_refresh */
func = RNA_def_function(srna, "select_refresh", NULL);
RNA_def_function_ui_description(func, "");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_enum(func, "action", select_actions, 0, "Action", "Selection action to execute");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* -------------------------------------------------------------------- */
@ -1092,10 +1085,10 @@ static void rna_def_manipulator(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_ui_text(prop, "Highlight", "");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
/* WM_MANIPULATOR_STATE_SELECT */
/* (note that setting is involved, needs to handle array) */
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_Manipulator_state_select_get", NULL);
RNA_def_property_boolean_funcs(prop, "rna_Manipulator_state_select_get", "rna_Manipulator_state_select_set");
RNA_def_property_ui_text(prop, "Select", "");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_api_manipulator(srna);

View File

@ -69,6 +69,8 @@ void WM_manipulator_free(
void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name);
bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select);
struct PointerRNA *WM_manipulator_set_operator(
struct wmManipulator *, struct wmOperatorType *ot, struct IDProperty *properties);

View File

@ -238,7 +238,7 @@ typedef struct wmManipulatorType {
wmManipulatorFnCursorGet cursor_get;
/* called when manipulator selection state changes */
wmManipulatorFnSelect select;
wmManipulatorFnSelectRefresh select_refresh;
/* RNA for properties */
struct StructRNA *srna;

View File

@ -201,7 +201,7 @@ void WM_manipulator_free(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmMa
wm_manipulatormap_modal_set(mmap, C, NULL, NULL);
}
if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
wm_manipulator_deselect(mmap, mpr);
WM_manipulator_select_set(mmap, mpr, false);
}
if (mpr->op_data.ptr.data) {
@ -386,73 +386,57 @@ void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulator
/* -------------------------------------------------------------------- */
/**
* Remove \a manipulator from selection.
* Add/Remove \a manipulator to selection.
* Reallocates memory for selected manipulators so better not call for selecting multiple ones.
*
* \return if the selection has changed.
*/
bool wm_manipulator_deselect(wmManipulatorMap *mmap, wmManipulator *mpr)
bool wm_manipulator_select_set_ex(wmManipulatorMap *mmap, wmManipulator *mpr, bool select, bool use_array)
{
if (!mmap->mmap_context.selected)
return false;
wmManipulator ***sel = &mmap->mmap_context.selected;
int *selected_len = &mmap->mmap_context.selected_len;
bool changed = false;
/* caller should check! */
BLI_assert(mpr->state & WM_MANIPULATOR_STATE_SELECT);
/* remove manipulator from selected_manipulators array */
for (int i = 0; i < (*selected_len); i++) {
if ((*sel)[i] == mpr) {
for (int j = i; j < ((*selected_len) - 1); j++) {
(*sel)[j] = (*sel)[j + 1];
if (select) {
if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
if (use_array) {
wm_manipulatormap_select_array_push_back(mmap, mpr);
}
mpr->state |= WM_MANIPULATOR_STATE_SELECT;
changed = true;
}
}
else {
if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
if (use_array) {
wm_manipulatormap_select_array_remove(mmap, mpr);
}
mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
changed = true;
break;
}
}
/* update array data */
if ((*selected_len) <= 1) {
wm_manipulatormap_selected_clear(mmap);
}
else {
*sel = MEM_reallocN(*sel, sizeof(**sel) * (*selected_len));
(*selected_len)--;
if (changed) {
if (mpr->type->select_refresh) {
mpr->type->select_refresh(mpr);
}
}
mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
return changed;
}
/**
* Add \a manipulator to selection.
* Reallocates memory for selected manipulators so better not call for selecting multiple ones.
*
* \return if the selection has changed.
*/
bool wm_manipulator_select(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select)
{
wmManipulator ***sel = &mmap->mmap_context.selected;
int *selected_len = &mmap->mmap_context.selected_len;
return wm_manipulator_select_set_ex(mmap, mpr, select, true);
}
if (!mpr || (mpr->state & WM_MANIPULATOR_STATE_SELECT))
return false;
(*selected_len)++;
*sel = MEM_reallocN(*sel, sizeof(wmManipulator *) * (*selected_len));
(*sel)[(*selected_len) - 1] = mpr;
mpr->state |= WM_MANIPULATOR_STATE_SELECT;
if (mpr->type->select) {
mpr->type->select(C, mpr, SEL_SELECT);
bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
{
if (WM_manipulator_select_set(mmap, mpr, true)) {
wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
return true;
}
else {
return false;
}
wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
return true;
}
void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C)

View File

@ -226,7 +226,7 @@ static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent
{
ARegion *ar = CTX_wm_region(C);
wmManipulatorMap *mmap = ar->manipulator_map;
wmManipulator ***sel = &mmap->mmap_context.selected;
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
wmManipulator *highlight = mmap->mmap_context.highlight;
bool extend = RNA_boolean_get(op->ptr, "extend");
@ -235,8 +235,8 @@ static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent
/* deselect all first */
if (extend == false && deselect == false && toggle == false) {
wm_manipulatormap_deselect_all(mmap, sel);
BLI_assert(*sel == NULL && mmap->mmap_context.selected_len == 0);
wm_manipulatormap_deselect_all(mmap);
BLI_assert(msel->items == NULL && msel->len == 0);
}
if (highlight) {
@ -249,11 +249,11 @@ static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent
}
if (deselect) {
if (is_selected && wm_manipulator_deselect(mmap, highlight)) {
if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) {
redraw = true;
}
}
else if (wm_manipulator_select(C, mmap, highlight)) {
else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) {
redraw = true;
}

View File

@ -38,8 +38,9 @@ struct GHashIterator;
/* -------------------------------------------------------------------- */
/* wmManipulator */
bool wm_manipulator_deselect(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
bool wm_manipulator_select(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
bool wm_manipulator_select_set_ex(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select, bool use_array);
bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C);
void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map);
@ -82,6 +83,11 @@ void wm_manipulatorgrouptype_setup_keymap(
/* -------------------------------------------------------------------- */
/* wmManipulatorMap */
typedef struct wmManipulatorMapSelectState {
struct wmManipulator **items;
int len, len_alloc;
} wmManipulatorMapSelectState;
struct wmManipulatorMap {
struct wmManipulatorMap *next, *prev;
@ -101,10 +107,8 @@ struct wmManipulatorMap {
struct wmManipulator *highlight;
/* User has clicked this manipulator and it gets all input. */
struct wmManipulator *modal;
/* array for all selected manipulators
* TODO check on using BLI_array */
struct wmManipulator **selected;
int selected_len;
/* array for all selected manipulators */
struct wmManipulatorMapSelectState select;
} mmap_context;
};
@ -124,7 +128,10 @@ struct wmManipulatorMapType {
uchar type_update_flag;
};
void wm_manipulatormap_selected_clear(struct wmManipulatorMap *mmap);
bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap, struct wmManipulator ***sel);
void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap);
bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap);
void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract);
void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr);
void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr);
#endif

View File

@ -80,6 +80,76 @@ enum eManipulatorMapUpdateFlags {
};
/* -------------------------------------------------------------------- */
/** \name wmManipulatorMap Selection Array API
*
* Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
*
* \{ */
static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
if (len <= msel->len_alloc) {
return;
}
msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
msel->len_alloc = len;
}
void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
MEM_SAFE_FREE(msel->items);
msel->len = 0;
msel->len_alloc = 0;
}
void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
msel->len -= len_subtract;
if (msel->len <= 0) {
wm_manipulatormap_select_array_clear(mmap);
}
else {
if (msel->len < msel->len_alloc / 2) {
msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
msel->len_alloc = msel->len;
}
}
}
void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
BLI_assert(msel->len <= msel->len_alloc);
if (msel->len == msel->len_alloc) {
msel->len_alloc = (msel->len + 1) * 2;
msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
}
msel->items[msel->len++] = mpr;
}
void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
/* remove manipulator from selected_manipulators array */
for (int i = 0; i < msel->len; i++) {
if (msel->items[i] == mpr) {
for (int j = i; j < (msel->len - 1); j++) {
msel->items[j] = msel->items[j + 1];
}
wm_manipulatormap_select_array_shrink(mmap, 1);
break;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name wmManipulatorMap
*
@ -107,12 +177,6 @@ wmManipulatorMap *WM_manipulatormap_new_from_type(
return mmap;
}
void wm_manipulatormap_selected_clear(wmManipulatorMap *mmap)
{
MEM_SAFE_FREE(mmap->mmap_context.selected);
mmap->mmap_context.selected_len = 0;
}
void wm_manipulatormap_remove(wmManipulatorMap *mmap)
{
if (!mmap)
@ -125,7 +189,7 @@ void wm_manipulatormap_remove(wmManipulatorMap *mmap)
}
BLI_assert(BLI_listbase_is_empty(&mmap->groups));
wm_manipulatormap_selected_clear(mmap);
wm_manipulatormap_select_array_clear(mmap);
MEM_freeN(mmap);
}
@ -137,7 +201,7 @@ const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap)
bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap)
{
return mmap->mmap_context.selected_len != 0;
return mmap->mmap_context.select.len != 0;
}
/**
@ -149,8 +213,8 @@ bool WM_manipulatormap_minmax(
{
if (use_select) {
int i;
for (i = 0; i < mmap->mmap_context.selected_len; i++) {
minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.selected[i]->matrix_basis[3]);
for (i = 0; i < mmap->mmap_context.select.len; i++) {
minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]);
}
return i != 0;
}
@ -544,16 +608,19 @@ void wm_manipulatormaps_handled_modal_update(
* Deselect all selected manipulators in \a mmap.
* \return if selection has changed.
*/
bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap, wmManipulator ***sel)
bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap)
{
if (*sel == NULL || mmap->mmap_context.selected_len == 0)
return false;
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
for (int i = 0; i < mmap->mmap_context.selected_len; i++) {
(*sel)[i]->state &= ~WM_MANIPULATOR_STATE_SELECT;
(*sel)[i] = NULL;
if (msel->items == NULL || msel->len == 0) {
return false;
}
wm_manipulatormap_selected_clear(mmap);
for (int i = 0; i < msel->len; i++) {
wm_manipulator_select_set_ex(mmap, msel->items[i], false, false);
}
wm_manipulatormap_select_array_clear(mmap);
/* always return true, we already checked
* if there's anything to deselect */
@ -570,36 +637,28 @@ BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUS
* \return if selection has changed.
*/
static bool wm_manipulatormap_select_all_intern(
bContext *C, wmManipulatorMap *mmap, wmManipulator ***sel,
const int action)
bContext *C, wmManipulatorMap *mmap)
{
wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
/* GHash is used here to avoid having to loop over all manipulators twice (once to
* get tot_sel for allocating, once for actually selecting). Instead we collect
* selectable manipulators in hash table and use this to get tot_sel and do selection */
GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true);
GHashIterator gh_iter;
int i, *selected_len = &mmap->mmap_context.selected_len;
int i;
bool changed = false;
*selected_len = BLI_ghash_size(hash);
*sel = MEM_reallocN(*sel, sizeof(**sel) * (*selected_len));
wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_size(hash));
GHASH_ITER_INDEX (gh_iter, hash, i) {
wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter);
if ((mpr_iter->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
changed = true;
}
mpr_iter->state |= WM_MANIPULATOR_STATE_SELECT;
if (mpr_iter->type->select) {
mpr_iter->type->select(C, mpr_iter, action);
}
(*sel)[i] = mpr_iter;
BLI_assert(i < (*selected_len));
WM_manipulator_select_set(mmap, mpr_iter, true);
}
/* highlight first manipulator */
wm_manipulatormap_highlight_set(mmap, C, (*sel)[0], (*sel)[0]->highlight_part);
wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part);
BLI_assert(BLI_ghash_size(hash) == msel->len);
BLI_ghash_free(hash, NULL, NULL);
return changed;
@ -613,15 +672,14 @@ static bool wm_manipulatormap_select_all_intern(
*/
bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action)
{
wmManipulator ***sel = &mmap->mmap_context.selected;
bool changed = false;
switch (action) {
case SEL_SELECT:
changed = wm_manipulatormap_select_all_intern(C, mmap, sel, action);
changed = wm_manipulatormap_select_all_intern(C, mmap);
break;
case SEL_DESELECT:
changed = wm_manipulatormap_deselect_all(mmap, sel);
changed = wm_manipulatormap_deselect_all(mmap);
break;
default:
BLI_assert(0);
@ -785,8 +843,8 @@ wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap)
wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len)
{
*r_selected_len = mmap->mmap_context.selected_len;
return mmap->mmap_context.selected;
*r_selected_len = mmap->mmap_context.select.len;
return mmap->mmap_context.select.items;
}
ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap)

View File

@ -56,7 +56,7 @@ typedef void (*wmManipulatorFnMatrixWorldGet)(struct wmManipulator *, float[4
typedef void (*wmManipulatorFnInvoke)(struct bContext *, struct wmManipulator *, const struct wmEvent *);
typedef void (*wmManipulatorFnExit)(struct bContext *, struct wmManipulator *, const bool);
typedef int (*wmManipulatorFnCursorGet)(struct wmManipulator *);
typedef void (*wmManipulatorFnSelect)(struct bContext *, struct wmManipulator *, const int);
typedef void (*wmManipulatorFnSelectRefresh)(struct wmManipulator *);
/* wmManipulatorProperty ('value' type defined by 'wmManipulatorProperty.data_type') */
typedef void (*wmManipulatorPropertyFnGet)(