Skip to content

UI: Browser - Support views - #1970

Closed
mahesh-ynput wants to merge 56 commits into
ui-qt-browserfrom
ui-qt-browser_view_fixes
Closed

mahesh-ynput wants to merge 56 commits into
ui-qt-browserfrom
ui-qt-browser_view_fixes

Conversation

@mahesh-ynput

@mahesh-ynput mahesh-ynput commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Changelog Description

Fix all bugs in "views" functionality in redesigned-browser

Additional info

Local browser "views" functionality should match with web

Fix #1806

Testing notes:

  1. Create different kind of views from products page and share it with multiple users with different permissions
  2. Login different users from launcher and cross check complete "views" functionality should match with web
  3. Sorting, hiding columns, order of columns should be same as we see in web view to local view with respective views

TODO:

  • - 1) Clicking on "working view" should unselect the view(match with web) and close the menu and update the working view
  • - 2) maintain only "desktop.browser"
  • - 3) save,load and change modified state on modifications of card/table view and any changes customize menu as same as currently doing for filters
  • - 4) Fix default settings loading(studio/project)
  • - 5) When loading any default settings(studio/project or clicking on "reset" should unselect if has any view selection and set to selection to working view)
  • - 6) For some scenario icon highlight is not removing
  • - 7) use 'share api' for user access permission control
  • - 8) Make 'working view' as always in selection unless if any view loaded and it's modified
  • - 9) Fix "All projects" in scope combo in view editor
  • - 10) In editor add icons for users, match it with web

@mahesh-ynput mahesh-ynput self-assigned this Jul 24, 2026
@mahesh-ynput
mahesh-ynput requested a review from Copilot July 24, 2026 04:40
@ynbot ynbot added the size/XXL label Jul 24, 2026
@mahesh-ynput
mahesh-ynput requested a review from BigRoy July 24, 2026 04:40
@mahesh-ynput
mahesh-ynput removed the request for review from Copilot July 24, 2026 04:40
@mahesh-ynput

mahesh-ynput commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author
  • Remove unwanted filters.
  • Update Filter Names match with web.
  • Update Filter edit When try to edit filter instead of showing it attributes it's only showing search option Eg: to modify status filter if click on it it should show status list but it showing search option. Note: This behaviour can see only when filters are applied with any view selection
  • Fix Group By so clearing the selection removes the applied grouping.
  • Rename Working to Working View, and on action should clear the applied filters.
  • Fix Star Icon Working needs to remove from view, (@BigRoy any idea why this here?)
  • Add a Save option to persist changes made to the current view.
  • Update Edit View Scope should be disabled old views only should enable for new view creation
  • Update New View Scope to display the project name and include an All Projects
  • Update Edit View & New View Fetch data for "People with access"
  • Fix Create New View functionality, so new views are created successfully.
  • Separate My Views and Shared Views into same like in web currently it showing all in "My views"
  • Add Columns order should be with user view? @BigRoy
  • Custom attribute values are not populating to table and filter

@mahesh-ynput mahesh-ynput mentioned this pull request Jul 24, 2026
4 tasks
@mahesh-ynput
mahesh-ynput changed the base branch from develop to ui-qt-browser July 24, 2026 08:21
@BigRoy

BigRoy commented Jul 24, 2026

Copy link
Copy Markdown
Member
  • Fix Star Icon Working needs to remove from view, (@BigRoy any idea why this here?)

No idea - it should really behave like the web frontend. I don't see a reason for that 'star'.

Add Columns order should be with user view? @BigRoy

The columns + filter should be included in the view, the slicer I believe should not. The slicer mode should be included, but not the slicer selection. (So it should remember whether it's on "Hierarchy" or "Reviews")

Also, note that the top "Folders" and "Reviews" tabs are still there only for development purposes, so that you can compare easily with the old loader - upon release we will NOT have that separation (those tabs won't be there).

Columns being hidable and reorderable, please in separate PR


It's also important that the views logic is mostly a "state manager" and preferably this state that it manages is not hardcoded into the "Views" logic. Because we may get similar Views support in e.g. Launcher or other tools. As such, it'd be great to keep in mind that the views purely store a named state and manage that, but it's up to what this particular view manager is connected to it to adjust based on it so that it can be used in a variety of tools as the "view state manager" etc.

@mahesh-ynput
mahesh-ynput force-pushed the ui-qt-browser_view_fixes branch from 4ad922f to 75eb8d0 Compare July 25, 2026 04:17
… hierarchy instance has only entity "versions" and review_sessions instance will have entity type per session so no where we needed to filter entityType

- Disabled folderName filter for hierarchy instance those can be filtered directly from ReviewSlicer
  - replaced the shared "versions" view type (a web UI identifier)
    - "desktop.browser" for browser table instance filters
    - "desktop.reviews" for review_sessions table filters
- replace raw_post/raw_patch with ayon_api.post/ayon_api.patch
- fixed save_view by "id" pop from POST payload (server rejected "" as an invalid entity ID)
- _review_table.py clearing applied filters on deletion of view
- view_selector.py capturing current UI state into the view before editing so it can save the changes
… later help to highlight view button upon any modifications in view
…yle after init in buttons.py

- Changing "view" menubutton color to blue same as web when any filter is modified in view_selector.py
Comment thread client/ayon_core/tools/loader/ui/_review_table.py Outdated
Comment thread client/ayon_core/tools/loader/ui/_review_table.py
Comment thread client/ayon_core/ui/components/views/view_selector.py Outdated
Comment thread client/ayon_core/ui/components/views/view_selector.py Outdated
Comment thread client/ayon_core/ui/components/buttons.py
@ynbot ynbot moved this to Review In Progress in PR reviewing Jul 26, 2026
…e them further

- improved filter capture method by using view bindings
…elpers

- added _HoverReveal filter to match UX match with web.
@mahesh-ynput

Copy link
Copy Markdown
Collaborator Author

In the View lists:

  • The save button should appear on any row if you hover over it, but it should be there by default (even when not hovering) and highlighted on the row of your list active view from which you started making changes, e.g.

    • Enter view "X"
    • Make some edits
    • You're now in a working view, but on view "X" you see the highlighted save button.
    • However, if you hover another existing view you also get the save button so you can always save your working view into any existing views.

21dd7f3

@mahesh-ynput

Copy link
Copy Markdown
Collaborator Author

@BigRoy , Currently I see on clicking of "working view" reset in web it just looking for any default view we set at project or studio else clearing all filters. Can you please confirm the same logic we should apply here in local browser side too?

@BigRoy

BigRoy commented Jul 27, 2026

Copy link
Copy Markdown
Member

@BigRoy , Currently I see on clicking of "working view" reset in web it just looking for any default view we set at project or studio else clearing all filters. Can you please confirm the same logic we should apply here in local browser side too?

Yes - if we can support studio/project level defaults then that's the way it should work:

Reset to default means:

  • reset to project default if set
  • reset to studio default if set
  • reset to AYON default

- added default view section for studio and project default view
- removed legacy visibility fallback and enforce web format payload validation in desktop models/tests
Comment thread client/ayon_core/ui/components/views/view_selector.py Outdated
@mahesh-ynput

Copy link
Copy Markdown
Collaborator Author

@BigRoy, UI part is ready, need few enhancements but I need right end points to sharing view with access, set studio/project defaults

Comment on lines +256 to +265
DEFAULT_SORT_BY = None
DEFAULT_SORT_DESC = False
DEFAULT_ROW_HEIGHT = 34
DEFAULT_GROUPING = GroupingDef()
DEFAULT_FILTER = FilterDef()
DEFAULT_EXTRA = {
"gridHeight": 230,
"displayType": "table",
"featuredVersionOrder": ["latestDone", "latest", "hero"],
}

@iLLiCiTiT iLLiCiTiT Aug 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why these were added? Re-using the same object for default value is dangerous. Having these as constants doesn't make sense at all (at least I don't see any reason).

Only thing that needs custom defaults is extra, and that one should use function instead of stored value too.

def _get_default_extra() -> dict[str, Any]:
    return {
        "gridHeight": 230,
        "displayType": "table",
        "featuredVersionOrder": ["latestDone", "latest", "hero"],
    }

To be used as field(default_factory=_get_default_extra).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

942e055, Can we do this way?

Comment thread client/ayon_core/ui/components/views/default_view_control.py Outdated
item = layout.takeAt(0)
widget = item.widget() if item is not None else None
if widget is not None:
widget.deleteLater()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
widget.deleteLater()
widget.setVisible(False)
widget.deleteLater()

self.studio_default_view: View | None = None
self.project_default_view: View | None = None

self._studio_control: AYContainer | None = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it is called control?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

control(AYContainer) which using to set and unset project and studio defaults when we set any defaults it modify the widgets inside

)
return
try:
self._selector._apply_view(self.studio_default_view, emit=True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably should be public method.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many private methods from self._selector are in private methods. I'm bit unclear which and all needs to make public.

Comment on lines +369 to +371
self._selector._bindings.apply(default_view)
self._selector._clear_modified()
self._selector._close_menu()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh? This should be one public method on selector.

self._id_to_type: dict[str, str] = {}
# view_id -> (view_type, scope) (survives list_views; used by
# delete_view so it doesn't require a populated per-type cache).
self._id_to_view_attributes: dict[str, tuple[str, Scope]] = {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rename it to _view_attributes_by_id.

self.views_changed.emit(saved.view_type)
return saved

def patch_view_access(self, view_id: str, access_data: dict[str, Any], should_share_access) -> None:

@iLLiCiTiT iLLiCiTiT Sep 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def patch_view_access(self, view_id: str, access_data: dict[str, Any], should_share_access) -> None:
def patch_view_access(
self, view_id: str,
access_data: dict[str, Any],
should_share_access: bool,
) -> None:

if not powerpack_version:
self.error.emit("Could not resolve powerpack addon version")
return
visibility = Visibility.PUBLIC.value if should_share_access else Visibility.PRIVATE.value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
visibility = Visibility.PUBLIC.value if should_share_access else Visibility.PRIVATE.value
visibility = (
Visibility.PUBLIC.value
if should_share_access
else Visibility.PRIVATE.value
)

Comment on lines +399 to +405
if is_studio:
ayon_api.delete(f"views/{view_type}/{view_id}")
else:
ayon_api.delete(
f"views/{view_type}/{view_id}",
project_name=self._project_name,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if is_studio:
ayon_api.delete(f"views/{view_type}/{view_id}")
else:
ayon_api.delete(
f"views/{view_type}/{view_id}",
project_name=self._project_name,
)
body = {}
if not is_studio:
body = dict(project_name=self._project_name)
ayon_api.delete(f"views/{view_type}/{view_id}", **body)

Comment on lines +280 to +284
self._owner_label.setText(owner_name)

# Recreate owner avatar with updated name
self._owner_avatar._name = owner_name
self._owner_avatar._full_name = owner_name

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be one public method?

"""Populate access control rows from _access_dict."""
# Clear existing rows
for widget in self._access_row_widgets.values():
widget.deleteLater()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
widget.deleteLater()
widget.setVisible(False)
widget.deleteLater()

layout_margin=0
)

not_modified = (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we swap the name to avoid not_* that is then checked as if not not_modified:...

Suggested change
not_modified = (
modified = not (

and not self._suppress_auto_apply
):
self._apply_view(working, emit=True)
return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return

working = next((v for v in self._views if v.working), None)
if working is not None:
self._apply_view(working, emit=True)
working = self._ensure_working_view_exists()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
working = self._ensure_working_view_exists()
working_view = self._ensure_working_view_exists()


# Find the full name from usernames_and_groups
user_full_name = user_name
for user in self.usernames_and_groups.get("users", []):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for user in self.usernames_and_groups.get("users", []):
for user in self.usernames_and_groups["users"]:

# Find the full name from usernames_and_groups
user_full_name = user_name
for user in self.usernames_and_groups.get("users", []):
if user.get("name") == user_name:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if user.get("name") == user_name:
if user["name"] == user_name:

def _on_edit_clicked(self, view: View) -> None:
"""Open the editor for an existing view."""
self._close_menu()
editable = View.from_payload(view.to_payload())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the editable is a view to edit? I would call it view_copy.

users = []
groups = []
try:
users = ayon_api.get_users(project_name=project_name) or []

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
users = ayon_api.get_users(project_name=project_name) or []
users = ayon_api.get_users(project_name=project_name)

user_full_name = user_name
for user in self.usernames_and_groups.get("users", []):
if user.get("name") == user_name:
user_full_name = user.get("fullName", "") or user_name

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
user_full_name = user.get("fullName", "") or user_name
user_full_name = user["fullName"] or user_name

"""Update state when the popup signals it has closed."""
self._menu_open = False
if QtWidgets.QApplication.mouseButtons() & Qt.MouseButton.LeftButton:
local_pos = self.mapFromGlobal(QtGui.QCursor.pos())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
local_pos = self.mapFromGlobal(QtGui.QCursor.pos())
local_pos = self.mapFromGlobal(QtGui.QCursor.pos())
if isinstance(local_pos, QtCore.QPointF):
local_poc = local_pos.toPoint()

item = self._list_layout.takeAt(0)
w = item.widget()
if w:
w.deleteLater()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
w.deleteLater()
w.setVisible(False)
w.deleteLater()

Args:
text: Search string typed into the line edit.
"""
needle = text.lower()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needle?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The needle you search for in a haystack :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok... I see... Could it be text_filter instead?

@BigRoy

BigRoy commented Sep 11, 2026

Copy link
Copy Markdown
Member

Closing in favor of #2031

@BigRoy BigRoy closed this Sep 11, 2026
@github-project-automation github-project-automation Bot moved this from Review In Progress to Done in PR reviewing Sep 11, 2026
BigRoy added a commit that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

YN-0483: default filters for Loader

6 participants