UI: Rewrite Move to Collection menu #110667

Open
Guillermo Venegas wants to merge 19 commits from guishe/blender:move-to-collection into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
1 changed files with 108 additions and 159 deletions

View File

@ -1915,22 +1915,39 @@ static bool move_to_collection_poll(bContext *C)
}
return ED_operator_objectmode(C);
}
Collection *collection_from_enum_or_scene(const bContext *C, const wmOperator *op)
{
Main *bmain = CTX_data_main(C);
PropertyRNA *prop;
prop = RNA_struct_find_property(op->ptr, "collection");
if (RNA_property_is_set(op->ptr, prop)) {
return static_cast<Collection *>(
BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")));
}
prop = RNA_struct_find_property(op->ptr, "scene");
if (RNA_property_is_set(op->ptr, prop)) {
return static_cast<Scene *>(BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")))
->master_collection;
}
return nullptr;
}
static int move_to_collection_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection_index");
const bool is_link = STREQ(op->idname, "OBJECT_OT_link_to_collection");
const bool is_new = RNA_boolean_get(op->ptr, "is_new");
if (!RNA_property_is_set(op->ptr, prop)) {
Collection *collection = collection_from_enum_or_scene(C, op);
if (!collection) {
BKE_report(op->reports, RPT_ERROR, "No collection selected");
return OPERATOR_CANCELLED;
}
int collection_index = RNA_property_int_get(op->ptr, prop);
Collection *collection = BKE_collection_from_index(scene, collection_index);
if (collection == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Unexpected error, collection not found");
return OPERATOR_CANCELLED;
@ -2013,104 +2030,97 @@ static int move_to_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
/* Name of the active move operation. Used only for menu drawing. */
static const char *operation_name = nullptr;
void move_to_collection_menu_new_item(uiLayout *layout,
const bool scene_collection,
const int enum_index)
{
wmOperatorType *ot = WM_operatortype_find(operation_name, false);
struct MoveToCollectionData {
MoveToCollectionData *next, *prev;
int index;
Collection *collection;
ListBase submenus;
PointerRNA ptr;
wmOperatorType *ot;
};
WM_operator_properties_create_ptr(&ptr, ot);
static int move_to_collection_menus_create(wmOperator *op, MoveToCollectionData *menu)
{
int index = menu->index;
LISTBASE_FOREACH (CollectionChild *, child, &menu->collection->children) {
Collection *collection = child->collection;
MoveToCollectionData *submenu = MEM_cnew<MoveToCollectionData>(__func__);
BLI_addtail(&menu->submenus, submenu);
submenu->collection = collection;
submenu->index = ++index;
index = move_to_collection_menus_create(op, submenu);
submenu->ot = op->type;
}
return index;
}
RNA_boolean_set(&ptr, "is_new", true);
static void move_to_collection_menus_free_recursive(MoveToCollectionData *menu)
{
LISTBASE_FOREACH (MoveToCollectionData *, submenu, &menu->submenus) {
move_to_collection_menus_free_recursive(submenu);
}
BLI_freelistN(&menu->submenus);
}
static void move_to_collection_menus_free(MoveToCollectionData **menu)
{
if (*menu == nullptr) {
return;
}
move_to_collection_menus_free_recursive(*menu);
MEM_freeN(*menu);
*menu = nullptr;
}
static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *menu_v)
{
MoveToCollectionData *menu = static_cast<MoveToCollectionData *>(menu_v);
const char *name = BKE_collection_ui_name_get(menu->collection);
WM_operator_properties_create_ptr(&menu->ptr, menu->ot);
RNA_int_set(&menu->ptr, "collection_index", menu->index);
RNA_boolean_set(&menu->ptr, "is_new", true);
RNA_enum_set(&ptr, scene_collection ? "scene" : "collection", enum_index);
uiItemFullO_ptr(layout,
menu->ot,
ot,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "New Collection"),
ICON_ADD,
static_cast<IDProperty *>(menu->ptr.data),
static_cast<IDProperty *>(ptr.data),
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
nullptr);
uiItemS(layout);
Scene *scene = CTX_data_scene(C);
const int icon = (menu->collection == scene->master_collection) ?
ICON_SCENE_DATA :
UI_icon_color_from_collection(menu->collection);
uiItemIntO(layout, name, icon, menu->ot->idname, "collection_index", menu->index);
LISTBASE_FOREACH (MoveToCollectionData *, submenu, &menu->submenus) {
move_to_collection_menus_items(layout, submenu);
}
}
static void move_to_collection_menus_items(uiLayout *layout, MoveToCollectionData *menu)
void move_to_collection_menu_item(uiLayout *layout,
Collection *collection,
const bool scene_collection,
const int enum_index)
{
const int icon = UI_icon_color_from_collection(menu->collection);
const int icon = scene_collection ? ICON_SCENE_DATA : UI_icon_color_from_collection(collection);
const char *name = BKE_collection_ui_name_get(collection);
uiItemEnumO(
layout, operation_name, name, icon, scene_collection ? "scene" : "collection", enum_index);
}
if (BLI_listbase_is_empty(&menu->submenus)) {
uiItemIntO(layout,
menu->collection->id.name + 2,
icon,
menu->ot->idname,
"collection_index",
menu->index);
void move_to_collection_menu_items(bContext *C, uiLayout *layout, void *arg)
{
Collection *collection = static_cast<Collection *>(arg);
const Scene *scene = CTX_data_scene(C);
Main *bmain = CTX_data_main(C);
int enum_index = 0;
bool scene_collection = scene->master_collection == collection;
if (scene_collection) {
enum_index = BLI_findindex(&bmain->scenes, scene);
}
else {
uiItemMenuF(layout, menu->collection->id.name + 2, icon, move_to_collection_menu_create, menu);
enum_index = BLI_findindex(&bmain->collections, collection);
}
move_to_collection_menu_item(layout, collection, scene_collection, enum_index);
uiItemS(layout);
move_to_collection_menu_new_item(layout, scene_collection, enum_index);
if (collection->children.first) {
uiItemS(layout);
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
const char *name = BKE_collection_ui_name_get(child->collection);
const int icon = UI_icon_color_from_collection(child->collection);
uiItemMenuF(layout, name, icon, move_to_collection_menu_items, child->collection);
}
}
/* This is allocated statically because we need this available for the menus creation callback. */
static MoveToCollectionData *master_collection_menu = nullptr;
static int move_to_collection_menu(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Collection *master_collection = scene->master_collection;
uiPopupMenu *pup;
uiLayout *layout;
/* Build the menus. */
const char *title = CTX_IFACE_(op->type->translation_context, op->type->name);
pup = UI_popup_menu_begin(C, title, ICON_NONE);
layout = UI_popup_menu_layout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
uiItemS(layout);
operation_name = op->type->idname;
move_to_collection_menu_items(C, layout, static_cast<void *>(master_collection));
UI_popup_menu_end(C, pup);
return OPERATOR_INTERFACE;
}
static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Scene *scene = CTX_data_scene(C);
ListBase objects = selected_objects_get(C);
if (BLI_listbase_is_empty(&objects)) {
@ -2119,21 +2129,15 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent
}
BLI_freelistN(&objects);
/* Reset the menus data for the current master collection, and free previously allocated data. */
move_to_collection_menus_free(&master_collection_menu);
PropertyRNA *prop;
prop = RNA_struct_find_property(op->ptr, "collection_index");
if (RNA_property_is_set(op->ptr, prop)) {
int collection_index = RNA_property_int_get(op->ptr, prop);
Collection *collection = collection_from_enum_or_scene(C, op);
if (collection) {
if (RNA_boolean_get(op->ptr, "is_new")) {
prop = RNA_struct_find_property(op->ptr, "new_collection_name");
if (!RNA_property_is_set(op->ptr, prop)) {
char name[MAX_NAME];
Collection *collection;
collection = BKE_collection_from_index(scene, collection_index);
BKE_collection_new_name_get(collection, name);
RNA_property_string_set(op->ptr, prop, name);
@ -2142,38 +2146,7 @@ static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent
}
return move_to_collection_exec(C, op);
}
Collection *master_collection = scene->master_collection;
/* We need the data to be allocated so it's available during menu drawing.
* Technically we could use #wmOperator.customdata. However there is no free callback
* called to an operator that exit with OPERATOR_INTERFACE to launch a menu.
*
* So we are left with a memory that will necessarily leak. It's a small leak though. */
if (master_collection_menu == nullptr) {
master_collection_menu = MEM_cnew<MoveToCollectionData>(
"MoveToCollectionData menu - expected eventual memleak");
}
master_collection_menu->collection = master_collection;
master_collection_menu->ot = op->type;
move_to_collection_menus_create(op, master_collection_menu);
uiPopupMenu *pup;
uiLayout *layout;
/* Build the menus. */
const char *title = CTX_IFACE_(op->type->translation_context, op->type->name);
pup = UI_popup_menu_begin(C, title, ICON_NONE);
layout = UI_popup_menu_layout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
move_to_collection_menu_create(C, layout, master_collection_menu);
UI_popup_menu_end(C, pup);
return OPERATOR_INTERFACE;
return move_to_collection_menu(C, op);
}
void OBJECT_OT_move_to_collection(wmOperatorType *ot)
@ -2193,15 +2166,21 @@ void OBJECT_OT_move_to_collection(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
prop = RNA_def_int(ot->srna,
"collection_index",
COLLECTION_INVALID_INDEX,
COLLECTION_INVALID_INDEX,
INT_MAX,
"Collection Index",
"Index of the collection to move to",
0,
INT_MAX);
prop = RNA_def_enum(ot->srna, "collection", rna_enum_dummy_NULL_items, 0, "Collection", "");
RNA_def_enum_funcs(prop, RNA_collection_itemf);
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
prop = RNA_def_enum(ot->srna,
"scene",
rna_enum_dummy_NULL_items,
0,
"Scene",
"Uses scenes master collection as target collection");
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
RNA_def_enum_funcs(prop, RNA_scene_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
prop = RNA_def_boolean(ot->srna, "is_new", false, "New", "Move objects to a new collection");
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
@ -2217,41 +2196,11 @@ void OBJECT_OT_move_to_collection(wmOperatorType *ot)
void OBJECT_OT_link_to_collection(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
OBJECT_OT_move_to_collection(ot);
ot->name = "Link to Collection";
ot->description = "Link objects to a collection";
ot->idname = "OBJECT_OT_link_to_collection";
/* api callbacks */
ot->exec = move_to_collection_exec;
ot->invoke = move_to_collection_invoke;
ot->poll = move_to_collection_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
prop = RNA_def_int(ot->srna,
"collection_index",
COLLECTION_INVALID_INDEX,
COLLECTION_INVALID_INDEX,
INT_MAX,
"Collection Index",
"Index of the collection to move to",
0,
INT_MAX);
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
prop = RNA_def_boolean(ot->srna, "is_new", false, "New", "Move objects to a new collection");
RNA_def_property_flag(prop, PropertyFlag(PROP_SKIP_SAVE | PROP_HIDDEN));
prop = RNA_def_string(ot->srna,
"new_collection_name",
nullptr,
MAX_NAME,
"Name",
"Name of the newly added collection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
ot->prop = prop;
}
/** \} */