diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index 47388d9685..dc535db5fc 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -15,6 +15,36 @@ from ayon_core.tools.utils.lib import checkstate_int_to_enum from .workfiles_page import WorkfilesPage +class LauncherFoldersWidget(FoldersWidget): + focused_in = QtCore.Signal() + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._folders_view.installEventFilter(self) + + def eventFilter(self, obj, event): + if event.type() == QtCore.QEvent.FocusIn: + self.focused_in.emit() + return False + + +class LauncherTasksWidget(TasksWidget): + focused_in = QtCore.Signal() + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._tasks_view.installEventFilter(self) + + def deselect(self): + sel_model = self._tasks_view.selectionModel() + sel_model.clearSelection() + + def eventFilter(self, obj, event): + if event.type() == QtCore.QEvent.FocusIn: + self.focused_in.emit() + return False + + class HierarchyPage(QtWidgets.QWidget): def __init__(self, controller, parent): super().__init__(parent) @@ -68,12 +98,12 @@ class HierarchyPage(QtWidgets.QWidget): filters_layout.addWidget(my_tasks_checkbox, 0) # - Folders widget - folders_widget = FoldersWidget(controller, content_body) + folders_widget = LauncherFoldersWidget(controller, content_body) folders_widget.set_header_visible(True) folders_widget.set_deselectable(True) # - Tasks widget - tasks_widget = TasksWidget(controller, content_body) + tasks_widget = LauncherTasksWidget(controller, content_body) # - Third page - Workfiles workfiles_page = WorkfilesPage(controller, content_body) @@ -97,6 +127,8 @@ class HierarchyPage(QtWidgets.QWidget): my_tasks_checkbox.stateChanged.connect( self._on_my_tasks_checkbox_state_changed ) + folders_widget.focused_in.connect(self._on_folders_focus) + tasks_widget.focused_in.connect(self._on_tasks_focus) self._is_visible = False self._controller = controller @@ -151,3 +183,9 @@ class HierarchyPage(QtWidgets.QWidget): task_ids = entity_ids["task_ids"] self._folders_widget.set_folder_ids_filter(folder_ids) self._tasks_widget.set_task_ids_filter(task_ids) + + def _on_folders_focus(self): + self._workfiles_page.deselect() + + def _on_tasks_focus(self): + self._workfiles_page.deselect() diff --git a/client/ayon_core/tools/launcher/ui/workfiles_page.py b/client/ayon_core/tools/launcher/ui/workfiles_page.py index 1ea223031e..d81221f38d 100644 --- a/client/ayon_core/tools/launcher/ui/workfiles_page.py +++ b/client/ayon_core/tools/launcher/ui/workfiles_page.py @@ -3,7 +3,7 @@ from typing import Optional import ayon_api from qtpy import QtCore, QtWidgets, QtGui -from ayon_core.tools.utils import get_qt_icon +from ayon_core.tools.utils import get_qt_icon, DeselectableTreeView from ayon_core.tools.launcher.abstract import AbstractLauncherFrontEnd VERSION_ROLE = QtCore.Qt.UserRole + 1 @@ -127,7 +127,7 @@ class WorkfilesModel(QtGui.QStandardItemModel): return icon -class WorkfilesView(QtWidgets.QTreeView): +class WorkfilesView(DeselectableTreeView): def drawBranches(self, painter, rect, index): return @@ -165,6 +165,10 @@ class WorkfilesPage(QtWidgets.QWidget): def refresh(self) -> None: self._workfiles_model.refresh() + def deselect(self): + sel_model = self._workfiles_view.selectionModel() + sel_model.clearSelection() + def _on_refresh(self) -> None: self._workfiles_proxy.sort(0, QtCore.Qt.DescendingOrder) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 84786a671e..ca95b1ff1a 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -202,7 +202,7 @@ class ContextCardWidget(CardWidget): Is not visually under group widget and is always at the top of card view. """ - def __init__(self, parent): + def __init__(self, parent: QtWidgets.QWidget): super().__init__(parent) self._id = CONTEXT_ID @@ -211,7 +211,7 @@ class ContextCardWidget(CardWidget): icon_widget = PublishPixmapLabel(None, self) icon_widget.setObjectName("ProductTypeIconLabel") - label_widget = QtWidgets.QLabel(CONTEXT_LABEL, self) + label_widget = QtWidgets.QLabel(f"{CONTEXT_LABEL}", self) icon_layout = QtWidgets.QHBoxLayout() icon_layout.setContentsMargins(5, 5, 5, 5) @@ -288,6 +288,8 @@ class InstanceCardWidget(CardWidget): self._last_product_name = None self._last_variant = None self._last_label = None + self._last_folder_path = None + self._last_task_name = None icon_widget = IconValuePixmapLabel(group_icon, self) icon_widget.setObjectName("ProductTypeIconLabel") @@ -383,29 +385,54 @@ class InstanceCardWidget(CardWidget): self._icon_widget.setVisible(valid) self._context_warning.setVisible(not valid) + @staticmethod + def _get_card_widget_sub_label( + folder_path: Optional[str], + task_name: Optional[str], + ) -> str: + sublabel = "" + if folder_path: + folder_name = folder_path.rsplit("/", 1)[-1] + sublabel = f"{folder_name}" + if task_name: + sublabel += f" - {task_name}" + return sublabel + def _update_product_name(self): variant = self.instance.variant product_name = self.instance.product_name label = self.instance.label + folder_path = self.instance.folder_path + task_name = self.instance.task_name if ( variant == self._last_variant and product_name == self._last_product_name and label == self._last_label + and folder_path == self._last_folder_path + and task_name == self._last_task_name ): return self._last_variant = variant self._last_product_name = product_name self._last_label = label + self._last_folder_path = folder_path + self._last_task_name = task_name + # Make `variant` bold label = html_escape(self.instance.label) found_parts = set(re.findall(variant, label, re.IGNORECASE)) if found_parts: for part in found_parts: - replacement = "{}".format(part) + replacement = f"{part}" label = label.replace(part, replacement) + label = f"{label}" + sublabel = self._get_card_widget_sub_label(folder_path, task_name) + if sublabel: + label += f"
{sublabel}" + self._label_widget.setText(label) # HTML text will cause that label start catch mouse clicks # - disabling with changing interaction flag @@ -702,11 +729,9 @@ class InstanceCardView(AbstractInstanceView): def refresh(self): """Refresh instances in view based on CreatedContext.""" - self._make_sure_context_widget_exists() self._update_convertors_group() - context_info_by_id = self._controller.get_instances_context_info() # Prepare instances by group and identifiers by group @@ -814,6 +839,8 @@ class InstanceCardView(AbstractInstanceView): widget.setVisible(False) widget.deleteLater() + sorted_group_names.insert(0, CONTEXT_GROUP) + self._parent_id_by_id = parent_id_by_id self._instance_ids_by_parent_id = instance_ids_by_parent_id self._group_name_by_instance_id = group_by_instance_id @@ -881,7 +908,7 @@ class InstanceCardView(AbstractInstanceView): context_info, is_parent_active, group_icon, - group_widget + group_widget, ) widget.selected.connect(self._on_widget_selection) widget.active_changed.connect(self._on_active_changed)