Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ expect rough edges until 1.0.
## [Unreleased]

### Changed
- **The popover behaves more like a native Mac panel.** Esc on a sub-page (Colour, Buttons,
Battery Usage, Profiles, What's New) now goes back one level instead of closing the whole
popover, and every sub-page uses the same Back button, What's New included. ⌘R refreshes and
⌘, opens Settings, matching the right-click menu. Chips and rows highlight on hover, the
colour swatches are real buttons with names, and the icon-only buttons have VoiceOver labels
and tooltips. A DPI chip that can be edited says so in its tooltip, since the editor is only
reachable by right-click. The sleep timer reads "5 min" instead of "5m 00s".
With no mouse plugged in, the popover shows one card explaining how to connect and a
"Look Again" button, instead of a column of greyed-out controls for a mouse that isn't there.

- **Updates are found within hours, not a day.** The app used to ask GitHub once a day. Now
it asks every time it starts, and again whenever its last answer is more than three hours
old. A Mac that slept longer than that asks as soon as it wakes. If the network isn't back
Expand Down
20 changes: 20 additions & 0 deletions Sources/MacRazer/CardComponents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ func sectionNote(_ text: String, color: Color = .secondary) -> some View {
.fixedSize(horizontal: false, vertical: true)
}

/// A faint wash over a clickable chip or row while the pointer is on it — the feedback
/// Control Center's tiles and menu items give, which plain-styled SwiftUI buttons don't.
/// Skipped while disabled, so a dimmed control doesn't look like it would respond.
private struct HoverHighlight<S: Shape>: ViewModifier {
let shape: S
@State private var hovered = false
@Environment(\.isEnabled) private var isEnabled

func body(content: Content) -> some View {
content
.overlay(shape.fill(Color.primary.opacity(hovered && isEnabled ? 0.08 : 0)).allowsHitTesting(false))
.onHover { hovered = $0 }
.animation(.easeOut(duration: 0.12), value: hovered)
}
}

extension View {
func hoverHighlight<S: Shape>(_ shape: S) -> some View { modifier(HoverHighlight(shape: shape)) }
}

/// Small secondary-styled section heading used inside cards.
func sectionLabel(_ text: String, _ symbol: String) -> some View {
Label(text, systemImage: symbol)
Expand Down
8 changes: 8 additions & 0 deletions Sources/MacRazer/ColorPickerPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
import SwiftUI

/// A clearly-labelled "‹ Back" button used by the popover sub-pages.
///
/// Esc triggers it, so a sub-page backs out one level the way a System Settings pane or a
/// Control Center module does, rather than Esc dismissing the whole popover. `escapes` is for
/// pages that own Esc for something narrower while it's in progress (cancelling a key
/// recording or a rename): a navigation there would throw away what the user was doing.
struct BackButton: View {
var action: () -> Void
var escapes = true
var body: some View {
Button(action: action) {
HStack(spacing: 3) {
Expand All @@ -17,6 +23,8 @@ struct BackButton: View {
.contentShape(Capsule())
}
.buttonStyle(.plain)
.keyboardShortcut(escapes ? KeyboardShortcut.cancelAction : nil)
.help(escapes ? "Back (Esc)" : "Back")
}
}

Expand Down
215 changes: 170 additions & 45 deletions Sources/MacRazer/PopoverView.swift

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Sources/MacRazer/ProfilesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ struct ProfilesView: View {

private var header: some View {
VStack(alignment: .leading, spacing: 8) {
if let onBack { BackButton(action: onBack) }
// Esc cancels an in-progress rename (the field's onExitCommand), not the page.
if let onBack { BackButton(action: onBack, escapes: renamingID == nil) }
VStack(alignment: .leading, spacing: 2) {
Text("Profiles").font(.system(size: 16, weight: .semibold))
Text("Saved DPI, lighting and button setups you can switch between.")
Expand Down
5 changes: 4 additions & 1 deletion Sources/MacRazer/RemapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ struct RemapView: View {

private var header: some View {
VStack(alignment: .leading, spacing: 8) {
if let onBack { BackButton(action: onBack) }
// Esc belongs to the key recorder while it's up: it cancels the recording.
if let onBack {
BackButton(action: onBack, escapes: recordingButton == nil && !recordingDpiCycleShortcut)
}
VStack(alignment: .leading, spacing: 2) {
Text("Configure Buttons").font(.system(size: 16, weight: .semibold))
Text("Remap your mouse's extra buttons.")
Expand Down
8 changes: 3 additions & 5 deletions Sources/MacRazer/WhatsNewPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ struct WhatsNewPage: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 8) {
Button(action: onBack) {
Image(systemName: "chevron.left").font(.system(size: 12, weight: .semibold))
}
.buttonStyle(.plain)
.foregroundStyle(Color.razerGreen)
// The same back control as every other sub-page, so navigation looks and
// behaves (Esc included) the same wherever the popover is.
BackButton(action: onBack)
Text("What's new in \(version)").font(.system(size: 13, weight: .semibold))
Spacer(minLength: 0)
}
Expand Down
8 changes: 8 additions & 0 deletions docs/DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ page is a `ScrollView` (so long content scrolls without resizing the popover):
- **Settings card**: a switch toggle for "Show battery % in menu bar".
- Live mouse-config sections **grey out + disable when disconnected** (battery stays
readable; refresh stays active).
- With **no mouse known at all** (none seen yet, or unplugged), the controls are replaced
by one card that says how to connect and offers "Look Again" (or the Input Monitoring
grant, when that's what is missing). A last battery reading is still shown if there is one.
- Keyboard: **⌘R** refreshes, **⌘,** opens Settings, and **Esc** on any sub-page goes back
one level instead of closing the popover (except while a key recording or a rename is in
progress, where Esc cancels that).
- Swatches, chips and the icon buttons carry VoiceOver labels and tooltips; chips and
rows get a hover highlight.
- **Colour page**, `ColorPickerPage.swift`: an inline **hue/saturation colour wheel** +
brightness slider + live preview, applied to the mouse live (throttled). Replaces the old
system `NSColorPanel`.
Expand Down
Loading