diff --git a/client/ayon_core/tools/launcher/ui/hierarchy_page.py b/client/ayon_core/tools/launcher/ui/hierarchy_page.py index 65efdc27ac..47388d9685 100644 --- a/client/ayon_core/tools/launcher/ui/hierarchy_page.py +++ b/client/ayon_core/tools/launcher/ui/hierarchy_page.py @@ -12,6 +12,8 @@ from ayon_core.tools.utils import ( ) from ayon_core.tools.utils.lib import checkstate_int_to_enum +from .workfiles_page import WorkfilesPage + class HierarchyPage(QtWidgets.QWidget): def __init__(self, controller, parent): @@ -73,10 +75,15 @@ class HierarchyPage(QtWidgets.QWidget): # - Tasks widget tasks_widget = TasksWidget(controller, content_body) + # - Third page - Workfiles + workfiles_page = WorkfilesPage(controller, content_body) + content_body.addWidget(folders_widget) content_body.addWidget(tasks_widget) - content_body.setStretchFactor(0, 100) - content_body.setStretchFactor(1, 65) + content_body.addWidget(workfiles_page) + content_body.setStretchFactor(0, 120) + content_body.setStretchFactor(1, 85) + content_body.setStretchFactor(2, 220) main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) @@ -99,6 +106,7 @@ class HierarchyPage(QtWidgets.QWidget): self._my_tasks_checkbox = my_tasks_checkbox self._folders_widget = folders_widget self._tasks_widget = tasks_widget + self._workfiles_page = workfiles_page self._project_name = None @@ -117,6 +125,7 @@ class HierarchyPage(QtWidgets.QWidget): def refresh(self): self._folders_widget.refresh() self._tasks_widget.refresh() + self._workfiles_page.refresh() self._on_my_tasks_checkbox_state_changed( self._my_tasks_checkbox.checkState() ) diff --git a/client/ayon_core/tools/launcher/ui/window.py b/client/ayon_core/tools/launcher/ui/window.py index 819e141d59..ad2fd2d3c2 100644 --- a/client/ayon_core/tools/launcher/ui/window.py +++ b/client/ayon_core/tools/launcher/ui/window.py @@ -177,7 +177,7 @@ class LauncherWindow(QtWidgets.QWidget): self._page_slide_anim = page_slide_anim hierarchy_page.setVisible(not self._is_on_projects_page) - self.resize(520, 740) + self.resize(920, 740) def showEvent(self, event): super().showEvent(event) diff --git a/client/ayon_core/tools/launcher/ui/workfiles_page.py b/client/ayon_core/tools/launcher/ui/workfiles_page.py new file mode 100644 index 0000000000..2f390f1bee --- /dev/null +++ b/client/ayon_core/tools/launcher/ui/workfiles_page.py @@ -0,0 +1,178 @@ +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.launcher.abstract import AbstractLauncherFrontEnd + +VERSION_ROLE = QtCore.Qt.UserRole + 1 + + +class WorkfilesModel(QtGui.QStandardItemModel): + refreshed = QtCore.Signal() + + def __init__(self, controller: AbstractLauncherFrontEnd) -> None: + super().__init__() + + self.setColumnCount(1) + self.setHeaderData(0, QtCore.Qt.Horizontal, "Workfiles") + + controller.register_event_callback( + "selection.project.changed", + self._on_selection_project_changed, + ) + controller.register_event_callback( + "selection.folder.changed", + self._on_selection_folder_changed, + ) + controller.register_event_callback( + "selection.task.changed", + self._on_selection_task_changed, + ) + + self._controller = controller + self._selected_project_name = None + self._selected_folder_id = None + self._selected_task_id = None + + self._transparent_icon = None + + self._cached_icons = {} + + def refresh(self) -> None: + root_item = self.invisibleRootItem() + root_item.removeRows(0, root_item.rowCount()) + + workfile_items = self._controller.get_workfile_items( + self._selected_project_name, self._selected_task_id + ) + new_items = [] + for workfile_item in workfile_items: + icon = self._get_icon(workfile_item.icon) + item = QtGui.QStandardItem(workfile_item.filename) + item.setData(icon, QtCore.Qt.DecorationRole) + item.setData(workfile_item.version, VERSION_ROLE) + flags = QtCore.Qt.NoItemFlags + if workfile_item.exists: + flags = QtCore.Qt.ItemIsEnabled + item.setFlags(flags) + new_items.append(item) + + if not new_items: + title = "< No workfiles >" + if not self._selected_project_name: + title = "< Select a project >" + elif not self._selected_folder_id: + title = "< Select a folder >" + elif not self._selected_task_id: + title = "< Select a task >" + item = QtGui.QStandardItem(title) + item.setFlags(QtCore.Qt.NoItemFlags) + new_items.append(item) + root_item.appendRows(new_items) + + self.refreshed.emit() + + def _on_selection_project_changed(self, event) -> None: + self._selected_project_name = event["project_name"] + self._selected_folder_id = None + self._selected_task_id = None + self.refresh() + + def _on_selection_folder_changed(self, event) -> None: + self._selected_project_name = event["project_name"] + self._selected_folder_id = event["folder_id"] + self._selected_task_id = None + self.refresh() + + def _on_selection_task_changed(self, event) -> None: + self._selected_project_name = event["project_name"] + self._selected_folder_id = event["folder_id"] + self._selected_task_id = event["task_id"] + self.refresh() + + def _get_transparent_icon(self) -> QtGui.QIcon: + if self._transparent_icon is None: + self._transparent_icon = get_qt_icon({ + "type": "transparent", "size": 256 + }) + return self._transparent_icon + + def _get_icon(self, icon_url: Optional[str]) -> QtGui.QIcon: + if icon_url is None: + return self._get_transparent_icon() + icon = self._cached_icons.get(icon_url) + if icon is not None: + return icon + + base_url = ayon_api.get_base_url() + if icon_url.startswith(base_url): + icon_def = { + "type": "ayon_url", + "url": icon_url[len(base_url) + 1:], + } + else: + icon_def = { + "type": "url", + "url": icon_url, + } + + icon = get_qt_icon(icon_def) + if icon is None: + icon = self._get_transparent_icon() + self._cached_icons[icon_url] = icon + return icon + + +class WorkfilesProxyModel(QtCore.QSortFilterProxyModel): + def lessThan(self, left, right) -> bool: + # left_version = left.data(VERSION_ROLE) + # right_version = right.data(VERSION_ROLE) + # if left_version != right_version: + # if left_version is None: + # return False + # if right_version is None: + # return True + # + # return left_version > right_version + return not super().lessThan(left, right) + + +class WorkfilesView(QtWidgets.QTreeView): + def drawBranches(self, painter, rect, index): + return + + +class WorkfilesPage(QtWidgets.QWidget): + def __init__( + self, + controller: AbstractLauncherFrontEnd, + parent: QtWidgets.QWidget, + ) -> None: + super().__init__(parent) + + workfiles_view = WorkfilesView(self) + workfiles_view.setIndentation(0) + workfiles_model = WorkfilesModel(controller) + workfiles_proxy = WorkfilesProxyModel() + workfiles_proxy.setSourceModel(workfiles_model) + + workfiles_view.setModel(workfiles_proxy) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(workfiles_view, 1) + + workfiles_model.refreshed.connect(self._on_refresh) + + self._controller = controller + self._workfiles_view = workfiles_view + self._workfiles_model = workfiles_model + self._workfiles_proxy = workfiles_proxy + + def refresh(self) -> None: + self._workfiles_model.refresh() + + def _on_refresh(self) -> None: + self._workfiles_proxy.sort(0)