From c39cc2429eea2c40dd3c78e5aa9f6cfe013e145d Mon Sep 17 00:00:00 2001 From: Dominik Moreira Date: Sun, 4 Oct 2026 17:43:20 +0200 Subject: [PATCH] Increase controls when enabled --- Localizable.xcstrings | 18 ++-- LoopLine/Views/PDFReadingView.swift | 152 +++++++++++++++++---------- LoopLine/Views/ReadingModeView.swift | 121 +++++++++++++-------- LoopLine/Views/SettingsView.swift | 4 +- 4 files changed, 188 insertions(+), 107 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 4cf75b9..346df55 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -349,14 +349,13 @@ } } }, - "Customize row highlight visibility" : { - "comment" : "A description of the guide opacity slider.", - "isCommentAutoGenerated" : true, + "Customize the row highlight in text-imported projects" : { + "comment" : "A description of the text row highlight opacity slider.", "localizations" : { "de" : { "stringUnit" : { - "state" : "machine_translated", - "value" : "Sichtbarkeit der Reihenmarkierung anpassen" + "state" : "translated", + "value" : "Reihenmarkierung in importierten Textprojekten anpassen" } } } @@ -513,14 +512,13 @@ } } }, - "Guide opacity" : { - "comment" : "A heading for the opacity of the guide on the reading mode settings page.", - "isCommentAutoGenerated" : true, + "Text row highlight opacity" : { + "comment" : "A heading for the opacity of the row highlight for text-imported projects on the Reading Mode settings page.", "localizations" : { "de" : { "stringUnit" : { - "state" : "machine_translated", - "value" : "Deckkraft der Reihenmarkierung" + "state" : "translated", + "value" : "Deckkraft der Text-Reihenmarkierung" } } } diff --git a/LoopLine/Views/PDFReadingView.swift b/LoopLine/Views/PDFReadingView.swift index ed10f4d..bc1b8d3 100644 --- a/LoopLine/Views/PDFReadingView.swift +++ b/LoopLine/Views/PDFReadingView.swift @@ -6,9 +6,14 @@ import SwiftUI struct PDFReadingView: View { @Environment(\.modelContext) private var modelContext + @Query private var settings: [AppSettings] @Bindable var project: Project @State private var viewModel = PDFReadingViewModel() + private var usesLargeControls: Bool { + settings.first?.largeControls ?? true + } + var body: some View { @Bindable var viewModel = viewModel @@ -87,66 +92,35 @@ struct PDFReadingView: View { } private var trackingControls: some View { - HStack(spacing: 16) { - Menu { - ForEach(ReadingTrackingMetric.allCases) { metric in - Button(metric.title) { - viewModel.selectedMetric = metric + Group { + if usesLargeControls { + VStack(alignment: .leading, spacing: 12) { + metricPicker + + HStack(spacing: 20) { + decreaseButton + + metricValue + .frame(minWidth: 90) + + increaseButton } + .frame(maxWidth: .infinity) } - } label: { - HStack(spacing: 6) { - Text(viewModel.selectedMetric.title) - .font(.subheadline.weight(.semibold)) - .lineLimit(1) + } else { + HStack(spacing: 16) { + metricPicker - Image(systemName: "chevron.down") - .font(.caption.weight(.bold)) - } - .foregroundStyle(LoopLineTheme.readingPrimaryText) - .frame(minWidth: 92, alignment: .leading) - .padding(.horizontal, 12) - .frame(height: 48) - .background(LoopLineTheme.readingControlFill, in: RoundedRectangle(cornerRadius: LoopLineTheme.compactCornerRadius, style: .continuous)) - } - .accessibilityLabel("Tracking metric") - .accessibilityValue(viewModel.selectedMetric.title) + Spacer(minLength: 0) - Spacer(minLength: 0) + decreaseButton - Button { - viewModel.decrementSelectedMetric(for: project, in: modelContext) - } label: { - Image(systemName: "minus") - } - .buttonStyle(LoopLineIconButtonStyle( - size: 48, - foregroundColor: LoopLineTheme.readingPrimaryText, - backgroundColor: LoopLineTheme.readingControlFill - )) - .disabled(!viewModel.canDecreaseSelectedMetric(for: project)) - .opacity(viewModel.canDecreaseSelectedMetric(for: project) ? 1 : 0.38) - .accessibilityLabel("Decrease \(viewModel.selectedMetric.title)") - - Text(String(viewModel.selectedMetricValue(for: project))) - .font(.system(size: 34, weight: .bold).monospacedDigit()) - .foregroundStyle(LoopLineTheme.readingPrimaryText) - .lineLimit(1) - .minimumScaleFactor(0.65) - .frame(minWidth: 70) - .accessibilityLabel("\(viewModel.selectedMetric.title) \(viewModel.selectedMetricValue(for: project))") + metricValue + .frame(minWidth: 70) - Button { - viewModel.incrementSelectedMetric(for: project, in: modelContext) - } label: { - Image(systemName: "plus") + increaseButton + } } - .buttonStyle(LoopLineIconButtonStyle( - size: 48, - foregroundColor: LoopLineTheme.primaryActionForeground, - backgroundColor: LoopLineTheme.primaryActionBackground - )) - .accessibilityLabel("Increase \(viewModel.selectedMetric.title)") } .padding(.horizontal, 12) .padding(.vertical, 10) @@ -156,6 +130,78 @@ struct PDFReadingView: View { .stroke(LoopLineTheme.readingStroke, lineWidth: 1) } } + + private var metricPicker: some View { + Menu { + ForEach(ReadingTrackingMetric.allCases) { metric in + Button(metric.title) { + viewModel.selectedMetric = metric + } + } + } label: { + HStack(spacing: 6) { + Text(viewModel.selectedMetric.title) + .font(.subheadline.weight(.semibold)) + .lineLimit(1) + + Image(systemName: "chevron.down") + .font(.caption.weight(.bold)) + } + .foregroundStyle(LoopLineTheme.readingPrimaryText) + .frame(minWidth: 92, alignment: .leading) + .padding(.horizontal, 12) + .frame(height: usesLargeControls ? 56 : 48) + .background(LoopLineTheme.readingControlFill, in: RoundedRectangle(cornerRadius: LoopLineTheme.compactCornerRadius, style: .continuous)) + } + .accessibilityLabel("Tracking metric") + .accessibilityValue(viewModel.selectedMetric.title) + } + + private var decreaseButton: some View { + Button { + viewModel.decrementSelectedMetric(for: project, in: modelContext) + } label: { + Image(systemName: "minus") + } + .buttonStyle(LoopLineIconButtonStyle( + size: usesLargeControls ? 70 : 48, + foregroundColor: LoopLineTheme.readingPrimaryText, + backgroundColor: LoopLineTheme.readingControlFill + )) + .disabled(!viewModel.canDecreaseSelectedMetric(for: project)) + .opacity(viewModel.canDecreaseSelectedMetric(for: project) ? 1 : 0.38) + .accessibilityLabel("Decrease \(viewModel.selectedMetric.title)") + } + + private var metricValue: some View { + Text(String(viewModel.selectedMetricValue(for: project))) + .font(metricValueFont) + .foregroundStyle(LoopLineTheme.readingPrimaryText) + .lineLimit(1) + .minimumScaleFactor(0.65) + .accessibilityLabel("\(viewModel.selectedMetric.title) \(viewModel.selectedMetricValue(for: project))") + } + + private var increaseButton: some View { + Button { + viewModel.incrementSelectedMetric(for: project, in: modelContext) + } label: { + Image(systemName: "plus") + } + .buttonStyle(LoopLineIconButtonStyle( + size: usesLargeControls ? 70 : 48, + foregroundColor: LoopLineTheme.primaryActionForeground, + backgroundColor: LoopLineTheme.primaryActionBackground + )) + .accessibilityLabel("Increase \(viewModel.selectedMetric.title)") + } + + private var metricValueFont: Font { + if usesLargeControls { + return Font.title.weight(.bold).monospacedDigit() + } + return Font.system(size: 34, weight: .bold).monospacedDigit() + } } private final class PDFMarkupPDFView: PDFView { diff --git a/LoopLine/Views/ReadingModeView.swift b/LoopLine/Views/ReadingModeView.swift index e9dc309..c932915 100644 --- a/LoopLine/Views/ReadingModeView.swift +++ b/LoopLine/Views/ReadingModeView.swift @@ -361,59 +361,96 @@ private struct CounterControlPanel: View { private var buttonSize: CGFloat { if isPrimary { - return usesLargeControls ? 76 : 62 + return usesLargeControls ? 88 : 62 } - return usesLargeControls ? 58 : 48 + return usesLargeControls ? 70 : 48 } var body: some View { - HStack(spacing: 16) { - Text(label) - .font(.caption.weight(.bold)) - .foregroundStyle(secondaryText) - .frame(width: 72, alignment: .leading) - - Button(action: decreaseAction) { - Image(systemName: "minus") - } - .buttonStyle(LoopLineIconButtonStyle( - size: buttonSize, - foregroundColor: primaryText, - backgroundColor: secondaryButtonBackground - )) - .disabled(!canDecrease) - .opacity(canDecrease ? 1 : 0.38) - - VStack(spacing: 1) { - Text(value) - .font((isPrimary ? Font.system(size: 48, weight: .bold) : Font.system(size: 34, weight: .bold)).monospacedDigit()) - .foregroundStyle(primaryText) - .lineLimit(1) - .minimumScaleFactor(0.6) - - if let detail { - Text(detail) - .font(.caption.monospacedDigit()) - .foregroundStyle(secondaryText) + if usesLargeControls { + VStack(alignment: .leading, spacing: 10) { + Text(label) + .font(.caption.weight(.bold)) + .foregroundStyle(secondaryText) + + HStack(spacing: 20) { + decreaseButton + + counterValue + .frame(minWidth: isPrimary ? 96 : 76) + + increaseButton } + .frame(maxWidth: .infinity) } - .frame(minWidth: isPrimary ? 74 : 58) + } else { + HStack(spacing: 16) { + Text(label) + .font(.caption.weight(.bold)) + .foregroundStyle(secondaryText) + .frame(width: 72, alignment: .leading) + + decreaseButton + + counterValue + .frame(minWidth: isPrimary ? 74 : 58) + + increaseButton - Button(action: increaseAction) { - Image(systemName: "plus") + Spacer(minLength: 0) } - .buttonStyle(LoopLineIconButtonStyle( - size: buttonSize, - foregroundColor: primaryIncreaseForeground, - backgroundColor: primaryIncreaseBackground - )) - .disabled(!canIncrease) - .opacity(canIncrease ? 1 : 0.38) - - Spacer(minLength: 0) } } + private var decreaseButton: some View { + Button(action: decreaseAction) { + Image(systemName: "minus") + } + .buttonStyle(LoopLineIconButtonStyle( + size: buttonSize, + foregroundColor: primaryText, + backgroundColor: secondaryButtonBackground + )) + .disabled(!canDecrease) + .opacity(canDecrease ? 1 : 0.38) + } + + private var increaseButton: some View { + Button(action: increaseAction) { + Image(systemName: "plus") + } + .buttonStyle(LoopLineIconButtonStyle( + size: buttonSize, + foregroundColor: primaryIncreaseForeground, + backgroundColor: primaryIncreaseBackground + )) + .disabled(!canIncrease) + .opacity(canIncrease ? 1 : 0.38) + } + + private var counterValue: some View { + VStack(spacing: 1) { + Text(value) + .font(valueFont) + .foregroundStyle(primaryText) + .lineLimit(1) + .minimumScaleFactor(0.6) + + if let detail { + Text(detail) + .font(.caption.monospacedDigit()) + .foregroundStyle(secondaryText) + } + } + } + + private var valueFont: Font { + if usesLargeControls { + return (isPrimary ? Font.largeTitle : Font.title).weight(.bold).monospacedDigit() + } + return (isPrimary ? Font.system(size: 48, weight: .bold) : Font.system(size: 34, weight: .bold)).monospacedDigit() + } + private var primaryText: Color { LoopLineTheme.readingPrimaryText } diff --git a/LoopLine/Views/SettingsView.swift b/LoopLine/Views/SettingsView.swift index b5d5b48..940a34f 100644 --- a/LoopLine/Views/SettingsView.swift +++ b/LoopLine/Views/SettingsView.swift @@ -64,9 +64,9 @@ private struct SettingsForm: View { VStack(alignment: .leading, spacing: 14) { HStack(alignment: .firstTextBaseline) { VStack(alignment: .leading, spacing: 4) { - Text("Guide opacity") + Text("Text row highlight opacity") .font(.headline) - Text("Customize row highlight visibility") + Text("Customize the row highlight in text-imported projects") .font(.caption) .foregroundStyle(LoopLineTheme.secondaryText) }