Peter Steinbergerfeat: add menu bar layout editor0f070a33 months agoHistory
206 lines · 7.8 KB · Swift
RawDownload
import CodexBarCoreimport SwiftUI @MainActorstruct MenuBarPane: View {    private static let maxOverviewProviders = SettingsStore.mergedOverviewProviderLimit     @State private var isOverviewProviderPopoverPresented = false    @Bindable var settings: SettingsStore    @Bindable var store: UsageStore     static func overviewProviderLimitText(limit: Int = Self.maxOverviewProviders) -> String {        L("overview_choose_providers", String(limit))    }     static func inactiveDisplayContrastAvailable(for style: MenuBarIconStyle) -> Bool {        style == .iconAndPercent    }     var body: some View {        Form {            Section {                SettingsMenuPicker(                    selection: self.$settings.menuBarIconStyle,                    options: MenuBarSettingsMenuOptions.iconStyles,                    label: {                        SettingsRowLabel(                            L("menu_bar_style_title"),                            subtitle: L("menu_bar_style_subtitle"))                    },                    optionLabel: { style in                        Text(style.label)                    })                 Toggle(isOn: self.$settings.menuBarHighContrastOnInactiveDisplays) {                    SettingsRowLabel(                        L("menu_bar_inactive_display_contrast_title"),                        subtitle: "\(MenuBarIconStyle.iconAndPercent.label): "                            + L("menu_bar_inactive_display_contrast_subtitle"))                }                .disabled(!Self.inactiveDisplayContrastAvailable(for: self.settings.menuBarIconStyle))            } header: {                Text(L("section_icon"))            }             Section {                MenuBarLayoutEditor(settings: self.settings, store: self.store)                    .disabled(self.settings.menuBarIconStyle != .iconAndPercent)            } header: {                Text(L("menu_bar_layout_title"))            } footer: {                SettingsSectionFooter(L("menu_bar_layout_footer"))            }             Section {                Toggle(isOn: self.$settings.mergeIcons) {                    SettingsRowLabel(L("merge_icons_title"), subtitle: L("merge_icons_subtitle"))                }                 SettingsMenuPicker(                    selection: self.$settings.switcherRowsOption,                    options: MenuBarSettingsMenuOptions.switcherRows,                    label: { Text(L("switcher_rows_title")) },                    optionLabel: { option in                        Text(option.label)                    })                    .disabled(!self.settings.mergeIcons)                 Toggle(isOn: self.$settings.menuBarShowsHighestUsage) {                    SettingsRowLabel(                        L("show_most_used_provider_title"),                        subtitle: L("show_most_used_provider_subtitle"))                }                .disabled(!self.settings.mergeIcons)                 self.overviewProviderRow                    .disabled(!self.settings.mergeIcons)            } header: {                Text(L("section_combined_icon"))            }             Section {                Toggle(isOn: self.$settings.randomBlinkEnabled) {                    SettingsRowLabel(L("surprise_me_title"), subtitle: L("surprise_me_subtitle"))                }            } header: {                Text(L("section_animation"))            }        }        .formStyle(.grouped)        .toggleStyle(.switch)        .scrollContentBackground(.hidden)        .onAppear {            self.reconcileOverviewSelection()        }        .onChange(of: self.settings.mergeIcons) { _, isEnabled in            guard isEnabled else {                self.isOverviewProviderPopoverPresented = false                return            }            self.reconcileOverviewSelection()        }        .onChange(of: self.activeProvidersInOrder) { _, _ in            if self.activeProvidersInOrder.isEmpty {                self.isOverviewProviderPopoverPresented = false            }            self.reconcileOverviewSelection()        }    }     private var overviewProviderRow: some View {        LabeledContent {            if self.showsOverviewConfigureButton {                Button(L("configure")) {                    self.isOverviewProviderPopoverPresented = true                }                .popover(isPresented: self.$isOverviewProviderPopoverPresented, arrowEdge: .bottom) {                    self.overviewProviderPopover                }            }        } label: {            SettingsRowLabel(L("overview_tab_providers_title"), subtitle: self.overviewProviderSubtitle)        }    }     private var overviewProviderSubtitle: String {        if !self.settings.mergeIcons {            L("overview_enable_merge_icons_hint")        } else if self.activeProvidersInOrder.isEmpty {            L("overview_no_providers_hint")        } else {            self.overviewProviderSelectionSummary        }    }     private var overviewProviderPopover: some View {        VStack(alignment: .leading, spacing: 10) {            Text(Self.overviewProviderLimitText())                .font(.headline)            Text(L("overview_rows_follow_order"))                .font(.footnote)                .foregroundStyle(.tertiary)             ScrollView(.vertical, showsIndicators: true) {                VStack(alignment: .leading, spacing: 6) {                    ForEach(self.activeProvidersInOrder, id: \.self) { provider in                        Toggle(                            isOn: Binding(                                get: { self.overviewSelectedProviders.contains(provider) },                                set: { shouldSelect in                                    self.setOverviewProviderSelection(provider: provider, isSelected: shouldSelect)                                })) {                            Text(self.providerDisplayName(provider))                                .font(.body)                        }                        .toggleStyle(.checkbox)                        .disabled(                            !self.overviewSelectedProviders.contains(provider) &&                                self.overviewSelectedProviders.count >= Self.maxOverviewProviders)                    }                }            }            .frame(maxHeight: 220)        }        .padding(12)        .frame(width: 280)    }     private var activeProvidersInOrder: [UsageProvider] {        self.store.enabledProviders()    }     private var overviewSelectedProviders: [UsageProvider] {        self.settings.resolvedMergedOverviewProviders(            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }     private var showsOverviewConfigureButton: Bool {        self.settings.mergeIcons && !self.activeProvidersInOrder.isEmpty    }     private var overviewProviderSelectionSummary: String {        let selectedNames = self.overviewSelectedProviders.map(self.providerDisplayName)        guard !selectedNames.isEmpty else { return L("overview_no_providers_selected") }        return selectedNames.joined(separator: ", ")    }     private func providerDisplayName(_ provider: UsageProvider) -> String {        ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName    }     private func setOverviewProviderSelection(provider: UsageProvider, isSelected: Bool) {        _ = self.settings.setMergedOverviewProviderSelection(            provider: provider,            isSelected: isSelected,            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }     private func reconcileOverviewSelection() {        _ = self.settings.reconcileMergedOverviewSelectedProviders(            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }}