Add OS metadata, preference hooks, and slider polish
This commit is contained in:
@@ -28,9 +28,10 @@ struct PreferencesView: View {
|
||||
@State private var pingIntervalSlider: Double = 10
|
||||
@State private var refreshIntervalSlider: Double = 60
|
||||
@State private var selection: Tab = .monitor
|
||||
@State private var hoveredTab: Tab?
|
||||
|
||||
private let minimumInterval: Double = 10
|
||||
private let maximumPingInterval: Double = 600
|
||||
private let maximumPingInterval: Double = 60
|
||||
private let maximumRefreshInterval: Double = 600
|
||||
|
||||
var body: some View {
|
||||
@@ -74,13 +75,20 @@ struct PreferencesView: View {
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
.padding(.horizontal, 6)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.fill(selection == tab ? Color.accentColor.opacity(0.25) : Color.clear)
|
||||
)
|
||||
.padding(.horizontal, 10)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.focusable(false)
|
||||
.contentShape(Capsule())
|
||||
.background(
|
||||
Capsule(style: .continuous)
|
||||
.fill(backgroundColor(for: tab))
|
||||
)
|
||||
.foregroundColor(selection == tab ? .white : .primary)
|
||||
.onHover { isHovering in
|
||||
hoveredTab = isHovering ? tab : (hoveredTab == tab ? nil : hoveredTab)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -127,6 +135,15 @@ struct PreferencesView: View {
|
||||
storedRefreshInterval = Int(refreshIntervalSlider)
|
||||
}
|
||||
}
|
||||
private func backgroundColor(for tab: Tab) -> Color {
|
||||
if selection == tab {
|
||||
return Color.accentColor
|
||||
}
|
||||
if hoveredTab == tab {
|
||||
return Color.accentColor.opacity(0.2)
|
||||
}
|
||||
return Color.accentColor.opacity(0.08)
|
||||
}
|
||||
}
|
||||
|
||||
private struct MonitorPreferencesView: View {
|
||||
@@ -141,56 +158,20 @@ private struct MonitorPreferencesView: View {
|
||||
let refreshChanged: (Bool) -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
Group {
|
||||
Text("Ping interval")
|
||||
.font(.headline)
|
||||
Slider(
|
||||
value: $pingIntervalSlider,
|
||||
in: minimumInterval...maximumPingInterval,
|
||||
step: 5
|
||||
) {
|
||||
Text("Ping interval")
|
||||
} minimumValueLabel: {
|
||||
Text("\(Int(minimumInterval))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} maximumValueLabel: {
|
||||
Text("\(Int(maximumPingInterval))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} onEditingChanged: { editing in
|
||||
pingChanged(editing)
|
||||
}
|
||||
Text("Current: \(Int(pingIntervalSlider)) seconds")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 24) {
|
||||
intervalSection(
|
||||
title: "Ping interval",
|
||||
value: $pingIntervalSlider,
|
||||
range: minimumInterval...maximumPingInterval,
|
||||
onEditingChanged: pingChanged
|
||||
)
|
||||
|
||||
Group {
|
||||
Text("Refresh interval")
|
||||
.font(.headline)
|
||||
Slider(
|
||||
value: $refreshIntervalSlider,
|
||||
in: minimumInterval...maximumRefreshInterval,
|
||||
step: 5
|
||||
) {
|
||||
Text("Refresh interval")
|
||||
} minimumValueLabel: {
|
||||
Text("\(Int(minimumInterval))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} maximumValueLabel: {
|
||||
Text("\(Int(maximumRefreshInterval))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} onEditingChanged: { editing in
|
||||
refreshChanged(editing)
|
||||
}
|
||||
Text("Current: \(Int(refreshIntervalSlider)) seconds")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
intervalSection(
|
||||
title: "Refresh interval",
|
||||
value: $refreshIntervalSlider,
|
||||
range: minimumInterval...maximumRefreshInterval,
|
||||
onEditingChanged: refreshChanged
|
||||
)
|
||||
|
||||
Divider()
|
||||
|
||||
@@ -201,6 +182,42 @@ private struct MonitorPreferencesView: View {
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private func intervalSection(
|
||||
title: String,
|
||||
value: Binding<Double>,
|
||||
range: ClosedRange<Double>,
|
||||
onEditingChanged: @escaping (Bool) -> Void
|
||||
) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
Text(title)
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Text("\(Int(value.wrappedValue)) seconds")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Slider(
|
||||
value: value,
|
||||
in: range,
|
||||
step: 5
|
||||
) {
|
||||
Text(title)
|
||||
} minimumValueLabel: {
|
||||
Text("\(Int(range.lowerBound))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} maximumValueLabel: {
|
||||
Text("\(Int(range.upperBound))s")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
} onEditingChanged: { editing in
|
||||
onEditingChanged(editing)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct NotificationsPreferencesView: View {
|
||||
|
||||
Reference in New Issue
Block a user