UI: Redesigned data-block selectors

The previous design is rather old and has a couple of problems:

* Scalability: The current solution of adding little icon buttons next to the
  data-block name field doesn't scale well. It only works if there's a small
  number of operations. We need to be able to place more items there for better
  data-block management. Especially with the introduction of library overrides.
* Discoverability: It's not obvious what some of the icons do. They appear and
  disappear, but it's not obvious why some are available at times and others
  not.
* Unclear Status: Currently their library status (linked, indirectly linked,
  broken link, library override) isn't really clear.
* Unusual behavior: Some of the icon buttons allow Shift or Ctrl clicking to
  invoke alternative behaviors. This is not a usual pattern in Blender.

This patch does the following changes:

* Adds a menu to the right of the name button to access all kinds of operations
  (create, delete, unlink, user management, library overrides, etc).
* Make good use of the "disabled hint" for tooltips, to explain why buttons are
  disabled. The UI team wants to establish this as a good practise.
* Use superimposed icons for duplicate and unlink, rather than extra buttons
  (uses less space, looks less distracting and is a nice + consistent design
  language).
* Remove fake user and user count button, they are available from the menu now.
* Support tooltips for superimposed icons (committed mouse hover feedback to
  master already).
* Slightly increase size of the name button - it was already a bit small
  before, and the move from real buttons to superimposed icons reduces usable
  space for the name itself.
* More clearly differentiate between duplicate and creating a new data-block.
  The latter is only available in the menu.
* Display library status icon on the left (linked, missing library, overridden,
  asset)
* Disables "Make Single User" button - in review we weren't sure if there are
  good use-cases for it, so better to see if we can remove it.

Note that I do expect some aspects of this design to change still. I think some
changes are problematic, but others disagreed. I will open a feedback thread on
devtalk to see what others think.

Differential Revision: https://developer.blender.org/D8554

Reviewed by: Bastien Montagne

Design discussed and agreed on with the UI team, also see T79959.
This commit is contained in:
Julian Eisel
2020-12-18 18:12:11 +01:00
parent 7bee1489c1
commit 2250b5cefe
36 changed files with 1473 additions and 690 deletions

View File

@@ -1148,7 +1148,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel):
split = layout.split(factor=0.65) split = layout.split(factor=0.65)
if ob: if ob:
split.template_ID(ob, "active_material", new="material.new") split.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate")
row = split.row() row = split.row()
if slot: if slot:

View File

@@ -133,7 +133,7 @@ class EEVEE_MATERIAL_PT_context_material(MaterialButtonsPanel, Panel):
row = layout.row() row = layout.row()
if ob: if ob:
row.template_ID(ob, "active_material", new="material.new") row.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate")
if slot: if slot:
icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA' icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'

View File

@@ -51,7 +51,7 @@ class WORLD_PT_context_world(WorldButtonsPanel, Panel):
space = context.space_data space = context.space_data
if scene: if scene:
layout.template_ID(scene, "world", new="world.new") layout.template_ID(scene, "world", new="world.new", duplicate="world.duplicate")
elif world: elif world:
layout.template_ID(space, "pin_id") layout.template_ID(space, "pin_id")

View File

@@ -248,7 +248,7 @@ class DOPESHEET_HT_editor_buttons:
layout.separator_spacer() layout.separator_spacer()
layout.template_ID(st, "action", new="action.new", unlink="action.unlink") layout.template_ID(st, "action", new="action.new", duplicate="action.duplicate_assign", unlink="action.unlink")
# Layer management # Layer management
if st.mode == 'GPENCIL': if st.mode == 'GPENCIL':

View File

@@ -52,10 +52,11 @@ void ED_spacedata_id_remap(struct ScrArea *area,
struct ID *old_id, struct ID *old_id,
struct ID *new_id); struct ID *new_id);
void ED_OT_flush_edits(struct wmOperatorType *ot);
void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot); void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot);
void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot); void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot);
void ED_operatortypes_edutils(void);
/* ************** XXX OLD CRUFT WARNING ************* */ /* ************** XXX OLD CRUFT WARNING ************* */
void apply_keyb_grid( void apply_keyb_grid(

View File

@@ -76,6 +76,7 @@ struct wmWindow;
typedef struct uiBlock uiBlock; typedef struct uiBlock uiBlock;
typedef struct uiBut uiBut; typedef struct uiBut uiBut;
typedef struct uiButExtraOpIcon uiButExtraOpIcon;
typedef struct uiLayout uiLayout; typedef struct uiLayout uiLayout;
typedef struct uiPopupBlockHandle uiPopupBlockHandle; typedef struct uiPopupBlockHandle uiPopupBlockHandle;
@@ -1381,13 +1382,16 @@ typedef struct uiStringInfo {
/* Note: Expects pointers to uiStringInfo structs as parameters. /* Note: Expects pointers to uiStringInfo structs as parameters.
* Will fill them with translated strings, when possible. * Will fill them with translated strings, when possible.
* Strings in uiStringInfo must be MEM_freeN'ed by caller. */ * Strings in uiStringInfo must be MEM_freeN'ed by caller. */
void UI_but_string_info_get(struct bContext *C, uiBut *but, ...) ATTR_SENTINEL(0); void UI_but_string_info_get(struct bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, ...)
ATTR_SENTINEL(0);
/* Edit i18n stuff. */ /* Edit i18n stuff. */
/* Name of the main py op from i18n addon. */ /* Name of the main py op from i18n addon. */
#define EDTSRC_I18N_OP_NAME "UI_OT_edittranslation" #define EDTSRC_I18N_OP_NAME "UI_OT_edittranslation"
/** /**
* TODO This is old stuff, only used by templateID. Should be cleaned up.
*
* Special Buttons * Special Buttons
* *
* Buttons with a more specific purpose: * Buttons with a more specific purpose:
@@ -1405,14 +1409,16 @@ enum {
UI_ID_ALONE = 1 << 4, UI_ID_ALONE = 1 << 4,
UI_ID_OPEN = 1 << 3, UI_ID_OPEN = 1 << 3,
UI_ID_DELETE = 1 << 5, UI_ID_DELETE = 1 << 5,
UI_ID_LOCAL = 1 << 6, UI_ID_MAKE_LOCAL = 1 << 6,
UI_ID_AUTO_NAME = 1 << 7, UI_ID_LIB_OVERRIDE_ADD = 1 << 7,
UI_ID_FAKE_USER = 1 << 8, UI_ID_AUTO_NAME = 1 << 8,
UI_ID_PIN = 1 << 9, UI_ID_PIN = 1 << 9,
UI_ID_PREVIEWS = 1 << 10, UI_ID_PREVIEWS = 1 << 10,
UI_ID_OVERRIDE = 1 << 11, UI_ID_LIB_OVERRIDE_REMOVE = 1 << 11,
UI_ID_LIB_OVERRIDE_RESET = 1 << 12,
UI_ID_FULL = UI_ID_RENAME | UI_ID_BROWSE | UI_ID_ADD_NEW | UI_ID_OPEN | UI_ID_ALONE | UI_ID_FULL = UI_ID_RENAME | UI_ID_BROWSE | UI_ID_ADD_NEW | UI_ID_OPEN | UI_ID_ALONE |
UI_ID_DELETE | UI_ID_LOCAL, UI_ID_DELETE | UI_ID_MAKE_LOCAL | UI_ID_LIB_OVERRIDE_ADD |
UI_ID_LIB_OVERRIDE_REMOVE | UI_ID_LIB_OVERRIDE_RESET,
}; };
/** /**
@@ -1658,10 +1664,12 @@ void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN);
void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, void *arg); void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, void *arg);
PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, struct uiButExtraOpIcon *UI_but_extra_operator_icon_add(uiBut *but,
const char *opname, const char *opname,
short opcontext, short opcontext,
int icon); int icon);
struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon);
PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon);
/* Autocomplete /* Autocomplete
* *
@@ -1963,6 +1971,7 @@ void uiTemplateID(uiLayout *layout,
struct PointerRNA *ptr, struct PointerRNA *ptr,
const char *propname, const char *propname,
const char *newop, const char *newop,
const char *duplicateop,
const char *openop, const char *openop,
const char *unlinkop, const char *unlinkop,
int filter, int filter,
@@ -2567,6 +2576,11 @@ struct ARegion *UI_tooltip_create_from_button(struct bContext *C,
struct ARegion *butregion, struct ARegion *butregion,
uiBut *but, uiBut *but,
bool is_label); bool is_label);
struct ARegion *UI_tooltip_create_from_button_or_extra_icon(struct bContext *C,
struct ARegion *butregion,
uiBut *but,
uiButExtraOpIcon *extra_icon,
bool is_label);
struct ARegion *UI_tooltip_create_from_gizmo(struct bContext *C, struct wmGizmo *gz); struct ARegion *UI_tooltip_create_from_gizmo(struct bContext *C, struct wmGizmo *gz);
void UI_tooltip_free(struct bContext *C, struct bScreen *screen, struct ARegion *region); void UI_tooltip_free(struct bContext *C, struct bScreen *screen, struct ARegion *region);

View File

@@ -1167,16 +1167,21 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str
* \{ */ * \{ */
static bool ui_but_event_operator_string_from_operator(const bContext *C, static bool ui_but_event_operator_string_from_operator(const bContext *C,
uiBut *but, wmOperatorCallParams *op_call_params,
char *buf, char *buf,
const size_t buf_len) const size_t buf_len)
{ {
BLI_assert(but->optype != NULL); BLI_assert(op_call_params->optype != NULL);
bool found = false; bool found = false;
IDProperty *prop = (but->opptr) ? but->opptr->data : NULL; IDProperty *prop = (op_call_params->opptr) ? op_call_params->opptr->data : NULL;
if (WM_key_event_operator_string( if (WM_key_event_operator_string(C,
C, but->optype->idname, but->opcontext, prop, true, buf, buf_len)) { op_call_params->optype->idname,
op_call_params->opcontext,
prop,
true,
buf,
buf_len)) {
found = true; found = true;
} }
return found; return found;
@@ -1253,15 +1258,22 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C,
return found; return found;
} }
static bool ui_but_event_operator_string(const bContext *C, static bool ui_but_event_operator_string(
uiBut *but, const bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, char *buf, const size_t buf_len)
char *buf,
const size_t buf_len)
{ {
bool found = false; bool found = false;
wmOperatorType *extra_icon_optype = UI_but_extra_operator_icon_optype_get(extra_icon);
if (but->optype != NULL) { if (extra_icon_optype) {
found = ui_but_event_operator_string_from_operator(C, but, buf, buf_len); found = ui_but_event_operator_string_from_operator(C, extra_icon->optype_params, buf, buf_len);
}
else if (but->optype != NULL) {
found = ui_but_event_operator_string_from_operator(
C,
&(wmOperatorCallParams){
.optype = but->optype, .opptr = but->opptr, .opcontext = but->opcontext},
buf,
buf_len);
} }
else if (UI_but_menutype_get(but) != NULL) { else if (UI_but_menutype_get(but) != NULL) {
found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len); found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len);
@@ -1564,7 +1576,7 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
continue; continue;
} }
if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) { if (ui_but_event_operator_string(C, but, NULL, buf, sizeof(buf))) {
ui_but_add_shortcut(but, buf, false); ui_but_add_shortcut(but, buf, false);
} }
else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) { else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) {
@@ -1605,12 +1617,12 @@ typedef enum PredefinedExtraOpIconType {
PREDEFINED_EXTRA_OP_ICON_EYEDROPPER, PREDEFINED_EXTRA_OP_ICON_EYEDROPPER,
} PredefinedExtraOpIconType; } PredefinedExtraOpIconType;
static PointerRNA *ui_but_extra_operator_icon_add_ptr(uiBut *but, static uiButExtraOpIcon *ui_but_extra_operator_icon_add_ptr(uiBut *but,
wmOperatorType *optype, wmOperatorType *optype,
short opcontext, short opcontext,
int icon) int icon)
{ {
uiButExtraOpIcon *extra_op_icon = MEM_mallocN(sizeof(*extra_op_icon), __func__); uiButExtraOpIcon *extra_op_icon = MEM_callocN(sizeof(*extra_op_icon), __func__);
extra_op_icon->icon = (BIFIconID)icon; extra_op_icon->icon = (BIFIconID)icon;
extra_op_icon->optype_params = MEM_callocN(sizeof(*extra_op_icon->optype_params), extra_op_icon->optype_params = MEM_callocN(sizeof(*extra_op_icon->optype_params),
@@ -1625,13 +1637,15 @@ static PointerRNA *ui_but_extra_operator_icon_add_ptr(uiBut *but,
BLI_addtail(&but->extra_op_icons, extra_op_icon); BLI_addtail(&but->extra_op_icons, extra_op_icon);
return extra_op_icon->optype_params->opptr; return extra_op_icon;
} }
static void ui_but_extra_operator_icon_free(uiButExtraOpIcon *extra_icon) static void ui_but_extra_operator_icon_free(uiButExtraOpIcon *extra_icon)
{ {
WM_operator_properties_free(extra_icon->optype_params->opptr); if (extra_icon->optype_params->opptr) {
MEM_freeN(extra_icon->optype_params->opptr); WM_operator_properties_free(extra_icon->optype_params->opptr);
MEM_freeN(extra_icon->optype_params->opptr);
}
MEM_freeN(extra_icon->optype_params); MEM_freeN(extra_icon->optype_params);
MEM_freeN(extra_icon); MEM_freeN(extra_icon);
} }
@@ -1644,18 +1658,25 @@ void ui_but_extra_operator_icons_free(uiBut *but)
BLI_listbase_clear(&but->extra_op_icons); BLI_listbase_clear(&but->extra_op_icons);
} }
PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, uiButExtraOpIcon *UI_but_extra_operator_icon_add(uiBut *but,
const char *opname, const char *opname,
short opcontext, short opcontext,
int icon) int icon)
{ {
wmOperatorType *optype = WM_operatortype_find(opname, false); wmOperatorType *optype = WM_operatortype_find(opname, false);
if (optype) { BLI_assert(optype);
return ui_but_extra_operator_icon_add_ptr(but, optype, opcontext, icon); return ui_but_extra_operator_icon_add_ptr(but, optype, opcontext, icon);
} }
return NULL; PointerRNA *UI_but_extra_operator_icon_opptr_get(uiButExtraOpIcon *extra_icon)
{
return extra_icon->optype_params->opptr;
}
wmOperatorType *UI_but_extra_operator_icon_optype_get(uiButExtraOpIcon *extra_icon)
{
return extra_icon ? extra_icon->optype_params->optype : NULL;
} }
static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but)
@@ -6818,7 +6839,7 @@ void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN)
but->hold_argN = argN; but->hold_argN = argN;
} }
void UI_but_string_info_get(bContext *C, uiBut *but, ...) void UI_but_string_info_get(bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, ...)
{ {
va_list args; va_list args;
uiStringInfo *si; uiStringInfo *si;
@@ -6827,13 +6848,19 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
int totitems; int totitems;
bool free_items = false; bool free_items = false;
va_start(args, but); wmOperatorType *extra_icon_optype = UI_but_extra_operator_icon_optype_get(extra_icon);
wmOperatorType *optype = extra_icon ? extra_icon_optype : but->optype;
/* Don't query RNA data when the extra-icon overrides the button. */
PropertyRNA *rnaprop = extra_icon ? NULL : but->rnaprop;
va_start(args, extra_icon);
while ((si = (uiStringInfo *)va_arg(args, void *))) { while ((si = (uiStringInfo *)va_arg(args, void *))) {
uiStringInfoType type = si->type; uiStringInfoType type = si->type;
char *tmp = NULL; char *tmp = NULL;
if (type == BUT_GET_LABEL) { if (type == BUT_GET_LABEL) {
if (but->str && but->str[0]) { if (but->str && but->str[0] && !extra_icon) {
const char *str_sep; const char *str_sep;
size_t str_len; size_t str_len;
@@ -6863,16 +6890,16 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
} }
if (type == BUT_GET_RNAPROP_IDENTIFIER) { if (type == BUT_GET_RNAPROP_IDENTIFIER) {
if (but->rnaprop) { if (rnaprop) {
tmp = BLI_strdup(RNA_property_identifier(but->rnaprop)); tmp = BLI_strdup(RNA_property_identifier(rnaprop));
} }
} }
else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) { else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
if (but->rnaprop && but->rnapoin.data) { if (rnaprop && but->rnapoin.data) {
tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type)); tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
} }
else if (but->optype) { else if (optype) {
tmp = BLI_strdup(but->optype->idname); tmp = BLI_strdup(optype->idname);
} }
else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN)) { else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN)) {
MenuType *mt = UI_but_menutype_get(but); MenuType *mt = UI_but_menutype_get(but);
@@ -6888,23 +6915,25 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
} }
} }
else if (ELEM(type, BUT_GET_RNA_LABEL, BUT_GET_RNA_TIP)) { else if (ELEM(type, BUT_GET_RNA_LABEL, BUT_GET_RNA_TIP)) {
if (but->rnaprop) { if (rnaprop) {
if (type == BUT_GET_RNA_LABEL) { if (type == BUT_GET_RNA_LABEL) {
tmp = BLI_strdup(RNA_property_ui_name(but->rnaprop)); tmp = BLI_strdup(RNA_property_ui_name(rnaprop));
} }
else { else {
const char *t = RNA_property_ui_description(but->rnaprop); const char *t = RNA_property_ui_description(rnaprop);
if (t && t[0]) { if (t && t[0]) {
tmp = BLI_strdup(t); tmp = BLI_strdup(t);
} }
} }
} }
else if (but->optype) { else if (optype) {
PointerRNA *opptr = extra_icon_optype ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
but->opptr;
if (type == BUT_GET_RNA_LABEL) { if (type == BUT_GET_RNA_LABEL) {
tmp = BLI_strdup(WM_operatortype_name(but->optype, but->opptr)); tmp = BLI_strdup(WM_operatortype_name(optype, opptr));
} }
else { else {
tmp = WM_operatortype_description(C, but->optype, but->opptr); tmp = WM_operatortype_description(C, optype, opptr);
} }
} }
else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) { else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN, UI_BTYPE_POPOVER)) {
@@ -6956,11 +6985,11 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
} }
else if (type == BUT_GET_RNA_LABEL_CONTEXT) { else if (type == BUT_GET_RNA_LABEL_CONTEXT) {
const char *_tmp = BLT_I18NCONTEXT_DEFAULT; const char *_tmp = BLT_I18NCONTEXT_DEFAULT;
if (but->rnaprop) { if (rnaprop) {
_tmp = RNA_property_translation_context(but->rnaprop); _tmp = RNA_property_translation_context(rnaprop);
} }
else if (but->optype) { else if (optype) {
_tmp = RNA_struct_translation_context(but->optype->srna); _tmp = RNA_struct_translation_context(optype->srna);
} }
else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN)) { else if (ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_PULLDOWN)) {
MenuType *mt = UI_but_menutype_get(but); MenuType *mt = UI_but_menutype_get(but);
@@ -6979,16 +7008,16 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
int value = 0; int value = 0;
/* get the enum property... */ /* get the enum property... */
if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) { if (rnaprop && RNA_property_type(rnaprop) == PROP_ENUM) {
/* enum property */ /* enum property */
ptr = &but->rnapoin; ptr = &but->rnapoin;
prop = but->rnaprop; prop = rnaprop;
value = (ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_TAB)) ? (int)but->hardmax : value = (ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_TAB)) ? (int)but->hardmax :
(int)ui_but_value_get(but); (int)ui_but_value_get(but);
} }
else if (but->optype) { else if (optype) {
PointerRNA *opptr = UI_but_operator_ptr_get(but); PointerRNA *opptr = extra_icon_optype ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
wmOperatorType *ot = but->optype; UI_but_operator_ptr_get(but);
/* so the context is passed to itemf functions */ /* so the context is passed to itemf functions */
WM_operator_properties_sanitize(opptr, false); WM_operator_properties_sanitize(opptr, false);
@@ -6998,11 +7027,11 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
* operator menus in the Anim Editors will show tooltips for the different * operator menus in the Anim Editors will show tooltips for the different
* operations instead of the meaningless generic operator tooltip * operations instead of the meaningless generic operator tooltip
*/ */
if (ot->prop && RNA_property_type(ot->prop) == PROP_ENUM) { if (optype->prop && RNA_property_type(optype->prop) == PROP_ENUM) {
if (RNA_struct_contains_property(opptr, ot->prop)) { if (RNA_struct_contains_property(opptr, optype->prop)) {
ptr = opptr; ptr = opptr;
prop = ot->prop; prop = optype->prop;
value = RNA_property_enum_get(opptr, ot->prop); value = RNA_property_enum_get(opptr, optype->prop);
} }
} }
} }
@@ -7035,7 +7064,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
else if (type == BUT_GET_OP_KEYMAP) { else if (type == BUT_GET_OP_KEYMAP) {
if (!ui_block_is_menu(but->block)) { if (!ui_block_is_menu(but->block)) {
char buf[128]; char buf[128];
if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) { if (ui_but_event_operator_string(C, but, extra_icon, buf, sizeof(buf))) {
tmp = BLI_strdup(buf); tmp = BLI_strdup(buf);
} }
} }

View File

@@ -509,7 +509,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
uiStringInfo label = {BUT_GET_LABEL, NULL}; uiStringInfo label = {BUT_GET_LABEL, NULL};
/* highly unlikely getting the label ever fails */ /* highly unlikely getting the label ever fails */
UI_but_string_info_get(C, but, &label, NULL); UI_but_string_info_get(C, but, NULL, &label, NULL);
pup = UI_popup_menu_begin(C, label.strinfo ? label.strinfo : "", ICON_NONE); pup = UI_popup_menu_begin(C, label.strinfo ? label.strinfo : "", ICON_NONE);
layout = UI_popup_menu_layout(pup); layout = UI_popup_menu_layout(pup);

View File

@@ -334,6 +334,7 @@ typedef struct uiHandleButtonData {
int retval; int retval;
/* booleans (could be made into flags) */ /* booleans (could be made into flags) */
bool cancel, escapecancel; bool cancel, escapecancel;
bool skip_undo_push;
bool applied, applied_interactive; bool applied, applied_interactive;
bool changed_cursor; bool changed_cursor;
wmTimer *flashtimer; wmTimer *flashtimer;
@@ -820,7 +821,9 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
/* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */
static void ui_apply_but_undo(uiBut *but) static void ui_apply_but_undo(uiBut *but)
{ {
if (but->flag & UI_BUT_UNDO) { const bool force_skip_undo = (but->active && but->active->skip_undo_push);
if (but->flag & UI_BUT_UNDO && !force_skip_undo) {
const char *str = NULL; const char *str = NULL;
size_t str_len_clip = SIZE_MAX - 1; size_t str_len_clip = SIZE_MAX - 1;
bool skip_undo = false; bool skip_undo = false;
@@ -2866,7 +2869,8 @@ void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but)
but->active->str[0] = 0; but->active->str[0] = 0;
ui_apply_but_TEX(C, but, but->active); ui_apply_but_TEX(C, but, but->active);
button_activate_state(C, but, BUTTON_STATE_EXIT); /* use onfree event so undo is handled by caller and apply is already done above */
button_activate_exit((bContext *)C, but, but->active, false, true);
} }
static void ui_textedit_string_ensure_max_length(uiBut *but, uiHandleButtonData *data, int maxlen) static void ui_textedit_string_ensure_max_length(uiBut *but, uiHandleButtonData *data, int maxlen)
@@ -4011,16 +4015,38 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
ED_region_tag_redraw(data->region); ED_region_tag_redraw(data->region);
} }
static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon) static void ui_but_extra_operator_icon_apply_func(uiBut *but, uiButExtraOpIcon *op_icon)
{ {
if (but->active->interactive) { if (ui_afterfunc_check(but->block, but)) {
ui_apply_but(C, but->block, but, but->active, true); uiAfterFunc *after = ui_afterfunc_new();
after->optype = op_icon->optype_params->optype;
after->opcontext = op_icon->optype_params->opcontext;
after->opptr = op_icon->optype_params->opptr;
if (but->context) {
after->context = CTX_store_copy(but->context);
}
/* Ownership moved, don't let the UI code free it. */
op_icon->optype_params->opptr = NULL;
} }
}
static void ui_but_extra_operator_icon_apply(bContext *C,
uiBut *but,
uiHandleButtonData *data,
uiButExtraOpIcon *op_icon)
{
button_activate_state(C, but, BUTTON_STATE_EXIT); button_activate_state(C, but, BUTTON_STATE_EXIT);
WM_operator_name_call_ptr(C, ui_apply_but(C, but->block, but, data, true);
op_icon->optype_params->optype,
op_icon->optype_params->opcontext, data->postbut = but;
op_icon->optype_params->opptr); data->posttype = BUTTON_ACTIVATE_OVER;
/* Leave undo up to the operator. */
data->skip_undo_push = true;
ui_but_extra_operator_icon_apply_func(but, op_icon);
/* Force recreation of extra operator icons (pseudo update). */ /* Force recreation of extra operator icons (pseudo update). */
ui_but_extra_operator_icons_free(but); ui_but_extra_operator_icons_free(but);
@@ -4219,7 +4245,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C,
ED_region_tag_redraw(data->region); ED_region_tag_redraw(data->region);
button_tooltip_timer_reset(C, but); button_tooltip_timer_reset(C, but);
ui_but_extra_operator_icon_apply(C, but, op_icon); ui_but_extra_operator_icon_apply(C, but, data, op_icon);
/* Note: 'but', 'data' may now be freed, don't access. */ /* Note: 'but', 'data' may now be freed, don't access. */
return true; return true;
@@ -7854,7 +7880,10 @@ static ARegion *ui_but_tooltip_init(
uiBut *but = UI_region_active_but_get(region); uiBut *but = UI_region_active_but_get(region);
*r_exit_on_event = false; *r_exit_on_event = false;
if (but) { if (but) {
return UI_tooltip_create_from_button(C, region, but, is_label); uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get(
but, but->active, CTX_wm_window(C)->eventstate);
return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label);
} }
return NULL; return NULL;
} }

View File

@@ -1521,6 +1521,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
UI_but_string_info_get(C, UI_but_string_info_get(C,
but, but,
NULL,
&but_label, &but_label,
&rna_label, &rna_label,
&enum_label, &enum_label,

View File

@@ -534,7 +534,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
{ {
uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
UI_but_string_info_get(C, but, &op_keymap, NULL); UI_but_string_info_get(C, but, NULL, &op_keymap, NULL);
shortcut = op_keymap.strinfo; shortcut = op_keymap.strinfo;
} }
@@ -764,7 +764,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
return data; return data;
} }
static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) static uiTooltipData *ui_tooltip_data_from_button(bContext *C,
uiBut *but,
uiButExtraOpIcon *extra_icon)
{ {
uiStringInfo but_label = {BUT_GET_LABEL, NULL}; uiStringInfo but_label = {BUT_GET_LABEL, NULL};
uiStringInfo but_tip = {BUT_GET_TIP, NULL}; uiStringInfo but_tip = {BUT_GET_TIP, NULL};
@@ -777,20 +779,29 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
char buf[512]; char buf[512];
wmOperatorType *extra_icon_optype = UI_but_extra_operator_icon_optype_get(extra_icon);
wmOperatorType *optype = extra_icon ? extra_icon_optype : but->optype;
/* create tooltip data */ /* create tooltip data */
uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
UI_but_string_info_get(C, if (extra_icon) {
but, UI_but_string_info_get(C, but, extra_icon, &but_label, &but_tip, &op_keymap, NULL);
&but_label, }
&but_tip, else {
&enum_label, UI_but_string_info_get(C,
&enum_tip, but,
&op_keymap, NULL,
&prop_keymap, &but_label,
&rna_struct, &but_tip,
&rna_prop, &enum_label,
NULL); &enum_tip,
&op_keymap,
&prop_keymap,
&rna_struct,
&rna_prop,
NULL);
}
/* Tip Label (only for buttons not already showing the label). /* Tip Label (only for buttons not already showing the label).
* Check prefix instead of comparing because the button may include the shortcut. */ * Check prefix instead of comparing because the button may include the shortcut. */
@@ -923,15 +934,16 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
} }
} }
} }
else if (but->optype) { else if (optype) {
PointerRNA *opptr; PointerRNA *opptr = extra_icon_optype ?
char *str; UI_but_extra_operator_icon_opptr_get(extra_icon) :
opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */ UI_but_operator_ptr_get(
but); /* allocated when needed, the button owns it */
/* so the context is passed to fieldf functions (some py fieldf functions use it) */ /* so the context is passed to fieldf functions (some py fieldf functions use it) */
WM_operator_properties_sanitize(opptr, false); WM_operator_properties_sanitize(opptr, false);
str = ui_tooltip_text_python_from_op(C, but->optype, opptr); char *str = ui_tooltip_text_python_from_op(C, optype, opptr);
/* operator info */ /* operator info */
if (U.flag & USER_TOOLTIPS_PYTHON) { if (U.flag & USER_TOOLTIPS_PYTHON) {
@@ -958,7 +970,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
disabled_msg = CTX_wm_operator_poll_msg_get(C); disabled_msg = CTX_wm_operator_poll_msg_get(C);
} }
/* alternatively, buttons can store some reasoning too */ /* alternatively, buttons can store some reasoning too */
else if (but->disabled_info) { if (!disabled_msg && but->disabled_info) {
disabled_msg = TIP_(but->disabled_info); disabled_msg = TIP_(but->disabled_info);
} }
@@ -1398,11 +1410,8 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
/** \name ToolTip Public API /** \name ToolTip Public API
* \{ */ * \{ */
/** ARegion *UI_tooltip_create_from_button_or_extra_icon(
* \param is_label: When true, show a small tip that only shows the name, bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label)
* otherwise show the full tooltip.
*/
ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label)
{ {
wmWindow *win = CTX_wm_window(C); wmWindow *win = CTX_wm_window(C);
/* aspect values that shrink text are likely unreadable */ /* aspect values that shrink text are likely unreadable */
@@ -1419,7 +1428,7 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b
} }
if (data == NULL) { if (data == NULL) {
data = ui_tooltip_data_from_button(C, but); data = ui_tooltip_data_from_button(C, but, extra_icon);
} }
if (data == NULL) { if (data == NULL) {
@@ -1457,6 +1466,15 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b
return region; return region;
} }
/**
* \param is_label: When true, show a small tip that only shows the name,
* otherwise show the full tooltip.
*/
ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label)
{
return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, NULL, is_label);
}
ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
{ {
wmWindow *win = CTX_wm_window(C); wmWindow *win = CTX_wm_window(C);

View File

@@ -102,9 +102,16 @@
// #define USE_OP_RESET_BUT // #define USE_OP_RESET_BUT
/* defines for templateID/TemplateSearch */ /* defines for templateID/TemplateSearch */
#define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 6) #define TEMPLATE_SEARCH_TEXTBUT_WIDTH (UI_UNIT_X * 8)
#define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y #define TEMPLATE_SEARCH_TEXTBUT_HEIGHT UI_UNIT_Y
/* Add "Make Single User" button to templateID. Users can just manually duplicate an ID, it's
* unclear what the use-case of this specific button is. So for now disabling it, we can bring it
* back or remove it later.
* - Julian
*/
//#define USE_TEMPLATE_ID_MAKE_SINGLE_USER
void UI_template_fix_linking(void) void UI_template_fix_linking(void)
{ {
} }
@@ -308,6 +315,12 @@ typedef struct TemplateID {
ListBase *idlb; ListBase *idlb;
short idcode; short idcode;
const char *new_op;
const char *duplicate_op;
const char *unlink_op;
const char *open_op;
short filter; short filter;
int prv_rows, prv_cols; int prv_rows, prv_cols;
bool preview; bool preview;
@@ -564,80 +577,64 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
memset(&idptr, 0, sizeof(idptr)); memset(&idptr, 0, sizeof(idptr));
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop); RNA_property_update(C, &template_ui->ptr, template_ui->prop);
if (id && CTX_wm_window(C)->eventstate->shift) {
/* only way to force-remove data (on save) */
id_us_clear_real(id);
id_fake_user_clear(id);
id->us = 0;
undo_push_label = "Delete Data-Block";
}
break; break;
case UI_ID_FAKE_USER: case UI_ID_MAKE_LOCAL:
if (id) {
if (id->flag & LIB_FAKEUSER) {
id_us_plus(id);
}
else {
id_us_min(id);
}
undo_push_label = "Fake User";
}
else {
return;
}
break;
case UI_ID_LOCAL:
if (id) { if (id) {
Main *bmain = CTX_data_main(C); Main *bmain = CTX_data_main(C);
if (CTX_wm_window(C)->eventstate->shift) { if (BKE_lib_id_make_local(bmain, id, false, 0)) {
if (ID_IS_OVERRIDABLE_LIBRARY(id)) { BKE_main_id_clear_newpoins(bmain);
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
if (override_id != NULL) {
BKE_main_id_clear_newpoins(bmain);
if (GS(override_id->name) == ID_OB) { /* reassign to get get proper updates/notifiers */
Scene *scene = CTX_data_scene(C); idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
if (!BKE_collection_has_object_recursive(scene->master_collection,
(Object *)override_id)) {
BKE_collection_object_add_from(
bmain, scene, (Object *)id, (Object *)override_id);
}
}
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
}
undo_push_label = "Make Library Override";
}
}
else {
if (BKE_lib_id_make_local(bmain, id, false, 0)) {
BKE_main_id_clear_newpoins(bmain);
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
undo_push_label = "Make Local";
}
}
if (undo_push_label != NULL) {
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop); RNA_property_update(C, &template_ui->ptr, template_ui->prop);
undo_push_label = "Make Local";
} }
} }
break; break;
case UI_ID_OVERRIDE: case UI_ID_LIB_OVERRIDE_ADD:
if (id && ID_IS_OVERRIDABLE_LIBRARY(id)) {
Main *bmain = CTX_data_main(C);
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
if (override_id != NULL) {
BKE_main_id_clear_newpoins(bmain);
if (GS(override_id->name) == ID_OB) {
Scene *scene = CTX_data_scene(C);
if (!BKE_collection_has_object_recursive(scene->master_collection,
(Object *)override_id)) {
BKE_collection_object_add_from(bmain, scene, (Object *)id, (Object *)override_id);
}
}
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
}
/* reassign to get get proper updates/notifiers */
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop);
undo_push_label = "Make Library Override";
}
break;
case UI_ID_LIB_OVERRIDE_RESET:
if (id && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
Main *bmain = CTX_data_main(C);
BKE_lib_override_library_id_reset(bmain, id);
undo_push_label = "Reset Library Override";
}
break;
case UI_ID_LIB_OVERRIDE_REMOVE:
if (id && ID_IS_OVERRIDE_LIBRARY(id)) { if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
BKE_lib_override_library_free(&id->override_library, true); BKE_lib_override_library_free(&id->override_library, true);
/* reassign to get get proper updates/notifiers */ /* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
RNA_property_update(C, &template_ui->ptr, template_ui->prop); RNA_property_update(C, &template_ui->ptr, template_ui->prop);
undo_push_label = "Override Data-Block"; undo_push_label = "Remove Library Override";
} }
break; break;
#ifdef USE_TEMPLATE_ID_MAKE_SINGLE_USER
case UI_ID_ALONE: case UI_ID_ALONE:
if (id) { if (id) {
const bool do_scene_obj = ((GS(id->name) == ID_OB) && const bool do_scene_obj = ((GS(id->name) == ID_OB) &&
@@ -659,6 +656,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
undo_push_label = "Make Single User"; undo_push_label = "Make Single User";
} }
break; break;
#endif
#if 0 #if 0
case UI_ID_AUTO_NAME: case UI_ID_AUTO_NAME:
break; break;
@@ -772,17 +770,221 @@ static const char *template_id_context(StructRNA *type)
# define template_id_context(type) 0 # define template_id_context(type) 0
#endif #endif
static uiBut *template_id_def_new_but(uiBlock *block, static void template_id_linked_operation_button(
const ID *id, uiBlock *block, Main *bmain, ID *id, TemplateID *template_ui, int operation)
const TemplateID *template_ui,
StructRNA *type,
const char *const newop,
const bool editable,
const bool id_open,
const bool use_tab_but,
int but_height)
{ {
BLI_assert(ELEM(operation, UI_ID_MAKE_LOCAL, UI_ID_LIB_OVERRIDE_ADD));
const char *label = (operation == UI_ID_MAKE_LOCAL) ?
CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Local") :
CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Library Override");
const char *tip = (operation == UI_ID_MAKE_LOCAL) ?
N_("Make library linked data-block local to this file") :
N_("Create a local override of this library linked data-block");
BIFIconID icon = (operation == UI_ID_MAKE_LOCAL) ? ICON_BLANK1 : ICON_LIBRARY_DATA_OVERRIDE;
uiBut *but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
icon,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, label),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_(tip));
bool disabled = false;
if (!ID_IS_LINKED(id)) {
disabled = true;
but->disabled_info = TIP_("Data-block is not linked");
}
else if (id->tag & LIB_TAG_INDIRECT) {
disabled = true;
but->disabled_info = TIP_("Indirect library data-block, cannot change");
}
else if (!BKE_lib_id_make_local(bmain, id, true /* test */, 0)) {
disabled = true;
but->disabled_info = TIP_("Data-blocks of this type cannot be made local");
}
else if (!RNA_property_editable_info(
&template_ui->ptr, template_ui->prop, &but->disabled_info)) {
disabled = true;
}
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
else {
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(operation));
}
}
static void template_id_library_overridden_button(uiBlock *block,
ID *id,
TemplateID *template_ui,
int operation)
{
BLI_assert(ELEM(operation, UI_ID_LIB_OVERRIDE_RESET, UI_ID_LIB_OVERRIDE_REMOVE) &&
ID_IS_OVERRIDE_LIBRARY(id));
if (operation == UI_ID_LIB_OVERRIDE_RESET && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
uiBut *but = uiDefIconTextBut(
block,
UI_BTYPE_BUT,
0,
ICON_BLANK1,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Reset Library Override"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Reset the local override to the state of "
"the overridden data-block"));
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
but->disabled_info = TIP_("Data-block is a virtual, not a real override");
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
else {
UI_but_funcN_set(but,
template_id_cb,
MEM_dupallocN(template_ui),
POINTER_FROM_INT(UI_ID_LIB_OVERRIDE_RESET));
}
}
else if (operation == UI_ID_LIB_OVERRIDE_REMOVE) {
uiBut *but = uiDefIconTextBut(
block,
UI_BTYPE_BUT,
0,
ICON_BLANK1,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Local"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Remove library override and make the library linked data-block "
"fully local to this file"));
UI_but_funcN_set(but,
template_id_cb,
MEM_dupallocN(template_ui),
POINTER_FROM_INT(UI_ID_LIB_OVERRIDE_REMOVE));
}
}
#ifdef USE_TEMPLATE_ID_MAKE_SINGLE_USER
static uiBut *template_id_make_single_user_button(uiBlock *block, ID *id, TemplateID *template_ui)
{
uiBut *but = uiDefIconTextBut(
block,
UI_BTYPE_BUT,
0,
ICON_BLANK1,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Single-User Copy"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Duplicate the data-block and assign the newly created copy"));
if (ID_REAL_USERS(id) <= 1) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
but->disabled_info = TIP_("Data-block already is a single-user");
/* No need for further setup. */
return but;
}
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ALONE));
ID *idfrom = template_ui->ptr.owner_id; ID *idfrom = template_ui->ptr.owner_id;
bool disabled = false;
if (!RNA_property_editable_info(&template_ui->ptr, template_ui->prop, &but->disabled_info)) {
disabled = true;
}
if (!BKE_id_copy_is_allowed(id)) {
but->disabled_info = TIP_("Data-blocks of this type cannot be copied");
disabled = true;
}
/* object in editmode - don't change data */
if (idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT)) {
but->disabled_info = TIP_("Cannot change object data in Edit Mode");
disabled = true;
}
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
return but;
}
#endif
static void template_id_use_fake_user_button(uiBlock *block, PointerRNA *idptr)
{
const bool use_fake_user = ID_FAKE_USERS(idptr->data) > 0;
uiBut *but = uiDefIconTextButR(
block,
UI_BTYPE_ICON_TOGGLE,
0,
ICON_FAKE_USER_OFF,
use_fake_user ? CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove Fake User") :
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add Fake User"),
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
idptr,
"use_fake_user",
-1,
0,
0,
-1,
-1,
TIP_("When set, ensures the data-block is kept when reloading the file, even if not used at "
"all"));
if ((ELEM(GS(((ID *)idptr->data)->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
but->disabled_info = TIP_("Data-block type does not support fake user");
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
enum TemplateIDCreateType {
TEMPLATE_ID_CREATE_NEW,
TEMPLATE_ID_CREATE_DUPLICATE,
};
static uiBut *template_id_new_button(uiBlock *block,
const ID *id,
TemplateID *template_ui,
enum TemplateIDCreateType create_type,
StructRNA *type,
const bool id_open,
const bool use_tab_but,
int but_height)
{
uiBut *but; uiBut *but;
const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6; const int w = id ? UI_UNIT_X : id_open ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT; const int but_type = use_tab_but ? UI_BTYPE_TAB : UI_BTYPE_BUT;
@@ -820,31 +1022,80 @@ static uiBut *template_id_def_new_but(uiBlock *block,
BLT_I18NCONTEXT_ID_POINTCLOUD, BLT_I18NCONTEXT_ID_POINTCLOUD,
BLT_I18NCONTEXT_ID_VOLUME, BLT_I18NCONTEXT_ID_VOLUME,
BLT_I18NCONTEXT_ID_SIMULATION, ); BLT_I18NCONTEXT_ID_SIMULATION, );
BLT_I18N_MSGID_MULTI_CTXT("Duplicate",
BLT_I18NCONTEXT_DEFAULT,
BLT_I18NCONTEXT_ID_SCENE,
BLT_I18NCONTEXT_ID_OBJECT,
BLT_I18NCONTEXT_ID_MESH,
BLT_I18NCONTEXT_ID_CURVE,
BLT_I18NCONTEXT_ID_METABALL,
BLT_I18NCONTEXT_ID_MATERIAL,
BLT_I18NCONTEXT_ID_TEXTURE,
BLT_I18NCONTEXT_ID_IMAGE,
BLT_I18NCONTEXT_ID_LATTICE,
BLT_I18NCONTEXT_ID_LIGHT,
BLT_I18NCONTEXT_ID_CAMERA,
BLT_I18NCONTEXT_ID_WORLD,
BLT_I18NCONTEXT_ID_SCREEN,
BLT_I18NCONTEXT_ID_TEXT, );
BLT_I18N_MSGID_MULTI_CTXT("Duplicate",
BLT_I18NCONTEXT_ID_SPEAKER,
BLT_I18NCONTEXT_ID_SOUND,
BLT_I18NCONTEXT_ID_ARMATURE,
BLT_I18NCONTEXT_ID_ACTION,
BLT_I18NCONTEXT_ID_NODETREE,
BLT_I18NCONTEXT_ID_BRUSH,
BLT_I18NCONTEXT_ID_PARTICLESETTINGS,
BLT_I18NCONTEXT_ID_GPENCIL,
BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
BLT_I18NCONTEXT_ID_WORKSPACE,
BLT_I18NCONTEXT_ID_LIGHTPROBE,
BLT_I18NCONTEXT_ID_HAIR,
BLT_I18NCONTEXT_ID_POINTCLOUD,
BLT_I18NCONTEXT_ID_VOLUME,
BLT_I18NCONTEXT_ID_SIMULATION, );
/* Note: BLT_I18N_MSGID_MULTI_CTXT takes a maximum number of parameters, /* Note: BLT_I18N_MSGID_MULTI_CTXT takes a maximum number of parameters,
* check the definition to see if a new call must be added when the limit * check the definition to see if a new call must be added when the limit
* is exceeded. */ * is exceeded. */
if (newop) { const char *text;
but = uiDefIconTextButO(block, const char *op;
but_type, BIFIconID icon;
newop,
WM_OP_INVOKE_DEFAULT, switch (create_type) {
(id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD, case TEMPLATE_ID_CREATE_NEW:
(id) ? "" : CTX_IFACE_(template_id_context(type), "New"), icon = ICON_ADD;
0, text = CTX_IFACE_(template_id_context(type), "New");
0, op = template_ui->new_op;
w, break;
but_height, case TEMPLATE_ID_CREATE_DUPLICATE:
NULL); icon = ICON_DUPLICATE;
text = CTX_IFACE_(template_id_context(type), "Duplicate");
op = template_ui->duplicate_op;
break;
}
const bool icon_only = use_tab_but;
if (icon_only) {
text = "";
}
if (op) {
but = uiDefIconTextButO(
block, but_type, op, WM_OP_INVOKE_DEFAULT, icon, text, 0, 0, w, but_height, NULL);
UI_but_funcN_set( UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW)); but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
if (!RNA_property_editable_info(&template_ui->ptr, template_ui->prop, &but->disabled_info)) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
} }
else { else {
but = uiDefIconTextBut(block, but = uiDefIconTextBut(block,
but_type, but_type,
0, 0,
(id && !use_tab_but) ? ICON_DUPLICATE : ICON_ADD, icon,
(id) ? "" : CTX_IFACE_(template_id_context(type), "New"), text,
0, 0,
0, 0,
w, w,
@@ -854,14 +1105,13 @@ static uiBut *template_id_def_new_but(uiBlock *block,
0, 0,
0, 0,
0, 0,
NULL); TIP_("Create a new data-block"));
UI_but_funcN_set( but->disabled_info = TIP_("Creating a new data-block is not supported here");
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ADD_NEW));
}
if ((idfrom && idfrom->lib) || !editable) {
UI_but_flag_enable(but, UI_BUT_DISABLED); UI_but_flag_enable(but, UI_BUT_DISABLED);
} }
if (icon_only) {
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
}
#ifndef WITH_INTERNATIONAL #ifndef WITH_INTERNATIONAL
UNUSED_VARS(type); UNUSED_VARS(type);
@@ -870,14 +1120,293 @@ static uiBut *template_id_def_new_but(uiBlock *block,
return but; return but;
} }
static void template_id_unlink_button(uiBlock *block,
TemplateID *template_ui,
bool hide_if_disabled)
{
const char *disable_info;
const bool is_menu = ui_block_is_menu(block);
const bool editable = RNA_property_editable_info(
&template_ui->ptr, template_ui->prop, &disable_info);
/* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */
uiBut *but = NULL;
if (hide_if_disabled && !editable) {
/* Add no button. */
}
else if (template_ui->unlink_op) {
but = uiDefIconTextButO(block,
UI_BTYPE_BUT,
template_ui->unlink_op,
WM_OP_INVOKE_DEFAULT,
ICON_X,
is_menu ? NULL : "",
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL);
/* so we can access the template from operators, font unlinking needs this */
UI_but_funcN_set(but, NULL, MEM_dupallocN(template_ui), NULL);
}
else {
const bool never_unlink = RNA_property_flag(template_ui->prop) &
(PROP_NEVER_UNLINK | PROP_NEVER_NULL);
if (!never_unlink || !hide_if_disabled) {
but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
ICON_X,
is_menu ? CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Unlink") : "",
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Remove this usage of the data-block"));
if (!never_unlink) {
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_DELETE));
}
if (!is_menu) {
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
}
}
if (but && never_unlink) {
but->disabled_info = TIP_("Property must never be in an unlinked state");
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
if (but) {
if (!editable) {
but->disabled_info = disable_info;
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
}
static void template_id_open_button(uiBlock *block,
ID *id,
TemplateID *template_ui,
const bool compact)
{
const bool is_menu = ui_block_is_menu(block);
const int w = compact ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
char text[UI_MAX_NAME_STR] = "";
const bool icon_only = !is_menu && id;
uiBut *but;
if (!icon_only) {
BLI_snprintf(text,
sizeof(text),
"%s%s",
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Open"),
is_menu ? "..." : "");
}
if (template_ui->open_op) {
but = uiDefIconTextButO(block,
UI_BTYPE_BUT,
template_ui->open_op,
WM_OP_INVOKE_DEFAULT,
ICON_FILEBROWSER,
text,
0,
0,
w,
UI_UNIT_Y,
NULL);
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
}
else {
but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
ICON_FILEBROWSER,
text,
0,
0,
w,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
NULL);
but->disabled_info = TIP_("Browsing for a new data-block is not supported here");
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
if (!RNA_property_editable_info(&template_ui->ptr, template_ui->prop, &but->disabled_info)) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
static void template_id_unpack_button(uiBlock *block, ID *id)
{
wmOperatorType *optype = WM_operatortype_find("FILE_OT_unpack_item", false);
uiBut *but;
but = uiDefIconTextButO_ptr(block,
UI_BTYPE_BUT,
optype,
WM_OP_INVOKE_REGION_WIN,
ICON_PACKAGE,
NULL,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL);
if (!BKE_packedfile_id_check(id)) {
but->disabled_info = TIP_("File is not packed");
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
UI_but_operator_ptr_get(but);
RNA_string_set(but->opptr, "id_name", id->name + 2);
RNA_int_set(but->opptr, "id_type", GS(id->name));
}
static void template_id_user_count_label(uiLayout *layout, const ID *id)
{
char numstr[UI_MAX_NAME_STR];
BLI_snprintf(numstr,
sizeof(numstr),
"%d %s",
ID_REAL_USERS(id),
ID_REAL_USERS(id) > 1 ? IFACE_("Users") : IFACE_("User"));
uiItemL(layout, numstr, ICON_NONE);
}
static void template_id_menu(bContext *C, uiLayout *layout, void *arg)
{
/* Button that spawned the menu. */
const uiBut *menu_parent_but = arg;
TemplateID *template_ui = menu_parent_but->func_argN;
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = idptr.data;
uiBlock *block = uiLayoutGetBlock(layout);
BLI_assert(id);
template_id_new_button(
block, id, template_ui, TEMPLATE_ID_CREATE_NEW, idptr.type, false, false, UI_UNIT_X);
template_id_new_button(
block, id, template_ui, TEMPLATE_ID_CREATE_DUPLICATE, idptr.type, false, false, UI_UNIT_X);
template_id_unlink_button(block, template_ui, false);
#ifdef USE_TEMPLATE_ID_MAKE_SINGLE_USER
template_id_make_single_user_button(block, id, template_ui);
#endif
template_id_use_fake_user_button(block, &idptr);
if (template_ui->open_op || BKE_packedfile_id_check(id)) {
uiItemS(layout);
template_id_open_button(block, id, template_ui, false);
template_id_unpack_button(block, id);
}
/* Library operators. */
if (ID_IS_OVERRIDE_LIBRARY(id)) {
uiItemS(layout);
template_id_library_overridden_button(block, id, template_ui, UI_ID_LIB_OVERRIDE_RESET);
template_id_library_overridden_button(block, id, template_ui, UI_ID_LIB_OVERRIDE_REMOVE);
}
else if (ID_IS_LINKED(id)) {
uiItemS(layout);
Main *bmain = CTX_data_main(C);
template_id_linked_operation_button(block, bmain, id, template_ui, UI_ID_MAKE_LOCAL);
template_id_linked_operation_button(block, bmain, id, template_ui, UI_ID_LIB_OVERRIDE_ADD);
}
uiItemS(layout);
template_id_user_count_label(layout, id);
}
static void template_id_name_button(
uiBlock *block, StructRNA *type, TemplateID *template_ui, int flag, const bool hide_extras)
{
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = idptr.data;
BIFIconID lib_icon = UI_icon_from_library(id);
char name[UI_MAX_NAME_STR] = "";
uiBut *but = uiDefIconTextButR(block,
UI_BTYPE_TEXT,
0,
lib_icon,
name,
0,
0,
TEMPLATE_SEARCH_TEXTBUT_WIDTH,
TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
&idptr,
"name",
-1,
0,
0,
0,
0,
RNA_struct_ui_description(type));
/* Note: Also needed for the extra icons below (their operators access the TemplateID). */
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_RENAME));
const bool user_alert = (id->us <= 0);
if (user_alert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
if (hide_extras) {
return;
}
if (template_ui->duplicate_op && (flag & UI_ID_ADD_NEW)) {
UI_but_extra_operator_icon_add(
but, template_ui->duplicate_op, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE);
}
if (id && (flag & UI_ID_DELETE)) {
const bool never_unlink = RNA_property_flag(template_ui->prop) &
(PROP_NEVER_UNLINK | PROP_NEVER_NULL);
if (template_ui->unlink_op) {
UI_but_extra_operator_icon_add(but, template_ui->unlink_op, WM_OP_INVOKE_DEFAULT, ICON_X);
}
else if (!never_unlink) {
UI_but_extra_operator_icon_add(but, "ED_OT_lib_unlink", WM_OP_INVOKE_DEFAULT, ICON_X);
}
}
/* Disable fake user icon for now, only have it in the menu. */
const bool add_extra_fake_user_icon = false;
if (add_extra_fake_user_icon && id->lib == NULL &&
!(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
UI_but_extra_operator_icon_add(but,
"ED_OT_lib_fake_user_toggle",
WM_OP_INVOKE_DEFAULT,
ID_FAKE_USERS(id) ? ICON_FAKE_USER_ON : ICON_FAKE_USER_OFF);
}
}
static void template_ID(const bContext *C, static void template_ID(const bContext *C,
uiLayout *layout, uiLayout *layout,
TemplateID *template_ui, TemplateID *template_ui,
StructRNA *type, StructRNA *type,
int flag, int flag,
const char *newop,
const char *openop,
const char *unlinkop,
const char *text, const char *text,
const bool live_icon, const bool live_icon,
const bool hide_buttons) const bool hide_buttons)
@@ -885,15 +1414,12 @@ static void template_ID(const bContext *C,
uiBut *but; uiBut *but;
uiBlock *block; uiBlock *block;
PointerRNA idptr; PointerRNA idptr;
// ListBase *lb; // UNUSED ID *id;
ID *id, *idfrom;
const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop); const bool editable = RNA_property_editable(&template_ui->ptr, template_ui->prop);
const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0; const bool use_previews = template_ui->preview = (flag & UI_ID_PREVIEWS) != 0;
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
id = idptr.data; id = idptr.data;
idfrom = template_ui->ptr.owner_id;
// lb = template_ui->idlb;
/* Allow opertators to take the ID from context. */ /* Allow opertators to take the ID from context. */
uiLayoutSetContextPointer(layout, "id", &idptr); uiLayoutSetContextPointer(layout, "id", &idptr);
@@ -926,277 +1452,39 @@ static void template_ID(const bContext *C,
/* text button with name */ /* text button with name */
if (id) { if (id) {
char name[UI_MAX_NAME_STR]; template_id_name_button(block, type, template_ui, flag, hide_buttons);
const bool user_alert = (id->us <= 0); }
/* If no ID is set, show a "new" button instead of a text button. */
// text_idbutton(id, name); else if ((flag & UI_ID_ADD_NEW) && (hide_buttons == false)) {
name[0] = '\0'; template_id_new_button(
but = uiDefButR(block, block, id, template_ui, TEMPLATE_ID_CREATE_NEW, type, flag & UI_ID_OPEN, false, UI_UNIT_X);
UI_BTYPE_TEXT,
0,
name,
0,
0,
TEMPLATE_SEARCH_TEXTBUT_WIDTH,
TEMPLATE_SEARCH_TEXTBUT_HEIGHT,
&idptr,
"name",
-1,
0,
0,
-1,
-1,
RNA_struct_ui_description(type));
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_RENAME));
if (user_alert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
if (id->lib) {
if (id->tag & LIB_TAG_INDIRECT) {
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_LIBRARY_DATA_INDIRECT,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Indirect library data-block, cannot change"));
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
else {
const bool disabled = (!BKE_lib_id_make_local(CTX_data_main(C), id, true /* test */, 0) ||
(idfrom && idfrom->lib));
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_LIBRARY_DATA_DIRECT,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Direct linked library data-block, click to make local, "
"Shift + Click to create a library override"));
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
else {
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_LOCAL));
}
}
}
else if (ID_IS_OVERRIDE_LIBRARY(id)) {
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
ICON_LIBRARY_DATA_OVERRIDE,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Library override of linked data-block, click to make fully local"));
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE));
}
if ((ID_REAL_USERS(id) > 1) && (hide_buttons == false)) {
char numstr[32];
short numstr_len;
numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", ID_REAL_USERS(id));
but = uiDefBut(
block,
UI_BTYPE_BUT,
0,
numstr,
0,
0,
numstr_len * 0.2f * UI_UNIT_X + UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Display number of users of this data (click to make a single-user copy)"));
but->flag |= UI_BUT_UNDO;
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_ALONE));
if ((!BKE_id_copy_is_allowed(id)) || (idfrom && idfrom->lib) || (!editable) ||
/* object in editmode - don't change data */
(idfrom && GS(idfrom->name) == ID_OB && (((Object *)idfrom)->mode & OB_MODE_EDIT))) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
if (user_alert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) &&
(hide_buttons == false)) {
uiDefIconButR(block,
UI_BTYPE_ICON_TOGGLE,
0,
ICON_FAKE_USER_OFF,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
&idptr,
"use_fake_user",
-1,
0,
0,
-1,
-1,
NULL);
}
} }
if ((flag & UI_ID_ADD_NEW) && (hide_buttons == false)) { if ((flag & UI_ID_OPEN) && !id) {
template_id_def_new_but( template_id_open_button(block, id, template_ui, flag & UI_ID_ADD_NEW);
block, id, template_ui, type, newop, editable, flag & UI_ID_OPEN, false, UI_UNIT_X);
}
/* Due to space limit in UI - skip the "open" icon for packed data, and allow to unpack.
* Only for images, sound and fonts */
if (id && BKE_packedfile_id_check(id)) {
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"FILE_OT_unpack_item",
WM_OP_INVOKE_REGION_WIN,
ICON_PACKAGE,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
TIP_("Packed File, click to unpack"));
UI_but_operator_ptr_get(but);
RNA_string_set(but->opptr, "id_name", id->name + 2);
RNA_int_set(but->opptr, "id_type", GS(id->name));
}
else if (flag & UI_ID_OPEN) {
const int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6;
if (openop) {
but = uiDefIconTextButO(block,
UI_BTYPE_BUT,
openop,
WM_OP_INVOKE_DEFAULT,
ICON_FILEBROWSER,
(id) ? "" : IFACE_("Open"),
0,
0,
w,
UI_UNIT_Y,
NULL);
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
}
else {
but = uiDefIconTextBut(block,
UI_BTYPE_BUT,
0,
ICON_FILEBROWSER,
(id) ? "" : IFACE_("Open"),
0,
0,
w,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
NULL);
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OPEN));
}
if ((idfrom && idfrom->lib) || !editable) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
/* delete button */
/* don't use RNA_property_is_unlink here */
if (id && (flag & UI_ID_DELETE) && (hide_buttons == false)) {
/* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */
but = NULL;
if (unlinkop) {
but = uiDefIconButO(block,
UI_BTYPE_BUT,
unlinkop,
WM_OP_INVOKE_DEFAULT,
ICON_X,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL);
/* so we can access the template from operators, font unlinking needs this */
UI_but_funcN_set(but, NULL, MEM_dupallocN(template_ui), NULL);
}
else {
if ((RNA_property_flag(template_ui->prop) & PROP_NEVER_UNLINK) == 0) {
but = uiDefIconBut(
block,
UI_BTYPE_BUT,
0,
ICON_X,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
NULL,
0,
0,
0,
0,
TIP_("Unlink data-block "
"(Shift + Click to set users to zero, data will then not be saved)"));
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_DELETE));
if (RNA_property_flag(template_ui->prop) & PROP_NEVER_NULL) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
}
if (but) {
if ((idfrom && idfrom->lib) || !editable) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
} }
if (template_ui->idcode == ID_TE) { if (template_ui->idcode == ID_TE) {
uiTemplateTextureShow(layout, C, &template_ui->ptr, template_ui->prop); uiTemplateTextureShow(layout, C, &template_ui->ptr, template_ui->prop);
} }
if (id && !hide_buttons) {
/* Additional operations menu ("Make Local", overrides, etc). */
but = uiDefIconMenuBut(block,
template_id_menu,
NULL,
ICON_DOWNARROW_HLT,
0,
0,
UI_UNIT_X,
UI_UNIT_Y,
TIP_("Show more operations for the selected data-block"));
UI_but_type_set_menu_from_pulldown(but);
/* Same hack as ui_item_menu() to allow allocated arg. */
but->poin = (char *)but;
but->func_argN = MEM_dupallocN(template_ui);
}
UI_block_align_end(block); UI_block_align_end(block);
} }
@@ -1215,7 +1503,6 @@ static void template_ID_tabs(const bContext *C,
TemplateID *template, TemplateID *template,
StructRNA *type, StructRNA *type,
int flag, int flag,
const char *newop,
const char *menu) const char *menu)
{ {
const ARegion *region = CTX_wm_region(C); const ARegion *region = CTX_wm_region(C);
@@ -1263,22 +1550,20 @@ static void template_ID_tabs(const bContext *C,
BLI_freelistN(&ordered); BLI_freelistN(&ordered);
if (flag & UI_ID_ADD_NEW) { if (flag & UI_ID_ADD_NEW) {
const bool editable = RNA_property_editable(&template->ptr, template->prop);
uiBut *but; uiBut *but;
if (active_ptr.type) { if (active_ptr.type) {
type = active_ptr.type; type = active_ptr.type;
} }
but = template_id_def_new_but(block, but = template_id_new_button(block,
active_ptr.data, active_ptr.data,
template, template,
type, TEMPLATE_ID_CREATE_NEW,
newop, type,
editable, flag & UI_ID_OPEN,
flag & UI_ID_OPEN, true,
true, but_height);
but_height);
UI_but_drawflag_enable(but, but_align); UI_but_drawflag_enable(but, but_align);
} }
} }
@@ -1288,6 +1573,7 @@ static void ui_template_id(uiLayout *layout,
PointerRNA *ptr, PointerRNA *ptr,
const char *propname, const char *propname,
const char *newop, const char *newop,
const char *duplicateop,
const char *openop, const char *openop,
const char *unlinkop, const char *unlinkop,
/* Only respected by tabs (use_tabs). */ /* Only respected by tabs (use_tabs). */
@@ -1317,6 +1603,10 @@ static void ui_template_id(uiLayout *layout,
template_ui = MEM_callocN(sizeof(TemplateID), "TemplateID"); template_ui = MEM_callocN(sizeof(TemplateID), "TemplateID");
template_ui->ptr = *ptr; template_ui->ptr = *ptr;
template_ui->prop = prop; template_ui->prop = prop;
template_ui->new_op = newop;
template_ui->duplicate_op = duplicateop;
template_ui->unlink_op = unlinkop;
template_ui->open_op = openop;
template_ui->prv_rows = prv_rows; template_ui->prv_rows = prv_rows;
template_ui->prv_cols = prv_cols; template_ui->prv_cols = prv_cols;
template_ui->scale = scale; template_ui->scale = scale;
@@ -1328,7 +1618,7 @@ static void ui_template_id(uiLayout *layout,
template_ui->filter = 0; template_ui->filter = 0;
} }
if (newop) { if (newop || duplicateop) {
flag |= UI_ID_ADD_NEW; flag |= UI_ID_ADD_NEW;
} }
if (openop) { if (openop) {
@@ -1346,21 +1636,11 @@ static void ui_template_id(uiLayout *layout,
if (template_ui->idlb) { if (template_ui->idlb) {
if (use_tabs) { if (use_tabs) {
layout = uiLayoutRow(layout, true); layout = uiLayoutRow(layout, true);
template_ID_tabs(C, layout, template_ui, type, flag, newop, menu); template_ID_tabs(C, layout, template_ui, type, flag, menu);
} }
else { else {
layout = uiLayoutRow(layout, true); layout = uiLayoutRow(layout, true);
template_ID(C, template_ID(C, layout, template_ui, type, flag, text, live_icon, hide_buttons);
layout,
template_ui,
type,
flag,
newop,
openop,
unlinkop,
text,
live_icon,
hide_buttons);
} }
} }
@@ -1372,6 +1652,7 @@ void uiTemplateID(uiLayout *layout,
PointerRNA *ptr, PointerRNA *ptr,
const char *propname, const char *propname,
const char *newop, const char *newop,
const char *duplicateop,
const char *openop, const char *openop,
const char *unlinkop, const char *unlinkop,
int filter, int filter,
@@ -1383,6 +1664,7 @@ void uiTemplateID(uiLayout *layout,
ptr, ptr,
propname, propname,
newop, newop,
duplicateop,
openop, openop,
unlinkop, unlinkop,
NULL, NULL,
@@ -1412,6 +1694,7 @@ void uiTemplateIDBrowse(uiLayout *layout,
ptr, ptr,
propname, propname,
newop, newop,
NULL,
openop, openop,
unlinkop, unlinkop,
NULL, NULL,
@@ -1443,6 +1726,7 @@ void uiTemplateIDPreview(uiLayout *layout,
ptr, ptr,
propname, propname,
newop, newop,
NULL,
openop, openop,
unlinkop, unlinkop,
NULL, NULL,
@@ -1475,6 +1759,7 @@ void uiTemplateGpencilColorPreview(uiLayout *layout,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL,
UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE, UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE,
rows, rows,
cols, cols,
@@ -1503,6 +1788,7 @@ void uiTemplateIDTabs(uiLayout *layout,
newop, newop,
NULL, NULL,
NULL, NULL,
NULL,
menu, menu,
NULL, NULL,
UI_ID_BROWSE | UI_ID_RENAME, UI_ID_BROWSE | UI_ID_RENAME,
@@ -1662,35 +1948,25 @@ static void template_search_add_button_searchmenu(const bContext *C,
static void template_search_add_button_name(uiBlock *block, static void template_search_add_button_name(uiBlock *block,
PointerRNA *active_ptr, PointerRNA *active_ptr,
const StructRNA *type) const StructRNA *type,
const char *newop,
const char *unlinkop)
{ {
uiDefAutoButR(block, uiBut *but = uiDefAutoButR(block,
active_ptr, active_ptr,
RNA_struct_name_property(type), RNA_struct_name_property(type),
0, 0,
"", "",
ICON_NONE, ICON_NONE,
0, 0,
0, 0,
TEMPLATE_SEARCH_TEXTBUT_WIDTH, TEMPLATE_SEARCH_TEXTBUT_WIDTH,
TEMPLATE_SEARCH_TEXTBUT_HEIGHT); TEMPLATE_SEARCH_TEXTBUT_HEIGHT);
} if (newop) {
UI_but_extra_operator_icon_add(but, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE);
static void template_search_add_button_operator(uiBlock *block,
const char *const operator_name,
const int opcontext,
const int icon,
const bool editable)
{
if (!operator_name) {
return;
} }
if (unlinkop) {
uiBut *but = uiDefIconButO( UI_but_extra_operator_icon_add(but, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X);
block, UI_BTYPE_BUT, operator_name, opcontext, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
if (!editable) {
UI_but_drawflag_enable(but, UI_BUT_DISABLED);
} }
} }
@@ -1716,10 +1992,7 @@ static void template_search_buttons(const bContext *C,
UI_block_align_begin(block); UI_block_align_begin(block);
template_search_add_button_searchmenu(C, layout, block, template_search, editable, false); template_search_add_button_searchmenu(C, layout, block, template_search, editable, false);
template_search_add_button_name(block, &active_ptr, type); template_search_add_button_name(block, &active_ptr, type, newop, unlinkop);
template_search_add_button_operator(
block, newop, WM_OP_INVOKE_DEFAULT, ICON_DUPLICATE, editable);
template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable);
UI_block_align_end(block); UI_block_align_end(block);
} }
@@ -7191,6 +7464,7 @@ void uiTemplateCacheFile(uiLayout *layout,
ptr, ptr,
propname, propname,
NULL, NULL,
NULL,
"CACHEFILE_OT_open", "CACHEFILE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,

View File

@@ -38,8 +38,11 @@ void OBJECT_OT_material_slot_move(struct wmOperatorType *ot);
void OBJECT_OT_material_slot_remove_unused(struct wmOperatorType *ot); void OBJECT_OT_material_slot_remove_unused(struct wmOperatorType *ot);
void MATERIAL_OT_new(struct wmOperatorType *ot); void MATERIAL_OT_new(struct wmOperatorType *ot);
void MATERIAL_OT_duplicate(struct wmOperatorType *ot);
void TEXTURE_OT_new(struct wmOperatorType *ot); void TEXTURE_OT_new(struct wmOperatorType *ot);
void TEXTURE_OT_duplicate(struct wmOperatorType *ot);
void WORLD_OT_new(struct wmOperatorType *ot); void WORLD_OT_new(struct wmOperatorType *ot);
void WORLD_OT_duplicate(struct wmOperatorType *ot);
void MATERIAL_OT_copy(struct wmOperatorType *ot); void MATERIAL_OT_copy(struct wmOperatorType *ot);
void MATERIAL_OT_paste(struct wmOperatorType *ot); void MATERIAL_OT_paste(struct wmOperatorType *ot);

View File

@@ -45,8 +45,11 @@ void ED_operatortypes_render(void)
WM_operatortype_append(OBJECT_OT_material_slot_remove_unused); WM_operatortype_append(OBJECT_OT_material_slot_remove_unused);
WM_operatortype_append(MATERIAL_OT_new); WM_operatortype_append(MATERIAL_OT_new);
WM_operatortype_append(MATERIAL_OT_duplicate);
WM_operatortype_append(TEXTURE_OT_new); WM_operatortype_append(TEXTURE_OT_new);
WM_operatortype_append(TEXTURE_OT_duplicate);
WM_operatortype_append(WORLD_OT_new); WM_operatortype_append(WORLD_OT_new);
WM_operatortype_append(WORLD_OT_duplicate);
WM_operatortype_append(MATERIAL_OT_copy); WM_operatortype_append(MATERIAL_OT_copy);
WM_operatortype_append(MATERIAL_OT_paste); WM_operatortype_append(MATERIAL_OT_paste);

View File

@@ -735,45 +735,40 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name New Material Operator /** \name Create Material Operators
* \{ */ * \{ */
static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) struct MaterialCreationData {
Object *ob;
PropertyPointerRNA pprop;
};
static void material_creation_data_init_from_UI_context(bContext *C,
struct MaterialCreationData *r_create_data)
{ {
Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; PointerRNA ptr;
Main *bmain = CTX_data_main(C);
PointerRNA ptr, idptr;
PropertyRNA *prop; PropertyRNA *prop;
/* hook into UI */ /* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop); UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
Object *ob = (prop && RNA_struct_is_a(ptr.type, &RNA_Object)) ? ptr.data : NULL; r_create_data->ob = (prop && RNA_struct_is_a(ptr.type, &RNA_Object)) ? ptr.data : NULL;
r_create_data->pprop.ptr = ptr;
r_create_data->pprop.prop = prop;
}
/* add or copy material */ static void material_creation_assign(bContext *C,
if (ma) { Material *ma,
Material *new_ma = (Material *)BKE_id_copy_ex( struct MaterialCreationData *create_data)
bmain, &ma->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS); {
ma = new_ma; Main *bmain = CTX_data_main(C);
}
else {
const char *name = DATA_("Material");
if (!(ob != NULL && ob->type == OB_GPENCIL)) {
ma = BKE_material_add(bmain, name);
}
else {
ma = BKE_gpencil_material_add(bmain, name);
}
ED_node_shader_default(C, &ma->id);
ma->use_nodes = true;
}
if (prop) { if (create_data->pprop.prop) {
if (ob != NULL) { if (create_data->ob != NULL) {
/* Add slot follows user-preferences for creating new slots, /* Add slot follows user-preferences for creating new slots,
* RNA pointer assignment doesn't, see: T60014. */ * RNA pointer assignment doesn't, see: T60014. */
if (BKE_object_material_get_p(ob, ob->actcol) == NULL) { if (BKE_object_material_get_p(create_data->ob, create_data->ob->actcol) == NULL) {
BKE_object_material_slot_add(bmain, ob); BKE_object_material_slot_add(bmain, create_data->ob);
} }
} }
@@ -781,10 +776,32 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op))
* pointer use also increases user, so this compensates it */ * pointer use also increases user, so this compensates it */
id_us_min(&ma->id); id_us_min(&ma->id);
PointerRNA idptr;
RNA_id_pointer_create(&ma->id, &idptr); RNA_id_pointer_create(&ma->id, &idptr);
RNA_property_pointer_set(&ptr, prop, idptr, NULL); RNA_property_pointer_set(&create_data->pprop.ptr, create_data->pprop.prop, idptr, NULL);
RNA_property_update(C, &ptr, prop); RNA_property_update(C, &create_data->pprop.ptr, create_data->pprop.prop);
} }
}
static int new_material_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
struct MaterialCreationData create_data;
material_creation_data_init_from_UI_context(C, &create_data);
const char *name = DATA_("Material");
Material *ma;
if ((create_data.ob == NULL) || (create_data.ob->type != OB_GPENCIL)) {
ma = BKE_material_add(bmain, name);
}
else {
ma = BKE_gpencil_material_add(bmain, name);
}
ED_node_shader_default(C, &ma->id);
ma->use_nodes = true;
material_creation_assign(C, ma, &create_data);
WM_event_add_notifier(C, NC_MATERIAL | NA_ADDED, ma); WM_event_add_notifier(C, NC_MATERIAL | NA_ADDED, ma);
@@ -806,27 +823,57 @@ void MATERIAL_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
} }
/** \} */ static int duplicate_material_exec(bContext *C, wmOperator *op)
{
Material *old_ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data;
if (!old_ma) {
BKE_report(
op->reports,
RPT_ERROR,
"Incorrect context for duplicating a material (did not find material to duplicate)");
return OPERATOR_CANCELLED;
}
Main *bmain = CTX_data_main(C);
struct MaterialCreationData create_data;
material_creation_data_init_from_UI_context(C, &create_data);
Material *new_ma = (Material *)BKE_id_copy_ex(
bmain, &old_ma->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS);
material_creation_assign(C, new_ma, &create_data);
WM_event_add_notifier(C, NC_MATERIAL | NA_ADDED, new_ma);
return OPERATOR_FINISHED;
}
void MATERIAL_OT_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Material";
ot->idname = "MATERIAL_OT_duplicate";
ot->description = "Duplicate an existing material";
/* api callbacks */
ot->exec = duplicate_material_exec;
ot->poll = object_materials_supported_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name New Texture Operator /** \name Create Texture Operators
* \{ */ * \{ */
static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) static void texture_creation_assign(bContext *C, Tex *tex)
{ {
Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
Main *bmain = CTX_data_main(C);
PointerRNA ptr, idptr; PointerRNA ptr, idptr;
PropertyRNA *prop; PropertyRNA *prop;
/* add or copy texture */
if (tex) {
tex = (Tex *)BKE_id_copy(bmain, &tex->id);
}
else {
tex = BKE_texture_add(bmain, DATA_("Texture"));
}
/* hook into UI */ /* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop); UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -839,6 +886,14 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op))
RNA_property_pointer_set(&ptr, prop, idptr, NULL); RNA_property_pointer_set(&ptr, prop, idptr, NULL);
RNA_property_update(C, &ptr, prop); RNA_property_update(C, &ptr, prop);
} }
}
static int new_texture_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
Tex *tex = BKE_texture_add(bmain, DATA_("Texture"));
texture_creation_assign(C, tex);
WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, tex); WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, tex);
@@ -859,31 +914,53 @@ void TEXTURE_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
} }
static int duplicate_texture_exec(bContext *C, wmOperator *op)
{
Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
if (!tex) {
BKE_report(op->reports,
RPT_ERROR,
"Incorrect context for duplicating a texture (did not find texture to duplicate)");
return OPERATOR_CANCELLED;
}
/* add or copy texture */
Main *bmain = CTX_data_main(C);
tex = (Tex *)BKE_id_copy(bmain, &tex->id);
texture_creation_assign(C, tex);
WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, tex);
return OPERATOR_FINISHED;
}
void TEXTURE_OT_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Texture";
ot->idname = "TEXTURE_OT_duplicate";
ot->description = "Duplicate an existing texture";
/* api callbacks */
ot->exec = duplicate_texture_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name new world operator /** \name Create world operators
* \{ */ * \{ */
static int new_world_exec(bContext *C, wmOperator *UNUSED(op)) static void world_creation_assign(bContext *C, World *wo)
{ {
World *wo = CTX_data_pointer_get_type(C, "world", &RNA_World).data;
Main *bmain = CTX_data_main(C);
PointerRNA ptr, idptr; PointerRNA ptr, idptr;
PropertyRNA *prop; PropertyRNA *prop;
/* add or copy world */
if (wo) {
World *new_wo = (World *)BKE_id_copy_ex(
bmain, &wo->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS);
wo = new_wo;
}
else {
wo = BKE_world_add(bmain, DATA_("World"));
ED_node_shader_default(C, &wo->id);
wo->use_nodes = true;
}
/* hook into UI */ /* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop); UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -896,6 +973,17 @@ static int new_world_exec(bContext *C, wmOperator *UNUSED(op))
RNA_property_pointer_set(&ptr, prop, idptr, NULL); RNA_property_pointer_set(&ptr, prop, idptr, NULL);
RNA_property_update(C, &ptr, prop); RNA_property_update(C, &ptr, prop);
} }
}
static int new_world_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
World *wo = BKE_world_add(bmain, DATA_("World"));
ED_node_shader_default(C, &wo->id);
wo->use_nodes = true;
world_creation_assign(C, wo);
WM_event_add_notifier(C, NC_WORLD | NA_ADDED, wo); WM_event_add_notifier(C, NC_WORLD | NA_ADDED, wo);
@@ -907,7 +995,7 @@ void WORLD_OT_new(wmOperatorType *ot)
/* identifiers */ /* identifiers */
ot->name = "New World"; ot->name = "New World";
ot->idname = "WORLD_OT_new"; ot->idname = "WORLD_OT_new";
ot->description = "Create a new world Data-Block"; ot->description = "Create a new world data-block";
/* api callbacks */ /* api callbacks */
ot->exec = new_world_exec; ot->exec = new_world_exec;
@@ -916,6 +1004,36 @@ void WORLD_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
} }
static int duplicate_world_exec(bContext *C, wmOperator *UNUSED(op))
{
World *wo = CTX_data_pointer_get_type(C, "world", &RNA_World).data;
if (wo) {
Main *bmain = CTX_data_main(C);
wo = (World *)BKE_id_copy(bmain, &wo->id);
}
world_creation_assign(C, wo);
WM_event_add_notifier(C, NC_WORLD | NA_ADDED, wo);
return OPERATOR_FINISHED;
}
void WORLD_OT_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate World";
ot->idname = "WORLD_OT_duplicate";
ot->description = "Duplicate an existing world data-block";
/* api callbacks */
ot->exec = duplicate_world_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */

View File

@@ -5509,7 +5509,6 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(ED_OT_undo_redo); WM_operatortype_append(ED_OT_undo_redo);
WM_operatortype_append(ED_OT_undo_history); WM_operatortype_append(ED_OT_undo_history);
WM_operatortype_append(ED_OT_flush_edits);
WM_operatortype_append(ED_OT_lib_id_load_custom_preview); WM_operatortype_append(ED_OT_lib_id_load_custom_preview);
WM_operatortype_append(ED_OT_lib_id_generate_preview); WM_operatortype_append(ED_OT_lib_id_generate_preview);
} }

View File

@@ -158,7 +158,78 @@ static void actedit_change_action(bContext *C, bAction *act)
RNA_property_update(C, &ptr, prop); RNA_property_update(C, &ptr, prop);
} }
/* ******************** New Action Operator *********************** */ /* ******************** New Action Operators *********************** */
static void action_creation_assign(bContext *C,
bAction *action,
PointerRNA *ptr,
PropertyRNA *prop)
{
PointerRNA idptr;
/* Set the new action.
* NOTE: we can't use actedit_change_action, as this function is also called from the NLA. */
RNA_id_pointer_create(&action->id, &idptr);
RNA_property_pointer_set(ptr, prop, idptr, NULL);
RNA_property_update(C, ptr, prop);
}
/**
* Stash the previously active action to prevent it from being lost.
* \return The old action if any.
*/
static bAction *action_creation_stash_old(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
bAction *oldact = NULL;
AnimData *adt = NULL;
if (prop) {
/* The operator was called from a button. */
PointerRNA oldptr;
oldptr = RNA_property_pointer_get(ptr, prop);
oldact = (bAction *)oldptr.owner_id;
/* stash the old action to prevent it from being lost */
if (ptr->type == &RNA_AnimData) {
adt = ptr->data;
}
else if (ptr->type == &RNA_SpaceDopeSheetEditor) {
adt = ED_actedit_animdata_from_context(C);
}
}
else {
adt = ED_actedit_animdata_from_context(C);
oldact = adt->action;
}
if (!adt || !oldact) {
/* Found nothing to stash in current context. */
return NULL;
}
/* Perform stashing operation. */
if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ptr->owner_id))) {
/* The stash operation will remove the user already
* (and unlink the action from the AnimData action slot).
* Hence, we must unset the ref to the action in the
* action editor too (if this is where we're being called from)
* first before setting the new action once it is created,
* or else the user gets decremented twice!
*/
if (ptr->type == &RNA_SpaceDopeSheetEditor) {
SpaceAction *saction = ptr->data;
saction->action = NULL;
}
}
else {
#if 0
printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
oldact->id.name);
#endif
}
return oldact;
}
/* Criteria: /* Criteria:
* 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions... * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions...
@@ -207,71 +278,17 @@ static bool action_new_poll(bContext *C)
static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) static int action_new_exec(bContext *C, wmOperator *UNUSED(op))
{ {
PointerRNA ptr, idptr; PointerRNA ptr;
PropertyRNA *prop; PropertyRNA *prop;
bAction *oldact = NULL;
AnimData *adt = NULL;
/* hook into UI */ /* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop); UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
action_creation_stash_old(C, &ptr, prop);
bAction *action = action_create_new(C, NULL);
if (prop) { if (prop) {
/* The operator was called from a button. */ action_creation_assign(C, action, &ptr, prop);
PointerRNA oldptr;
oldptr = RNA_property_pointer_get(&ptr, prop);
oldact = (bAction *)oldptr.owner_id;
/* stash the old action to prevent it from being lost */
if (ptr.type == &RNA_AnimData) {
adt = ptr.data;
}
else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
adt = ED_actedit_animdata_from_context(C);
}
}
else {
adt = ED_actedit_animdata_from_context(C);
oldact = adt->action;
}
{
bAction *action = NULL;
/* Perform stashing operation - But only if there is an action */
if (adt && oldact) {
/* stash the action */
if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ptr.owner_id))) {
/* The stash operation will remove the user already
* (and unlink the action from the AnimData action slot).
* Hence, we must unset the ref to the action in the
* action editor too (if this is where we're being called from)
* first before setting the new action once it is created,
* or else the user gets decremented twice!
*/
if (ptr.type == &RNA_SpaceDopeSheetEditor) {
SpaceAction *saction = ptr.data;
saction->action = NULL;
}
}
else {
#if 0
printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
oldact->id.name);
#endif
}
}
/* create action */
action = action_create_new(C, oldact);
if (prop) {
/* set this new action
* NOTE: we can't use actedit_change_action, as this function is also called from the NLA
*/
RNA_id_pointer_create(&action->id, &idptr);
RNA_property_pointer_set(&ptr, prop, idptr, NULL);
RNA_property_update(C, &ptr, prop);
}
} }
/* set notifier that keyframes have changed */ /* set notifier that keyframes have changed */
@@ -285,7 +302,7 @@ void ACTION_OT_new(wmOperatorType *ot)
/* identifiers */ /* identifiers */
ot->name = "New Action"; ot->name = "New Action";
ot->idname = "ACTION_OT_new"; ot->idname = "ACTION_OT_new";
ot->description = "Create new action"; ot->description = "Create a new action";
/* api callbacks */ /* api callbacks */
ot->exec = action_new_exec; ot->exec = action_new_exec;
@@ -295,6 +312,45 @@ void ACTION_OT_new(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
} }
static int action_duplicate_assign_exec(bContext *C, wmOperator *UNUSED(op))
{
PointerRNA ptr;
PropertyRNA *prop;
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
bAction *old_action = action_creation_stash_old(C, &ptr, prop);
bAction *new_action = action_create_new(C, old_action);
if (prop) {
action_creation_assign(C, new_action, &ptr, prop);
}
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL);
return OPERATOR_FINISHED;
}
/**
* Duplicate an action assigned to a templateID and update it's assignment - based on UI context.
*/
void ACTION_OT_duplicate_assign(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate & Assign Action";
ot->idname = "ACTION_OT_duplicate_assign";
ot->description = "Create a copy of an existing action and assign it";
/* api callbacks */
ot->exec = action_duplicate_assign_exec;
ot->poll = action_new_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
/* ******************* Action Push-Down Operator ******************** */ /* ******************* Action Push-Down Operator ******************** */
/* Criteria: /* Criteria:

View File

@@ -106,6 +106,7 @@ void ACTION_OT_snap(struct wmOperatorType *ot);
void ACTION_OT_mirror(struct wmOperatorType *ot); void ACTION_OT_mirror(struct wmOperatorType *ot);
void ACTION_OT_new(struct wmOperatorType *ot); void ACTION_OT_new(struct wmOperatorType *ot);
void ACTION_OT_duplicate_assign(struct wmOperatorType *ot);
void ACTION_OT_unlink(struct wmOperatorType *ot); void ACTION_OT_unlink(struct wmOperatorType *ot);
void ACTION_OT_push_down(struct wmOperatorType *ot); void ACTION_OT_push_down(struct wmOperatorType *ot);

View File

@@ -73,7 +73,9 @@ void action_operatortypes(void)
WM_operatortype_append(ACTION_OT_copy); WM_operatortype_append(ACTION_OT_copy);
WM_operatortype_append(ACTION_OT_paste); WM_operatortype_append(ACTION_OT_paste);
/* UI-context based operators. */
WM_operatortype_append(ACTION_OT_new); WM_operatortype_append(ACTION_OT_new);
WM_operatortype_append(ACTION_OT_duplicate_assign);
WM_operatortype_append(ACTION_OT_unlink); WM_operatortype_append(ACTION_OT_unlink);
WM_operatortype_append(ACTION_OT_push_down); WM_operatortype_append(ACTION_OT_push_down);

View File

@@ -65,6 +65,7 @@
#include "ED_space_api.h" #include "ED_space_api.h"
#include "ED_transform.h" #include "ED_transform.h"
#include "ED_userpref.h" #include "ED_userpref.h"
#include "ED_util.h"
#include "ED_uvedit.h" #include "ED_uvedit.h"
#include "io_ops.h" #include "io_ops.h"
@@ -124,6 +125,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_render(); ED_operatortypes_render();
ED_operatortypes_mask(); ED_operatortypes_mask();
ED_operatortypes_io(); ED_operatortypes_io();
ED_operatortypes_edutils();
ED_operatortypes_view2d(); ED_operatortypes_view2d();
ED_operatortypes_ui(); ED_operatortypes_ui();

View File

@@ -140,6 +140,7 @@ void uiTemplateMovieClip(
ptr, ptr,
propname, propname,
NULL, NULL,
NULL,
"CLIP_OT_open", "CLIP_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,

View File

@@ -180,11 +180,16 @@ static void file_panel_execution_buttons_draw(const bContext *C, Panel *panel)
UI_but_funcN_set(but, file_filename_enter_handle, NULL, but); UI_but_funcN_set(but, file_filename_enter_handle, NULL, but);
if (params->flag & FILE_CHECK_EXISTING) { if (params->flag & FILE_CHECK_EXISTING) {
but_extra_rna_ptr = UI_but_extra_operator_icon_add( uiButExtraOpIcon *extra_icon;
extra_icon = UI_but_extra_operator_icon_add(
but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_REMOVE); but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_REMOVE);
but_extra_rna_ptr = UI_but_extra_operator_icon_opptr_get(extra_icon);
RNA_int_set(but_extra_rna_ptr, "increment", -1); RNA_int_set(but_extra_rna_ptr, "increment", -1);
but_extra_rna_ptr = UI_but_extra_operator_icon_add(
extra_icon = UI_but_extra_operator_icon_add(
but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_ADD); but, "FILE_OT_filenum", WM_OP_EXEC_REGION_WIN, ICON_ADD);
but_extra_rna_ptr = UI_but_extra_operator_icon_opptr_get(extra_icon);
RNA_int_set(but_extra_rna_ptr, "increment", 1); RNA_int_set(but_extra_rna_ptr, "increment", 1);
} }

View File

@@ -806,7 +806,8 @@ void uiTemplateImage(uiLayout *layout,
C, C,
ptr, ptr,
propname, propname,
ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_new",
NULL,
"IMAGE_OT_open", "IMAGE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,

View File

@@ -292,8 +292,9 @@ static void nla_panel_animdata(const bContext *C, Panel *panel)
(bContext *)C, (bContext *)C,
&adt_ptr, &adt_ptr,
"action", "action",
"ACTION_OT_new",
NULL, NULL,
"ACTION_OT_new",
"ACTION_OT_duplicate_assign",
"NLA_OT_action_unlink", "NLA_OT_action_unlink",
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,
false, false,

View File

@@ -771,6 +771,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA
ptr, ptr,
"image", "image",
"IMAGE_OT_new", "IMAGE_OT_new",
NULL,
"IMAGE_OT_open", "IMAGE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,
@@ -808,6 +809,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin
ptr, ptr,
"image", "image",
"IMAGE_OT_new", "IMAGE_OT_new",
NULL,
"IMAGE_OT_open", "IMAGE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,
@@ -1395,6 +1397,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
ptr, ptr,
"image", "image",
"IMAGE_OT_new", "IMAGE_OT_new",
NULL,
"IMAGE_OT_open", "IMAGE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,
@@ -1426,7 +1429,8 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
bNode *node = ptr->data; bNode *node = ptr->data;
uiLayout *col, *row; uiLayout *col, *row;
uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); uiTemplateID(
layout, C, ptr, "scene", NULL, NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
if (!node->id) { if (!node->id) {
return; return;
@@ -1548,7 +1552,8 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
col = uiLayoutColumn(layout, false); col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE);
uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); uiTemplateID(
layout, C, ptr, "scene", NULL, NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
col = uiLayoutColumn(layout, false); col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE);
@@ -2163,8 +2168,17 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe
static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr)
{ {
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
} }
static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -2172,8 +2186,17 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
bNode *node = ptr->data; bNode *node = ptr->data;
PointerRNA clipptr; PointerRNA clipptr;
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
if (!node->id) { if (!node->id) {
return; return;
@@ -2188,8 +2211,17 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe
{ {
bNode *node = ptr->data; bNode *node = ptr->data;
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
if (!node->id) { if (!node->id) {
return; return;
@@ -2214,8 +2246,17 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
{ {
bNode *node = ptr->data; bNode *node = ptr->data;
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
if (!node->id) { if (!node->id) {
return; return;
@@ -2538,7 +2579,8 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
{ {
bNode *node = ptr->data; bNode *node = ptr->data;
uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); uiTemplateID(
layout, C, ptr, "mask", NULL, NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE);
uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE);
@@ -2559,7 +2601,8 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
{ {
bNode *node = ptr->data; bNode *node = ptr->data;
uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); uiTemplateID(
layout, C, ptr, "clip", NULL, NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL);
if (node->id) { if (node->id) {
MovieClip *clip = (MovieClip *)node->id; MovieClip *clip = (MovieClip *)node->id;
@@ -2595,8 +2638,17 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
{ {
bNode *node = ptr->data; bNode *node = ptr->data;
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
if (node->id) { if (node->id) {
MovieClip *clip = (MovieClip *)node->id; MovieClip *clip = (MovieClip *)node->id;
@@ -2636,8 +2688,17 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
bNode *node = ptr->data; bNode *node = ptr->data;
NodePlaneTrackDeformData *data = node->storage; NodePlaneTrackDeformData *data = node->storage;
uiTemplateID( uiTemplateID(layout,
layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); C,
ptr,
"clip",
NULL,
NULL,
"CLIP_OT_open",
NULL,
UI_TEMPLATE_ID_FILTER_ALL,
false,
NULL);
if (node->id) { if (node->id) {
MovieClip *clip = (MovieClip *)node->id; MovieClip *clip = (MovieClip *)node->id;
@@ -3072,6 +3133,7 @@ static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *p
ptr, ptr,
"image", "image",
"IMAGE_OT_new", "IMAGE_OT_new",
NULL,
"IMAGE_OT_open", "IMAGE_OT_open",
NULL, NULL,
UI_TEMPLATE_ID_FILTER_ALL, UI_TEMPLATE_ID_FILTER_ALL,

View File

@@ -39,6 +39,7 @@ set(SRC
ed_transverts.c ed_transverts.c
ed_util.c ed_util.c
ed_util_imbuf.c ed_util_imbuf.c
ed_util_ops.c
gizmo_utils.c gizmo_utils.c
numinput.c numinput.c
select_utils.c select_utils.c

View File

@@ -485,27 +485,6 @@ void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, ID *old_i
} }
} }
static int ed_flush_edits_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ED_editors_flush_edits(bmain);
return OPERATOR_FINISHED;
}
void ED_OT_flush_edits(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Flush Edits";
ot->description = "Flush edit data from active editing modes";
ot->idname = "ED_OT_flush_edits";
/* api callbacks */
ot->exec = ed_flush_edits_exec;
/* flags */
ot->flag = OPTYPE_INTERNAL;
}
static bool lib_id_preview_editing_poll(bContext *C) static bool lib_id_preview_editing_poll(bContext *C)
{ {
const PointerRNA idptr = CTX_data_pointer_get(C, "id"); const PointerRNA idptr = CTX_data_pointer_get(C, "id");

View File

@@ -0,0 +1,155 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edutil
*
* Utility operators for UI data or for the UI to use.
*/
#include <string.h>
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "DNA_windowmanager_types.h"
#include "ED_util.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "WM_api.h"
#include "WM_types.h"
static int lib_fake_user_toggle_exec(bContext *C, wmOperator *op)
{
PropertyPointerRNA pprop;
PointerRNA idptr = PointerRNA_NULL;
UI_context_active_but_prop_get_templateID(C, &pprop.ptr, &pprop.prop);
if (pprop.prop) {
idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop);
}
if ((pprop.prop == NULL) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) {
BKE_report(
op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling");
return OPERATOR_CANCELLED;
}
ID *id = idptr.data;
if ((id->lib != NULL) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) {
BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user");
return OPERATOR_CANCELLED;
}
if (ID_FAKE_USERS(id)) {
id_fake_user_clear(id);
}
else {
id_fake_user_set(id);
}
return OPERATOR_FINISHED;
}
static void ED_OT_lib_fake_user_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Fake User";
ot->description = "Save this data-block even if it has no users";
ot->idname = "ED_OT_lib_fake_user_toggle";
/* api callbacks */
ot->exec = lib_fake_user_toggle_exec;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
static int lib_unlink_exec(bContext *C, wmOperator *op)
{
PropertyPointerRNA pprop;
PointerRNA idptr;
UI_context_active_but_prop_get_templateID(C, &pprop.ptr, &pprop.prop);
if (pprop.prop) {
idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop);
}
if ((pprop.prop == NULL) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) {
BKE_report(
op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling");
return OPERATOR_CANCELLED;
}
memset(&idptr, 0, sizeof(idptr));
RNA_property_pointer_set(&pprop.ptr, pprop.prop, idptr, NULL);
RNA_property_update(C, &pprop.ptr, pprop.prop);
return OPERATOR_FINISHED;
}
static void ED_OT_lib_unlink(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Unlink Data-Block";
ot->description = "Remove a usage of a data-block, clearing the assignment";
ot->idname = "ED_OT_lib_unlink";
/* api callbacks */
ot->exec = lib_unlink_exec;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
}
static int ed_flush_edits_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
ED_editors_flush_edits(bmain);
return OPERATOR_FINISHED;
}
static void ED_OT_flush_edits(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Flush Edits";
ot->description = "Flush edit data from active editing modes";
ot->idname = "ED_OT_flush_edits";
/* api callbacks */
ot->exec = ed_flush_edits_exec;
/* flags */
ot->flag = OPTYPE_INTERNAL;
}
void ED_operatortypes_edutils(void)
{
WM_operatortype_append(ED_OT_lib_fake_user_toggle);
WM_operatortype_append(ED_OT_lib_unlink);
WM_operatortype_append(ED_OT_flush_edits);
}

View File

@@ -480,6 +480,7 @@ static void rna_uiTemplateID(uiLayout *layout,
PointerRNA *ptr, PointerRNA *ptr,
const char *propname, const char *propname,
const char *newop, const char *newop,
const char *duplicateop,
const char *openop, const char *openop,
const char *unlinkop, const char *unlinkop,
int filter, int filter,
@@ -498,7 +499,8 @@ static void rna_uiTemplateID(uiLayout *layout,
/* Get translated name (label). */ /* Get translated name (label). */
name = rna_translate_ui_text(name, text_ctxt, NULL, prop, translate); name = rna_translate_ui_text(name, text_ctxt, NULL, prop, translate);
uiTemplateID(layout, C, ptr, propname, newop, openop, unlinkop, filter, live_icon, name); uiTemplateID(
layout, C, ptr, propname, newop, duplicateop, openop, unlinkop, filter, live_icon, name);
} }
static void rna_uiTemplateAnyID(uiLayout *layout, static void rna_uiTemplateAnyID(uiLayout *layout,
@@ -1155,9 +1157,12 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_function_flag(func, FUNC_USE_CONTEXT);
api_ui_item_rna_common(func); api_ui_item_rna_common(func);
RNA_def_string(func, "new", NULL, 0, "", "Operator identifier to create a new ID block"); RNA_def_string(func, "new", NULL, 0, "", "Operator identifier to create a new ID block");
RNA_def_string(
func, "duplicate", NULL, 0, "", "Operator identifier to duplicate the selected ID block");
RNA_def_string( RNA_def_string(
func, "open", NULL, 0, "", "Operator identifier to open a file for creating a new ID block"); func, "open", NULL, 0, "", "Operator identifier to open a file for creating a new ID block");
RNA_def_string(func, "unlink", NULL, 0, "", "Operator identifier to unlink the ID block"); RNA_def_string(
func, "unlink", NULL, 0, "", "Operator identifier to unlink the selected ID block");
RNA_def_enum(func, RNA_def_enum(func,
"filter", "filter",
id_template_filter_items, id_template_filter_items,

View File

@@ -437,7 +437,7 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true); uiLayoutSetPropSep(layout, true);
uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, NULL, 0, ICON_NONE, NULL);
col = uiLayoutColumn(layout, false); col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, has_texture); uiLayoutSetActive(col, has_texture);

View File

@@ -1031,6 +1031,7 @@ static void panel_draw(const bContext *C, Panel *panel)
"node.new_geometry_node_group_assign", "node.new_geometry_node_group_assign",
nullptr, nullptr,
nullptr, nullptr,
nullptr,
0, 0,
false, false,
nullptr); nullptr);

View File

@@ -113,7 +113,8 @@ static void panel_draw(const bContext *C, Panel *panel)
uiLayoutSetPropSep(layout, true); uiLayoutSetPropSep(layout, true);
uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr); uiTemplateID(
layout, C, ptr, "texture", "texture.new", nullptr, nullptr, nullptr, 0, ICON_NONE, nullptr);
uiItemR(layout, ptr, "texture_map_mode", 0, "Texture Mapping", ICON_NONE); uiItemR(layout, ptr, "texture_map_mode", 0, "Texture Mapping", ICON_NONE);
if (vdmd->texture_map_mode == MOD_VOLUME_DISPLACE_MAP_OBJECT) { if (vdmd->texture_map_mode == MOD_VOLUME_DISPLACE_MAP_OBJECT) {

View File

@@ -466,7 +466,17 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
int texture_coords = RNA_enum_get(ptr, "texture_coords"); int texture_coords = RNA_enum_get(ptr, "texture_coords");
uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); uiTemplateID(layout,
C,
ptr,
"texture",
"texture.new",
"texture.duplicate",
NULL,
NULL,
0,
ICON_NONE,
NULL);
uiLayoutSetPropSep(layout, true); uiLayoutSetPropSep(layout, true);

View File

@@ -436,7 +436,17 @@ static void texture_panel_draw(const bContext *C, Panel *panel)
int texture_coords = RNA_enum_get(ptr, "texture_coords"); int texture_coords = RNA_enum_get(ptr, "texture_coords");
uiTemplateID(layout, C, ptr, "texture", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); uiTemplateID(layout,
C,
ptr,
"texture",
"texture.new",
"texture.duplicate",
NULL,
NULL,
0,
ICON_NONE,
NULL);
uiLayoutSetPropSep(layout, true); uiLayoutSetPropSep(layout, true);

View File

@@ -363,6 +363,7 @@ void weightvg_ui_common(const bContext *C, PointerRNA *ob_ptr, PointerRNA *ptr,
ptr, ptr,
"mask_texture", "mask_texture",
"texture.new", "texture.new",
"texture.duplicate",
NULL, NULL,
NULL, NULL,
0, 0,

View File

@@ -596,7 +596,7 @@ char *WM_operatortype_description(struct bContext *C,
struct wmOperatorType *ot, struct wmOperatorType *ot,
struct PointerRNA *properties) struct PointerRNA *properties)
{ {
if (ot->get_description && properties) { if (C && ot->get_description && properties) {
char *description = ot->get_description(C, ot, properties); char *description = ot->get_description(C, ot, properties);
if (description) { if (description) {