diff --git a/Nifro/Screens/DisplayPanel.swift b/Nifro/Screens/DisplayPanel.swift index f55d7ca..7611d0d 100644 --- a/Nifro/Screens/DisplayPanel.swift +++ b/Nifro/Screens/DisplayPanel.swift @@ -281,8 +281,8 @@ private struct DisplayColumn: View { PanelButton( symbol: column.rotationMode.symbol, label: column.rotationMode.label, - // Lit while it is doing something. Pinned is the resting state and says so by staying dark. - isOn: column.rotationMode != .pinned + // This always names the selected mode, so Pin, Loop and Random all use the selected state. + isOn: true ) { model.cycleRotationMode(on: column.display) } @@ -442,6 +442,7 @@ private struct DisplayColumn: View { PanelButton( symbol: column.isMuted ? "speaker.slash.fill" : "speaker.wave.2.fill", label: column.isMuted ? String(localized: "Muted") : String(localized: "Playing sound"), + isOn: !column.isMuted, isEnabled: hasPage ) { model.toggleMuted(on: column.display) @@ -453,7 +454,7 @@ private struct DisplayColumn: View { // Lit while the wallpaper is up, like every other lit control in the panel. It used // to be the inverse — lit while the display was switched *off* — which made this the // one control reading its lit state as "this button is engaged" instead of as "the - // thing it turns on is on". Five controls now answer that question the same way. + // thing it turns on is on". The panel's stateful controls answer that question the same way. isOn: column.isShowing ) { model.toggleShowing(on: column.display) diff --git a/Tests/PanelControlStateTests.swift b/Tests/PanelControlStateTests.swift new file mode 100644 index 0000000..2360f46 --- /dev/null +++ b/Tests/PanelControlStateTests.swift @@ -0,0 +1,55 @@ +import Foundation +import Testing + +/** +The panel uses button-shaped toggles for state that is already in effect. Their colour is the +quick reading of that state, so treating one selected mode or an audible page as unselected makes +the panel contradict itself even though its actions still work. +*/ +@Suite("Panel controls show enabled state") +struct PanelControlStateTests { + private static let repository = URL(filePath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + + @Test("The current rotation mode is always highlighted, including Pin") + func currentRotationModeIsHighlighted() throws { + let controls = try Self.rotationControls() + + #expect( + controls.contains("isOn: true"), + "Pin is a selected rotation mode, so it must be highlighted just like Loop and Random." + ) + } + + @Test("Sound is highlighted only while it is playing") + func playingSoundIsHighlighted() throws { + let controls = try Self.pictureControls() + + #expect( + controls.contains("isOn: !column.isMuted"), + "The speaker control must be highlighted when sound is playing and unhighlighted when it is muted." + ) + } + + private static func rotationControls() throws -> Substring { + try controls(between: "private var rotationControls: some View", and: "private var modeButtons: some View") + } + + private static func pictureControls() throws -> Substring { + try controls(between: "private var preview: some View", and: "private func reading(") + } + + private static func controls(between start: String, and end: String) throws -> Substring { + let source = try String( + contentsOf: repository.appending(path: "Nifro/Screens/DisplayPanel.swift"), + encoding: .utf8 + ) + + guard let range = source.range(of: start)?.lowerBound, let end = source.range(of: end)?.lowerBound else { + throw CocoaError(.fileReadCorruptFile) + } + + return source[range..