UI: Rewrite Move to Collection menu #110667
|
@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
Loading…
Reference in New Issue