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
11 changes: 6 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -945,13 +945,14 @@ the `@AppStorage(TriggerKeyStore.defaultsKey)` + `TriggerKey.fromPersisted` pair
restating that pairing per view. The unset default belongs to `fromPersisted` (an absent keycode maps
to right ⌘), so views must not re-declare `TriggerKey.rightCommand.rawValue` themselves.

**Per-key styles.** A Key Styles row in the Advanced pane's Experimental section (`KeyStylesRow`) — a status naming the
**Per-key styles.** A Key Styles row in its own section on the Styles pane (`KeyStylesSection`, `KeyStylesRow`) — a status naming the
keys set up, and an Edit… button — opens a sheet with one pop-up per `TriggerKey`: the style a press
of that key dictates with — Same as main window, Default, or a profile (disabled while enhanced
transcripts are off) — or Off for keys other than the bound one. On Advanced rather than under the
key picker because it's a power-user extra, so General stays short enough for a small display; a
row in an existing section and a sheet, not a section or a row per key, because Advanced is already
near that limit — a section of its own pushed Reset… off the bottom of CI's screen. `TriggerKeyStyleStore` (`BlurtTriggerKeyStyles`, keycode → style id) holds
transcripts are off) — or Off for keys other than the bound one. On Styles, under the profiles it
picks from, rather than under the key picker, so General stays short enough for a small display;
its own section rather than a row of Custom Styles, because that section is disabled while
enhanced transcripts are off and turning a key on or off still works then; one row and a sheet,
not a row per key, to keep the pane short. `TriggerKeyStyleStore` (`BlurtTriggerKeyStyles`, keycode → style id) holds
it; an absent value is today's single key with the current style. Rebinding swaps each moved key's
entry with a stash (`BlurtTriggerKeyStyleStash`): the key moved away from goes back to what it was
as an extra before it was bound (off, unless the user had set it up as one), and gets its bound
Expand Down
7 changes: 2 additions & 5 deletions App/Blurt/Blurt/Wizard/DeveloperSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,12 @@ struct UpdatesSection: View {
}
}

/// The Advanced pane's experimental features — per-key styles (`KeyStylesRow`),
/// read-aloud (see `SelectionSpeechStore`), asking about the selection
/// The Advanced pane's experimental features — read-aloud (see
/// `SelectionSpeechStore`), asking about the selection
/// (`SelectionAskStore`) and how a read sounds (`ReadAloudStyleStore`). The
/// router reads the same defaults these controls write at every press, so a
/// change applies to the next one.
struct ExperimentalSection: View {
var coordinator: AppCoordinator

@AppStorage(SelectionSpeechStore.defaultsKey) private var selectionSpeech = false
@AppStorage(SelectionAskStore.defaultsKey) private var selectionAsk = SelectionAskStore.defaultValue
@AppStorage(ReadAloudStyleStore.speedDefaultsKey) private var speed = ReadAloudStyleStore.defaultSpeed
Expand Down Expand Up @@ -90,7 +88,6 @@ struct ExperimentalSection: View {

var body: some View {
Section {
KeyStylesRow(coordinator: coordinator)
Toggle(isOn: $selectionSpeech) {
SettingLabel(title: "Read selected text aloud", systemImage: "speaker.wave.2")
}
Expand Down
27 changes: 21 additions & 6 deletions App/Blurt/Blurt/Wizard/KeyStylesRow.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import BlurtEngine
import SwiftUI

/// The Key Styles row in a section of its own, under the styles it picks from.
/// Not a row of Custom Styles: that section is disabled while enhanced
/// transcripts are off, and turning a key on or off still works then (the
/// sheet disables only the styles).
struct KeyStylesSection: View {
let coordinator: AppCoordinator

var body: some View {
Section {
KeyStylesRow(coordinator: coordinator)
} footer: {
Text("Use a different style with each key, without switching styles in the main window.")
}
}
}

/// The Key Styles row: a status and an "Edit…" button, like the API key row,
/// whose sheet gives each trigger key its own style (or Off, for keys other
/// than the bound one). The first row of the Advanced pane's Experimental
/// section rather than beside the dictation key on General — it's a
/// power-user extra, and a section of its own made Advanced too tall for a
/// small display. Settings-only, so it is not a wizard step.
/// than the bound one). On the Styles pane, under the styles it picks from,
/// rather than beside the dictation key on General, which has to stay short
/// enough for a small display. Settings-only, so it is not a wizard step.
struct KeyStylesRow: View {
var coordinator: AppCoordinator

Expand Down Expand Up @@ -40,7 +55,7 @@ struct KeyStylesRow: View {
bound: bound, styles: TriggerKeyStyleStore.styles(decoding: keyStylesRaw),
profiles: StyleProfileStore().profiles(decoding: profilesRaw))
let changed = entries.filter(\.isChanged).map(\.text)
SettingRow(title: "Key Styles", systemImage: "textformat") {
SettingRow(title: "Key Styles", systemImage: "keyboard") {
HStack(spacing: 8) {
// The keys set up beyond the default, or the main window's style.
Text(changed.isEmpty ? KeyStylesSheet.mainWindowName : changed.joined(separator: ", "))
Expand Down Expand Up @@ -145,7 +160,7 @@ private struct KeyStylesSheet: View {
VStack(alignment: .leading, spacing: 6) {
Text("Key Styles")
.font(.headline)
Text("Each key can dictate with its own style, without changing the style selected in the main window.")
Text("Pick a style for each key. Turn another key on to dictate with it too.")
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
Expand Down
18 changes: 11 additions & 7 deletions App/Blurt/Blurt/Wizard/SettingsWindowRoot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct SettingsWindowRoot: View {
TabView {
GeneralSettingsTab(coordinator: coordinator)
.tabItem { Label(UITestIdentifiers.generalSettingsTab, systemImage: "gearshape") }
StylesSettingsTab()
StylesSettingsTab(coordinator: coordinator)
.tabItem { Label(UITestIdentifiers.stylesSettingsTab, systemImage: "textformat") }
VocabularySettingsTab()
.tabItem {
Expand Down Expand Up @@ -66,21 +66,25 @@ private struct GeneralSettingsTab: View {
}
}

/// The enhanced-transcripts switch and the user's style profiles — their own
/// pane rather than an Advanced section. The switch leads because styles only
/// shape the enhanced rewrite: with it off they're disabled, and the switch that
/// re-enables them sits directly above rather than on another pane.
/// The enhanced-transcripts switch, the user's style profiles, and which style
/// each trigger key dictates with — their own pane rather than an Advanced
/// section. The switch leads because styles only shape the enhanced rewrite:
/// with it off they're disabled, and the switch that re-enables them sits
/// directly above rather than on another pane.
private struct StylesSettingsTab: View {
let coordinator: AppCoordinator

var body: some View {
SettingsPane {
TranscriptionSection()
StyleProfilesSection()
KeyStylesSection(coordinator: coordinator)
}
}
}

/// The occasional stuff: checking for an update, experimental features
/// (per-key styles among them), the developer-mode log toggle, and the
/// (read-aloud), the developer-mode log toggle, and the
/// start-over button. Kept out of General so
/// the common pane stays short.
private struct AdvancedSettingsTab: View {
Expand All @@ -90,7 +94,7 @@ private struct AdvancedSettingsTab: View {
var body: some View {
SettingsPane {
UpdatesSection(model: updaterModel)
ExperimentalSection(coordinator: coordinator)
ExperimentalSection()
MaintenanceSection()
ResetSection(coordinator: coordinator)
}
Expand Down
2 changes: 1 addition & 1 deletion App/Blurt/Blurt/Wizard/Steps/HotkeyStepView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SwiftUI
/// The dictation-key section of the setup/settings screen. Two menu pickers: which
/// lone modifier triggers dictation, and whether it activates by tap (toggle),
/// hold (push-to-talk), or both; changes are persisted and pushed to the event tap
/// immediately. Giving the other trigger keys their own styles is the Advanced
/// immediately. Giving the other trigger keys their own styles is the Styles
/// pane's `KeyStylesRow`.
struct HotkeyStepView: View {
var coordinator: AppCoordinator
Expand Down
14 changes: 7 additions & 7 deletions App/Blurt/BlurtUITests/SettingsUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ final class SettingsUITests: BlurtUITestCase {
/// right ⌘ is the bound key: it dictates with the main window's style and, since
/// the bound key always dictates, offers no Off; the other keys start Off.
func testKeyStylesSheetListsEveryKey() {
let sheet = openKeyStyles(openAdvancedSettings())
let sheet = openKeyStyles(openStylesSettings())

for keyCode in [rightCommand, rightOption, fnKey] {
XCTAssertTrue(
Expand All @@ -268,7 +268,7 @@ final class SettingsUITests: BlurtUITestCase {
/// Turning right ⌥ on persists: the button's spoken summary names it, and the
/// sheet shows the choice again when reopened.
func testKeyStyleChoicePersists() {
let settings = openAdvancedSettings()
let settings = openStylesSettings()
let sheet = openKeyStyles(settings)

sheet.popUpButtons[UITestIdentifiers.keyStylePicker(rightOption)].click()
Expand All @@ -290,7 +290,7 @@ final class SettingsUITests: BlurtUITestCase {
/// Escape closes the sheet (it has no Cancel button to carry the shortcut) and
/// leaves the Settings window open.
func testEscapeClosesKeyStylesSheet() {
let settings = openAdvancedSettings()
let settings = openStylesSettings()
let sheet = openKeyStyles(settings)

app.typeKey(.escape, modifierFlags: [])
Expand All @@ -299,12 +299,12 @@ final class SettingsUITests: BlurtUITestCase {
XCTAssertTrue(settings.exists, "Escape should close only the sheet, not Settings")
}

/// Returns the Advanced pane, where the Key Styles row lives.
private func openAdvancedSettings() -> XCUIElement {
selectSettingsTab(openSettingsWindow(), named: UITestIdentifiers.advancedSettingsTab)
/// Returns the Styles pane, where the Key Styles row lives.
private func openStylesSettings() -> XCUIElement {
selectSettingsTab(openSettingsWindow(), named: UITestIdentifiers.stylesSettingsTab)
}

/// Opens the Key Styles sheet from its row's Edit… button on the Advanced
/// Opens the Key Styles sheet from its row's Edit… button on the Styles
/// pane and returns it.
private func openKeyStyles(_ settings: XCUIElement) -> XCUIElement {
let button = settings.buttons[UITestIdentifiers.keyStylesButton]
Expand Down