mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Fucked up a lot - need to fix
This commit is contained in:
parent
a9f9ac3aa8
commit
eefd951522
6 changed files with 236 additions and 44 deletions
|
|
@ -16957,6 +16957,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Show timer name" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Shows a lock icon in the Dynamic Island when the screen is locked. Use the toggles above to control the lock screen media panel and weather capsule." : {
|
"Shows a lock icon in the Dynamic Island when the screen is locked. Use the toggles above to control the lock screen media panel and weather capsule." : {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ struct NotchTimerView: View {
|
||||||
// Timer icon in center
|
// Timer icon in center
|
||||||
Image(systemName: "timer")
|
Image(systemName: "timer")
|
||||||
.font(.system(size: 32, weight: .medium))
|
.font(.system(size: 32, weight: .medium))
|
||||||
.foregroundStyle(timerManager.currentColor)
|
.foregroundStyle(openIconColor)
|
||||||
.scaleEffect(timerManager.isRunning && timerManager.isTimerActive ? 1.1 : 1.0)
|
.scaleEffect(timerManager.isRunning && timerManager.isTimerActive ? 1.1 : 1.0)
|
||||||
.animation(
|
.animation(
|
||||||
timerManager.isRunning && timerManager.isTimerActive ?
|
timerManager.isRunning && timerManager.isTimerActive ?
|
||||||
|
|
@ -287,6 +287,13 @@ struct NotchTimerView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension NotchTimerView {
|
||||||
|
var openIconColor: Color {
|
||||||
|
guard timerManager.isTimerActive && timerManager.isRunning else { return .white.opacity(0.9) }
|
||||||
|
return timerManager.currentColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
NotchTimerView()
|
NotchTimerView()
|
||||||
.environmentObject(DynamicIslandViewModel())
|
.environmentObject(DynamicIslandViewModel())
|
||||||
|
|
|
||||||
|
|
@ -1713,6 +1713,7 @@ struct TimerSettings: View {
|
||||||
@Default(.timerIconColorMode) private var colorMode
|
@Default(.timerIconColorMode) private var colorMode
|
||||||
@Default(.timerSolidColor) private var solidColor
|
@Default(.timerSolidColor) private var solidColor
|
||||||
@Default(.timerShowsCountdown) private var showsCountdown
|
@Default(.timerShowsCountdown) private var showsCountdown
|
||||||
|
@Default(.timerShowsLabel) private var showsLabel
|
||||||
@Default(.timerShowsProgress) private var showsProgress
|
@Default(.timerShowsProgress) private var showsProgress
|
||||||
@Default(.timerProgressStyle) private var progressStyle
|
@Default(.timerProgressStyle) private var progressStyle
|
||||||
@AppStorage("customTimerDuration") private var customTimerDuration: Double = 600
|
@AppStorage("customTimerDuration") private var customTimerDuration: Double = 600
|
||||||
|
|
@ -1777,6 +1778,7 @@ struct TimerSettings: View {
|
||||||
ColorPicker("Solid colour", selection: $solidColor, supportsOpacity: false)
|
ColorPicker("Solid colour", selection: $solidColor, supportsOpacity: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Toggle("Show timer name", isOn: $showsLabel)
|
||||||
Toggle("Show countdown", isOn: $showsCountdown)
|
Toggle("Show countdown", isOn: $showsCountdown)
|
||||||
Toggle("Show progress", isOn: $showsProgress)
|
Toggle("Show progress", isOn: $showsProgress)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,16 @@ import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
struct TimerIconAnimation: View {
|
struct TimerIconAnimation: View {
|
||||||
|
@ObservedObject var timerManager = TimerManager.shared
|
||||||
|
@Default(.timerIconColorMode) private var colorMode
|
||||||
|
@Default(.timerSolidColor) private var solidColor
|
||||||
|
@Default(.timerPresets) private var timerPresets
|
||||||
@Default(.accentColor) private var accentColor
|
@Default(.accentColor) private var accentColor
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(accentColor.gradient)
|
.fill(resolvedColor.gradient)
|
||||||
.frame(width: 24, height: 24)
|
.frame(width: 24, height: 24)
|
||||||
|
|
||||||
Image(systemName: "timer")
|
Image(systemName: "timer")
|
||||||
|
|
@ -22,6 +26,20 @@ struct TimerIconAnimation: View {
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var resolvedColor: Color {
|
||||||
|
guard timerManager.isTimerActive else { return accentColor }
|
||||||
|
switch colorMode {
|
||||||
|
case .adaptive:
|
||||||
|
if let presetId = timerManager.activePresetId,
|
||||||
|
let preset = timerPresets.first(where: { $0.id == presetId }) {
|
||||||
|
return preset.color
|
||||||
|
}
|
||||||
|
return timerManager.timerColor
|
||||||
|
case .solid:
|
||||||
|
return solidColor
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TimerIconView: NSViewRepresentable {
|
struct TimerIconView: NSViewRepresentable {
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,96 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
|
#if canImport(AppKit)
|
||||||
|
import AppKit
|
||||||
|
typealias PlatformFont = NSFont
|
||||||
|
#elseif canImport(UIKit)
|
||||||
|
import UIKit
|
||||||
|
typealias PlatformFont = UIFont
|
||||||
|
#endif
|
||||||
|
|
||||||
struct TimerLiveActivity: View {
|
struct TimerLiveActivity: View {
|
||||||
@EnvironmentObject var vm: DynamicIslandViewModel
|
@EnvironmentObject var vm: DynamicIslandViewModel
|
||||||
@ObservedObject var timerManager = TimerManager.shared
|
@ObservedObject var timerManager = TimerManager.shared
|
||||||
@State private var isHovering: Bool = false
|
@State private var isHovering: Bool = false
|
||||||
@Default(.timerShowsCountdown) private var showsCountdown
|
@Default(.timerShowsCountdown) private var showsCountdown
|
||||||
@Default(.timerShowsProgress) private var showsProgress
|
@Default(.timerShowsProgress) private var showsProgress
|
||||||
|
@Default(.timerShowsLabel) private var showsLabel
|
||||||
@Default(.timerProgressStyle) private var progressStyle
|
@Default(.timerProgressStyle) private var progressStyle
|
||||||
@Default(.timerIconColorMode) private var colorMode
|
@Default(.timerIconColorMode) private var colorMode
|
||||||
@Default(.timerSolidColor) private var solidColor
|
@Default(.timerSolidColor) private var solidColor
|
||||||
@Default(.timerPresets) private var timerPresets
|
@Default(.timerPresets) private var timerPresets
|
||||||
|
|
||||||
private var iconAreaWidth: CGFloat {
|
private var notchContentHeight: CGFloat {
|
||||||
max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12))
|
max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var wingPadding: CGFloat { 18 }
|
||||||
|
|
||||||
|
private var ringWrapsIcon: Bool {
|
||||||
|
showsRingProgress && showsCountdown
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ringOnRight: Bool {
|
||||||
|
showsRingProgress && !ringWrapsIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
private var iconWidth: CGFloat {
|
||||||
|
ringWrapsIcon ? max(notchContentHeight, 32) : max(0, notchContentHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var infoContentWidth: CGFloat {
|
||||||
|
guard showsInfoSection else { return 0 }
|
||||||
|
let textWidth = min(max(titleTextWidth, 44), 220)
|
||||||
|
if showsLabel {
|
||||||
|
return textWidth
|
||||||
|
} else {
|
||||||
|
return min(max(notchContentHeight * 1.4, 64), 220)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var infoWidth: CGFloat {
|
||||||
|
guard showsInfoSection else { return 0 }
|
||||||
|
return infoContentWidth + 18
|
||||||
|
}
|
||||||
|
|
||||||
|
private var leftWingWidth: CGFloat {
|
||||||
|
var width = iconWidth + wingPadding
|
||||||
|
if showsInfoSection {
|
||||||
|
width += 8 + infoWidth
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ringWidth: CGFloat {
|
||||||
|
ringOnRight ? 32 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private var rightWingWidth: CGFloat {
|
||||||
|
var width = wingPadding
|
||||||
|
if ringOnRight {
|
||||||
|
width += ringWidth
|
||||||
|
}
|
||||||
|
if ringOnRight && showsCountdown {
|
||||||
|
width += 8
|
||||||
|
}
|
||||||
|
if showsCountdown {
|
||||||
|
width += countdownWidth
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
private var titleTextWidth: CGFloat {
|
||||||
|
measureTextWidth(timerManager.timerName, font: systemFont(size: 12, weight: .medium))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var countdownTextWidth: CGFloat {
|
||||||
|
measureTextWidth(timerManager.formattedRemainingTime(), font: monospacedDigitFont(size: 13, weight: .semibold))
|
||||||
|
}
|
||||||
|
|
||||||
private var countdownWidth: CGFloat {
|
private var countdownWidth: CGFloat {
|
||||||
guard showsCountdown else { return 0 }
|
guard showsCountdown else { return 0 }
|
||||||
let characters = max(timerManager.formattedRemainingTime().count, 4)
|
return max(countdownTextWidth + 20, 80)
|
||||||
let base = CGFloat(characters) * 9.5 + 24
|
|
||||||
return max(base, 72)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var clampedProgress: Double {
|
private var clampedProgress: Double {
|
||||||
|
|
@ -51,21 +121,70 @@ struct TimerLiveActivity: View {
|
||||||
showsProgress && progressStyle == .bar
|
showsProgress && progressStyle == .bar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var showsInfoSection: Bool {
|
||||||
|
showsLabel || (showsBarProgress && !showsCountdown)
|
||||||
|
}
|
||||||
|
|
||||||
private var activePresetColor: Color? {
|
private var activePresetColor: Color? {
|
||||||
guard let presetId = timerManager.activePresetId else { return nil }
|
guard let presetId = timerManager.activePresetId else { return nil }
|
||||||
return timerPresets.first { $0.id == presetId }?.color
|
return timerPresets.first { $0.id == presetId }?.color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func measureTextWidth(_ text: String, font: PlatformFont) -> CGFloat {
|
||||||
|
let attributes: [NSAttributedString.Key: Any] = [.font: font]
|
||||||
|
let width = NSAttributedString(string: text, attributes: attributes).size().width
|
||||||
|
return CGFloat(ceil(width))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func systemFont(size: CGFloat, weight: PlatformFont.Weight) -> PlatformFont {
|
||||||
|
#if canImport(AppKit)
|
||||||
|
return NSFont.systemFont(ofSize: size, weight: weight)
|
||||||
|
#else
|
||||||
|
return UIFont.systemFont(ofSize: size, weight: weight)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func monospacedDigitFont(size: CGFloat, weight: PlatformFont.Weight) -> PlatformFont {
|
||||||
|
#if canImport(AppKit)
|
||||||
|
return NSFont.monospacedDigitSystemFont(ofSize: size, weight: weight)
|
||||||
|
#else
|
||||||
|
return UIFont.monospacedDigitSystemFont(ofSize: size, weight: weight)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 10) {
|
HStack(spacing: 0) {
|
||||||
iconSection
|
Color.clear
|
||||||
infoSection
|
.frame(width: leftWingWidth, height: notchContentHeight)
|
||||||
if showsRingProgress {
|
.background(alignment: .leading) {
|
||||||
ringSection
|
HStack(spacing: showsInfoSection ? 8 : 0) {
|
||||||
}
|
iconSection
|
||||||
if showsCountdown {
|
if showsInfoSection {
|
||||||
countdownSection
|
infoSection
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.padding(.leading, wingPadding / 2)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle()
|
||||||
|
.fill(.black)
|
||||||
|
.frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0), height: notchContentHeight)
|
||||||
|
|
||||||
|
Color.clear
|
||||||
|
.frame(width: rightWingWidth, height: notchContentHeight)
|
||||||
|
.background(alignment: .trailing) {
|
||||||
|
HStack(spacing: ringOnRight && showsCountdown ? 8 : 0) {
|
||||||
|
if ringOnRight {
|
||||||
|
ringSection
|
||||||
|
}
|
||||||
|
if showsCountdown {
|
||||||
|
countdownSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.trailing, wingPadding / 2)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .trailing)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
|
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
|
|
@ -77,42 +196,81 @@ struct TimerLiveActivity: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var iconSection: some View {
|
private var iconSection: some View {
|
||||||
let iconSize = max(20, iconAreaWidth - 4)
|
let ringDiameter = ringWrapsIcon ? max(iconWidth, 32) : iconWidth
|
||||||
return Image(systemName: "timer")
|
let iconSize = ringWrapsIcon ? max(ringDiameter - 10, 18) : max(20, iconWidth - 4)
|
||||||
.font(.system(size: 16, weight: .semibold))
|
|
||||||
.foregroundStyle(glyphColor)
|
return ZStack {
|
||||||
.frame(width: iconSize, height: iconSize)
|
if ringWrapsIcon {
|
||||||
.frame(width: iconAreaWidth, height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center)
|
Circle()
|
||||||
|
.stroke(Color.white.opacity(0.15), lineWidth: 3)
|
||||||
|
.frame(width: ringDiameter, height: ringDiameter)
|
||||||
|
|
||||||
|
Circle()
|
||||||
|
.trim(from: 0, to: clampedProgress)
|
||||||
|
.stroke(glyphColor, style: StrokeStyle(lineWidth: 3, lineCap: .round))
|
||||||
|
.rotationEffect(.degrees(-90))
|
||||||
|
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
||||||
|
.frame(width: ringDiameter, height: ringDiameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
Image(systemName: "timer")
|
||||||
|
.font(.system(size: 16, weight: .semibold))
|
||||||
|
.foregroundStyle(glyphColor)
|
||||||
|
.frame(width: iconSize, height: iconSize)
|
||||||
|
}
|
||||||
|
.frame(width: ringWrapsIcon ? ringDiameter : iconWidth,
|
||||||
|
height: notchContentHeight,
|
||||||
|
alignment: .center)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var infoSection: some View {
|
private var infoSection: some View {
|
||||||
Rectangle()
|
let availableWidth = max(0, infoWidth - 18)
|
||||||
|
let resolvedTextWidth = min(max(titleTextWidth, 44), availableWidth)
|
||||||
|
let shouldMarquee = showsLabel && (timerManager.isFinished || timerManager.isOvertime || titleTextWidth > availableWidth)
|
||||||
|
let showsBarHere = showsBarProgress && !showsCountdown
|
||||||
|
let barWidth = showsLabel ? resolvedTextWidth : availableWidth
|
||||||
|
|
||||||
|
return Rectangle()
|
||||||
.fill(.black)
|
.fill(.black)
|
||||||
.overlay(
|
.frame(width: infoWidth, height: notchContentHeight)
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
.overlay(alignment: .leading) {
|
||||||
if timerManager.isFinished || timerManager.isOvertime {
|
VStack(alignment: .leading, spacing: showsBarHere ? 4 : 0) {
|
||||||
GeometryReader { geo in
|
if showsLabel {
|
||||||
|
if shouldMarquee {
|
||||||
MarqueeText(
|
MarqueeText(
|
||||||
.constant(timerManager.timerName),
|
.constant(timerManager.timerName),
|
||||||
|
font: .system(size: 12, weight: .medium),
|
||||||
|
nsFont: .callout,
|
||||||
textColor: .white,
|
textColor: .white,
|
||||||
minDuration: 0.25,
|
minDuration: 0.25,
|
||||||
frameWidth: geo.size.width - 12
|
frameWidth: resolvedTextWidth
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Text(timerManager.timerName)
|
||||||
|
.font(.system(size: 12, weight: .medium))
|
||||||
|
.lineLimit(1)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.transition(.opacity.combined(with: .move(edge: .top)))
|
||||||
|
.frame(width: resolvedTextWidth, alignment: .leading)
|
||||||
}
|
}
|
||||||
.frame(height: 16)
|
}
|
||||||
} else {
|
|
||||||
Text(timerManager.timerName)
|
if showsBarHere {
|
||||||
.font(.system(size: 12, weight: .medium))
|
Capsule()
|
||||||
.lineLimit(1)
|
.fill(Color.white.opacity(0.12))
|
||||||
.foregroundStyle(.white)
|
.frame(width: barWidth, height: 3)
|
||||||
.transition(.opacity.combined(with: .move(edge: .top)))
|
.overlay(alignment: .leading) {
|
||||||
|
Capsule()
|
||||||
|
.fill(glyphColor)
|
||||||
|
.frame(width: barWidth * max(0, CGFloat(clampedProgress)))
|
||||||
|
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.leading, 12)
|
||||||
.padding(.vertical, 6)
|
.padding(.trailing, 6)
|
||||||
.animation(.smooth, value: timerManager.isFinished)
|
}
|
||||||
)
|
.animation(.smooth, value: timerManager.isFinished)
|
||||||
.frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var ringSection: some View {
|
private var ringSection: some View {
|
||||||
|
|
@ -126,10 +284,12 @@ struct TimerLiveActivity: View {
|
||||||
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
||||||
}
|
}
|
||||||
.frame(width: 26, height: 26)
|
.frame(width: 26, height: 26)
|
||||||
|
.frame(width: ringWidth, height: notchContentHeight, alignment: .center)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var countdownSection: some View {
|
private var countdownSection: some View {
|
||||||
VStack(spacing: 4) {
|
let barWidth = max(countdownTextWidth, 1)
|
||||||
|
return VStack(spacing: 4) {
|
||||||
Text(timerManager.formattedRemainingTime())
|
Text(timerManager.formattedRemainingTime())
|
||||||
.font(.system(size: 13, weight: .semibold, design: .monospaced))
|
.font(.system(size: 13, weight: .semibold, design: .monospaced))
|
||||||
.foregroundColor(timerManager.isOvertime ? .red : .white)
|
.foregroundColor(timerManager.isOvertime ? .red : .white)
|
||||||
|
|
@ -140,18 +300,19 @@ struct TimerLiveActivity: View {
|
||||||
if showsBarProgress {
|
if showsBarProgress {
|
||||||
Capsule()
|
Capsule()
|
||||||
.fill(Color.white.opacity(0.12))
|
.fill(Color.white.opacity(0.12))
|
||||||
.frame(height: 3)
|
.frame(width: barWidth, height: 3)
|
||||||
.overlay(alignment: .leading) {
|
.overlay(alignment: .leading) {
|
||||||
Capsule()
|
Capsule()
|
||||||
.fill(glyphColor)
|
.fill(glyphColor)
|
||||||
.frame(width: max(0, countdownWidth - 20) * max(0, CGFloat(clampedProgress)))
|
.frame(width: barWidth * max(0, CGFloat(clampedProgress)))
|
||||||
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
.animation(.smooth(duration: 0.25), value: clampedProgress)
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.trailing, 8)
|
.padding(.trailing, 8)
|
||||||
.frame(width: countdownWidth,
|
.frame(width: countdownWidth,
|
||||||
height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center)
|
height: notchContentHeight, alignment: .center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,7 @@ extension Defaults.Keys {
|
||||||
static let timerIconColorMode = Key<TimerIconColorMode>("timerIconColorMode", default: .adaptive)
|
static let timerIconColorMode = Key<TimerIconColorMode>("timerIconColorMode", default: .adaptive)
|
||||||
static let timerSolidColor = Key<Color>("timerSolidColor", default: .blue)
|
static let timerSolidColor = Key<Color>("timerSolidColor", default: .blue)
|
||||||
static let timerShowsCountdown = Key<Bool>("timerShowsCountdown", default: true)
|
static let timerShowsCountdown = Key<Bool>("timerShowsCountdown", default: true)
|
||||||
|
static let timerShowsLabel = Key<Bool>("timerShowsLabel", default: true)
|
||||||
static let timerShowsProgress = Key<Bool>("timerShowsProgress", default: true)
|
static let timerShowsProgress = Key<Bool>("timerShowsProgress", default: true)
|
||||||
static let timerProgressStyle = Key<TimerProgressStyle>("timerProgressStyle", default: .bar)
|
static let timerProgressStyle = Key<TimerProgressStyle>("timerProgressStyle", default: .bar)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue