UI: Browser - Support views - #1970
mahesh-ynput wants to merge 56 commits into
Conversation
|
No idea - it should really behave like the web frontend. I don't see a reason for that 'star'.
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. |
4ad922f to
75eb8d0
Compare
… 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
…lick save silently save as in web
…e them further - improved filter capture method by using view bindings
…elpers - added _HoverReveal filter to match UX match with web.
|
|
@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:
|
- added default view section for studio and project default view
- removed legacy visibility fallback and enforce web format payload validation in desktop models/tests
|
@BigRoy, UI part is ready, need few enhancements but I need right end points to sharing view with access, set studio/project defaults |
| 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"], | ||
| } |
There was a problem hiding this comment.
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).
…ol.py and update ViewSettings
Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com>
| item = layout.takeAt(0) | ||
| widget = item.widget() if item is not None else None | ||
| if widget is not None: | ||
| widget.deleteLater() |
There was a problem hiding this comment.
| 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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
This probably should be public method.
There was a problem hiding this comment.
There are many private methods from self._selector are in private methods. I'm bit unclear which and all needs to make public.
| self._selector._bindings.apply(default_view) | ||
| self._selector._clear_modified() | ||
| self._selector._close_menu() |
There was a problem hiding this comment.
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]] = {} |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
| 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 |
There was a problem hiding this comment.
| visibility = Visibility.PUBLIC.value if should_share_access else Visibility.PRIVATE.value | |
| visibility = ( | |
| Visibility.PUBLIC.value | |
| if should_share_access | |
| else Visibility.PRIVATE.value | |
| ) |
| 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, | ||
| ) |
There was a problem hiding this comment.
| 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) |
| 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 |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
| widget.deleteLater() | |
| widget.setVisible(False) | |
| widget.deleteLater() |
| layout_margin=0 | ||
| ) | ||
|
|
||
| not_modified = ( |
There was a problem hiding this comment.
Could we swap the name to avoid not_* that is then checked as if not not_modified:...
| not_modified = ( | |
| modified = not ( |
| and not self._suppress_auto_apply | ||
| ): | ||
| self._apply_view(working, emit=True) | ||
| 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() |
There was a problem hiding this comment.
| 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", []): |
There was a problem hiding this comment.
| 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: |
There was a problem hiding this comment.
| 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()) |
There was a problem hiding this comment.
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 [] |
There was a problem hiding this comment.
| 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 |
There was a problem hiding this comment.
| 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()) |
There was a problem hiding this comment.
| 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() |
There was a problem hiding this comment.
| w.deleteLater() | |
| w.setVisible(False) | |
| w.deleteLater() |
| Args: | ||
| text: Search string typed into the line edit. | ||
| """ | ||
| needle = text.lower() |
There was a problem hiding this comment.
The needle you search for in a haystack :)
There was a problem hiding this comment.
Ok... I see... Could it be text_filter instead?
|
Closing in favor of #2031 |
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:
TODO: