Fix: Nodes: bad ui for adding panel toggles
Adding panel toggles in nodegroups have somewhat of a UX antipattern. When running the operator, it checks for conditions that indicate it should not run, and if those are hit, it cancels execution and mentions the invalid condition in the footer bar. This is not ideal, the user should not have to call the operator to find out whether it can be called. Why it got implemented like this is likely a consequence of all interface items being the same "New Item" operator. Poll functions cannot use operator properties, so variants of the same operator cannot check for different conditions for execution. This is a problem for panel toggles, as they have more restrictions to when they can be added that don't apply to other interface items. This patch creates a separate operator for adding panel toggles. This allows the condition checks to be implemented in the poll function, which enables greying out the operator buttons and showing on tooltips what condition is invalid. Pull Request: https://projects.blender.org/blender/blender/pulls/146379
This commit is contained in:
committed by
Jacques Lucke
parent
a1fde8bed8
commit
ce88d773db
@@ -916,32 +916,15 @@ class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator):
|
||||
bl_label = "New Item"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def get_items(_self, context):
|
||||
items = [
|
||||
('INPUT', "Input", ""),
|
||||
('OUTPUT', "Output", ""),
|
||||
('PANEL', "Panel", ""),
|
||||
]
|
||||
|
||||
if context is None:
|
||||
return items
|
||||
|
||||
snode = context.space_data
|
||||
tree = snode.edit_tree
|
||||
interface = tree.interface
|
||||
|
||||
active_item = interface.active
|
||||
# Panels have the extra option to add a toggle.
|
||||
if active_item and active_item.item_type == 'PANEL':
|
||||
items.append(('PANEL_TOGGLE', "Panel Toggle", ""))
|
||||
|
||||
return items
|
||||
|
||||
item_type: EnumProperty(
|
||||
name="Item Type",
|
||||
description="Type of the item to create",
|
||||
items=get_items,
|
||||
default=0,
|
||||
items=(
|
||||
('INPUT', "Input", ""),
|
||||
('OUTPUT', "Output", ""),
|
||||
('PANEL', "Panel", ""),
|
||||
),
|
||||
default='INPUT',
|
||||
)
|
||||
|
||||
# Returns a valid socket type for the given tree or None.
|
||||
@@ -977,18 +960,6 @@ class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator):
|
||||
item = interface.new_socket("Socket", socket_type=self.find_valid_socket_type(tree), in_out='OUTPUT')
|
||||
elif self.item_type == 'PANEL':
|
||||
item = interface.new_panel("Panel")
|
||||
elif self.item_type == 'PANEL_TOGGLE':
|
||||
active_panel = active_item
|
||||
if len(active_panel.interface_items) > 0:
|
||||
first_item = active_panel.interface_items[0]
|
||||
if type(first_item) is bpy.types.NodeTreeInterfaceSocketBool and first_item.is_panel_toggle:
|
||||
self.report({'INFO'}, "Panel already has a toggle")
|
||||
return {'CANCELLED'}
|
||||
item = interface.new_socket(active_panel.name, socket_type='NodeSocketBool', in_out='INPUT')
|
||||
item.is_panel_toggle = True
|
||||
interface.move_to_parent(item, active_panel, 0)
|
||||
# Return in this case because we don't want to move the item.
|
||||
return {'FINISHED'}
|
||||
else:
|
||||
return {'CANCELLED'}
|
||||
|
||||
@@ -1003,6 +974,55 @@ class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class NODE_OT_interface_item_new_panel_toggle(Operator):
|
||||
'''Add a checkbox to the currently selected panel'''
|
||||
bl_idname = "node.interface_item_new_panel_toggle"
|
||||
bl_label = "New Panel Toggle"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
@staticmethod
|
||||
def get_panel_toggle(panel):
|
||||
if len(panel.interface_items) > 0:
|
||||
first_item = panel.interface_items[0]
|
||||
if type(first_item) is bpy.types.NodeTreeInterfaceSocketBool and first_item.is_panel_toggle:
|
||||
return first_item
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
try:
|
||||
snode = context.space_data
|
||||
tree = snode.edit_tree
|
||||
interface = tree.interface
|
||||
|
||||
active_item = interface.active
|
||||
|
||||
if active_item.item_type != 'PANEL':
|
||||
cls.poll_message_set("Active item is not a panel")
|
||||
return False
|
||||
|
||||
if cls.get_panel_toggle(active_item) is not None:
|
||||
cls.poll_message_set("Panel already has a toggle")
|
||||
return False
|
||||
|
||||
return True
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def execute(self, context):
|
||||
snode = context.space_data
|
||||
tree = snode.edit_tree
|
||||
|
||||
interface = tree.interface
|
||||
active_panel = interface.active
|
||||
|
||||
item = interface.new_socket(active_panel.name, socket_type='NodeSocketBool', in_out='INPUT')
|
||||
item.is_panel_toggle = True
|
||||
interface.move_to_parent(item, active_panel, 0)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class NODE_OT_interface_item_duplicate(NodeInterfaceOperator, Operator):
|
||||
"""Add a copy of the active item to the interface"""
|
||||
bl_idname = "node.interface_item_duplicate"
|
||||
@@ -1309,6 +1329,7 @@ classes = (
|
||||
NODE_OT_add_closure_zone,
|
||||
NODE_OT_collapse_hide_unused_toggle,
|
||||
NODE_OT_interface_item_new,
|
||||
NODE_OT_interface_item_new_panel_toggle,
|
||||
NODE_OT_interface_item_duplicate,
|
||||
NODE_OT_interface_item_remove,
|
||||
NODE_OT_interface_item_make_panel_toggle,
|
||||
|
||||
@@ -1021,6 +1021,19 @@ class NODE_MT_node_tree_interface_context_menu(Menu):
|
||||
layout.operator("node.interface_item_unlink_panel_toggle")
|
||||
|
||||
|
||||
class NODE_MT_node_tree_interface_new_item(Menu):
|
||||
bl_label = "New Item"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator_enum("node.interface_item_new", "item_type")
|
||||
|
||||
active_item = context.space_data.edit_tree.interface.active
|
||||
|
||||
if active_item.item_type == 'PANEL':
|
||||
layout.operator("node.interface_item_new_panel_toggle", text="Panel Toggle")
|
||||
|
||||
|
||||
class NODE_PT_node_tree_properties(Panel):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
@@ -1175,6 +1188,7 @@ classes = (
|
||||
NODE_PT_geometry_node_tool_options,
|
||||
NODE_PT_node_color_presets,
|
||||
NODE_PT_node_tree_properties,
|
||||
NODE_MT_node_tree_interface_new_item,
|
||||
NODE_MT_node_tree_interface_context_menu,
|
||||
NODE_PT_node_tree_animation,
|
||||
NODE_PT_active_node_generic,
|
||||
|
||||
@@ -57,7 +57,7 @@ void node_tree_interface_draw(bContext &C, uiLayout &layout, bNodeTree &tree)
|
||||
|
||||
uiLayout &col = row.column(true);
|
||||
col.enabled_set(ID_IS_EDITABLE(&tree.id));
|
||||
col.op_menu_enum(&C, "node.interface_item_new", "item_type", "", ICON_ADD);
|
||||
col.menu("NODE_MT_node_tree_interface_new_item", "", ICON_ADD);
|
||||
col.op("node.interface_item_remove", "", ICON_REMOVE);
|
||||
col.separator();
|
||||
col.menu("NODE_MT_node_tree_interface_context_menu", "", ICON_DOWNARROW_HLT);
|
||||
|
||||
Reference in New Issue
Block a user