208 lines
7.9 KiB
Swift
208 lines
7.9 KiB
Swift
import CodexBarCore
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
struct 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))
|
|
}
|
|
|
|
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)
|
|
})
|
|
|
|
SettingsMenuPicker(
|
|
selection: self.$settings.menuBarDisplayMode,
|
|
options: MenuBarSettingsMenuOptions.displayModes,
|
|
label: {
|
|
SettingsRowLabel(
|
|
L("display_mode_title"),
|
|
subtitle: self.settings.menuBarDisplayMode.description)
|
|
},
|
|
optionLabel: { mode in
|
|
Text(mode.label)
|
|
})
|
|
.disabled(self.settings.menuBarIconStyle != .iconAndPercent)
|
|
|
|
Toggle(isOn: self.$settings.menuBarShowsResetTimeWhenExhausted) {
|
|
SettingsRowLabel(
|
|
L("menu_bar_reset_when_exhausted_title"),
|
|
subtitle: L("menu_bar_reset_when_exhausted_subtitle"))
|
|
}
|
|
.disabled(
|
|
self.settings.menuBarIconStyle != .iconAndPercent
|
|
|| self.settings.menuBarDisplayMode == .resetTime)
|
|
} header: {
|
|
Text(L("section_icon"))
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|