From 1157eef2010eedf8b6a8f28bba336f0935ac5b04 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Mon, 15 Jun 2026 08:07:40 +0200 Subject: [PATCH] Fix macOS native styling for search toolbar pickers and view options The content type segmented picker rendered as a Liquid-Glass pill instead of a native macOS segmented control. Wrapping a `.segmented` Picker in `.opacity`/`.disabled`/`.accessibilityHidden`/`.fixedSize` (used to keep toolbar items mounted-but-invisible for stable layout) inserts a hosting layer that makes the toolbar fall back to the pill rendering. - SearchView: filters button and content type picker are now always visible on macOS, rendered bare so `.segmented` keeps native styling. - SearchView: don't reset the content type to .video on submit on macOS, so a pre-selected type (e.g. Playlists) is preserved (iOS unchanged). - InstanceBrowseView: same pill fix, rendering the tab / content type pickers bare via conditional `if` instead of opacity-masking. - ViewOptionsSheet / MediaBrowserViewOptionsSheet: use `.formStyle(.grouped)` for native macOS form styling in the options popover. --- .../Views/Components/ViewOptionsSheet.swift | 2 +- .../Views/Instances/InstanceBrowseView.swift | 28 ++++++-------- .../MediaBrowserViewOptionsSheet.swift | 2 +- Yattee/Views/Search/SearchView.swift | 37 ++++++++----------- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/Yattee/Views/Components/ViewOptionsSheet.swift b/Yattee/Views/Components/ViewOptionsSheet.swift index 82578d78..5aae42a6 100644 --- a/Yattee/Views/Components/ViewOptionsSheet.swift +++ b/Yattee/Views/Components/ViewOptionsSheet.swift @@ -156,7 +156,7 @@ struct ViewOptionsSheet: View { .presentationDragIndicator(.visible) #endif #if os(macOS) - .padding() + .formStyle(.grouped) .frame(width: 300) #endif } diff --git a/Yattee/Views/Instances/InstanceBrowseView.swift b/Yattee/Views/Instances/InstanceBrowseView.swift index 26a57221..ebed4432 100644 --- a/Yattee/Views/Instances/InstanceBrowseView.swift +++ b/Yattee/Views/Instances/InstanceBrowseView.swift @@ -357,30 +357,26 @@ struct InstanceBrowseView: View { } #if os(macOS) if #available(macOS 26, *) { - // Search filters button appears only while showing search results. - // Items stay mounted (invisible/inert) otherwise so the toolbar layout is stable. - ToolbarItem(placement: .navigation) { - searchFiltersToolbarButton - .opacity(showsSearchFiltersInToolbar ? 1 : 0) - .disabled(!showsSearchFiltersInToolbar) - .accessibilityHidden(!showsSearchFiltersInToolbar) + // Render the items bare (conditional `if`, no opacity/disabled + // wrappers): wrapping a `.segmented` Picker inserts a hosting layer + // that makes the toolbar fall back to a Liquid-Glass pill instead of + // the native segmented control. The search filters button shows only + // while displaying search results; the principal picker swaps between + // the browse tab picker and the search content type picker. + if showsSearchFiltersInToolbar { + ToolbarItem(placement: .navigation) { + searchFiltersToolbarButton + } } - .sharedBackgroundVisibility(showsSearchFiltersInToolbar ? .automatic : .hidden) - // Search content type picker while showing results, browse tab picker otherwise - ToolbarItem(placement: .principal) { - Group { + if isToolbarPickerVisible { + ToolbarItem(placement: .principal) { if showsSearchFiltersInToolbar { searchContentTypePicker } else { tabPicker } } - .fixedSize() - .opacity(isToolbarPickerVisible ? 1 : 0) - .disabled(!isToolbarPickerVisible) - .accessibilityHidden(!isToolbarPickerVisible) } - .sharedBackgroundVisibility(isToolbarPickerVisible ? .automatic : .hidden) } else if showsSearchFiltersInToolbar { ToolbarItem(placement: .navigation) { searchFiltersToolbarButton diff --git a/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift b/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift index bf031f8e..e73a79d0 100644 --- a/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift +++ b/Yattee/Views/MediaBrowser/MediaBrowserViewOptionsSheet.swift @@ -24,7 +24,7 @@ struct MediaBrowserViewOptionsSheet: View { #if os(macOS) // Popover content: no navigation chrome, click-outside dismisses. formContent - .padding() + .formStyle(.grouped) .frame(width: 300) .onAppear { // Reset sort order if current selection is not available for this source type diff --git a/Yattee/Views/Search/SearchView.swift b/Yattee/Views/Search/SearchView.swift index 75af6bbf..4e2eff49 100644 --- a/Yattee/Views/Search/SearchView.swift +++ b/Yattee/Views/Search/SearchView.swift @@ -291,29 +291,18 @@ struct SearchView: View { } #if os(macOS) if searchInstance?.supportsSearchFilters == true { - if #available(macOS 26, *) { - // Items stay in the toolbar even before a search is submitted - // (invisible and inert) so the search field keeps a stable size. - ToolbarItem(placement: .navigation) { - searchFiltersToolbarButton - .opacity(hasSearched ? 1 : 0) - .disabled(!hasSearched) - .accessibilityHidden(!hasSearched) - } - .sharedBackgroundVisibility(hasSearched ? .automatic : .hidden) - ToolbarItem(placement: .principal) { + // Filters button + content type picker are always visible on macOS. + // The picker is rendered bare (no opacity/disabled/fixedSize wrappers): + // wrapping a `.segmented` Picker in those modifiers inserts a hosting + // layer that makes the toolbar fall back to a Liquid-Glass pill instead + // of the native segmented control. + ToolbarItem(placement: .navigation) { + searchFiltersToolbarButton + } + ToolbarItem(placement: .principal) { + if #available(macOS 26, *) { contentTypePicker - .fixedSize() - .opacity(hasSearched ? 1 : 0) - .disabled(!hasSearched) - .accessibilityHidden(!hasSearched) - } - .sharedBackgroundVisibility(hasSearched ? .automatic : .hidden) - } else if hasSearched { - ToolbarItem(placement: .navigation) { - searchFiltersToolbarButton - } - ToolbarItem(placement: .principal) { + } else { contentTypePicker .fixedSize() } @@ -331,7 +320,11 @@ struct SearchView: View { .searchable(text: searchTextBinding, prompt: Text(String(localized: "search.placeholder"))) .onSubmit(of: .search) { searchViewModel?.cancelSuggestions() + #if !os(macOS) + // On macOS the content type picker is always visible, so preserve the + // user's chosen type instead of resetting it to .video on each submit. searchViewModel?.filters.type = .video + #endif if let filters = searchViewModel?.filters { saveFilters(filters) }