show grouped actions as menu

This commit is contained in:
Jakub Trllo 2025-08-25 16:31:07 +02:00
parent 1768543b8b
commit f100a6c563
2 changed files with 42 additions and 29 deletions

View file

@ -322,9 +322,9 @@ class ActionItem:
entity_ids (set[str]): Entity ids.
entity_type (str): Entity type.
label (str): Action label.
group_label (str): Group label.
icon (dict[str, Any]): Action icon definition.
tooltip (str): Action tooltip.
group_label (Optional[str]): Group label.
icon (Optional[dict[str, Any]]): Action icon definition.
tooltip (Optional[str]): Action tooltip.
order (int): Action order.
options (Union[list[AbstractAttrDef], list[qargparse.QArgument]]):
Action options. Note: 'qargparse' is considered as deprecated.
@ -332,16 +332,16 @@ class ActionItem:
"""
def __init__(
self,
plugin_identifier,
identifier,
entity_ids,
entity_type,
label,
group_label,
icon,
tooltip,
order,
options,
plugin_identifier: str,
identifier: str,
entity_ids: set[str],
entity_type: str,
label: str,
group_label: Optional[str],
icon: Optional[dict[str, Any]],
tooltip: Optional[str],
order: int,
options: Optional[list],
):
self.plugin_identifier = plugin_identifier
self.identifier = identifier
@ -364,13 +364,12 @@ class ActionItem:
# future development of detached UI tools it would be better to be
# prepared for it.
raise NotImplementedError(
"{}.to_data is not implemented. Use Attribute definitions"
" from 'ayon_core.lib' instead of 'qargparse'.".format(
self.__class__.__name__
)
f"{self.__class__.__name__}.to_data is not implemented."
" Use Attribute definitions from 'ayon_core.lib'"
" instead of 'qargparse'."
)
def to_data(self):
def to_data(self) -> dict[str, Any]:
options = self._options_to_data()
return {
"plugin_identifier": self.plugin_identifier,
@ -386,7 +385,7 @@ class ActionItem:
}
@classmethod
def from_data(cls, data):
def from_data(cls, data) -> "ActionItem":
options = data["options"]
if options:
options = deserialize_attr_defs(options)

View file

@ -15,15 +15,18 @@ from ayon_core.tools.utils import get_qt_icon
from ayon_core.tools.loader.abstract import ActionItem
def _actions_sorter(item: tuple[str, ActionItem]):
def _actions_sorter(item: tuple[ActionItem, str, str]):
"""Sort the Loaders by their order and then their name.
Returns:
tuple[int, str]: Sort keys.
"""
label, action_item = item
return action_item.order, label
action_item, group_label, label = item
if group_label is None:
group_label = label
label = ""
return action_item.order, group_label, label
def show_actions_menu(
@ -46,21 +49,21 @@ def show_actions_menu(
action_items_with_labels = []
for action_item in action_items:
label = action_item.label
if action_item.group_label:
label = f"{action_item.group_label} ({label})"
action_items_with_labels.append((label, action_item))
action_items_with_labels.append(
(action_item, action_item.group_label, action_item.label)
)
group_menu_by_label = {}
action_items_by_id = {}
for item in sorted(action_items_with_labels, key=_actions_sorter):
label, action_item = item
action_item, _, _ = item
item_id = uuid.uuid4().hex
action_items_by_id[item_id] = action_item
item_options = action_item.options
icon = get_qt_icon(action_item.icon)
use_option = bool(item_options)
action = OptionalAction(
label,
action_item.label,
icon,
use_option,
menu
@ -76,7 +79,18 @@ def show_actions_menu(
action.setData(item_id)
menu.addAction(action)
group_label = action_item.group_label
if group_label:
group_menu = group_menu_by_label.get(group_label)
if group_menu is None:
group_menu = OptionalMenu(group_label, menu)
if icon is not None:
group_menu.setIcon(icon)
menu.addMenu(group_menu)
group_menu_by_label[group_label] = group_menu
group_menu.addAction(action)
else:
menu.addAction(action)
action = menu.exec_(global_point)
if action is not None: