general: text swap and attempt for menu item fix
This commit is contained in:
@@ -61,10 +61,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func showPopover() {
|
private func showPopover() {
|
||||||
let popover = NSPopover()
|
// Reuse existing popover or create new one
|
||||||
popover.contentSize = NSSize(width: 300, height: 400)
|
if popover == nil {
|
||||||
popover.behavior = .transient
|
let newPopover = NSPopover()
|
||||||
popover.contentViewController = NSHostingController(
|
newPopover.contentSize = NSSize(width: 300, height: 400)
|
||||||
|
newPopover.behavior = .transient
|
||||||
|
popover = newPopover
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always set fresh content
|
||||||
|
popover?.contentViewController = NSHostingController(
|
||||||
rootView: MenuBarContentView(
|
rootView: MenuBarContentView(
|
||||||
timerEngine: timerEngine!,
|
timerEngine: timerEngine!,
|
||||||
settingsManager: settingsManager!,
|
settingsManager: settingsManager!,
|
||||||
@@ -74,10 +80,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if let button = statusItem?.button {
|
if let button = statusItem?.button {
|
||||||
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.popover = popover
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startTimers() {
|
private func startTimers() {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ struct MenuBarButtonStyle: ButtonStyle {
|
|||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 6)
|
RoundedRectangle(cornerRadius: 6)
|
||||||
.fill(
|
.fill(
|
||||||
configuration.isPressed ? Color.accentColor.opacity(0.2) : Color.gray.opacity(0.1)
|
configuration.isPressed
|
||||||
|
? Color.accentColor.opacity(0.2) : Color.gray.opacity(0.1)
|
||||||
)
|
)
|
||||||
.opacity(configuration.isPressed ? 1 : 0)
|
.opacity(configuration.isPressed ? 1 : 0)
|
||||||
)
|
)
|
||||||
@@ -28,9 +29,9 @@ struct MenuBarHoverButtonStyle: ButtonStyle {
|
|||||||
|
|
||||||
func makeBody(configuration: Configuration) -> some View {
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
configuration.label
|
configuration.label
|
||||||
.background(
|
.glassEffect(
|
||||||
RoundedRectangle(cornerRadius: 6)
|
isHovered ? .regular.tint(.accentColor).interactive() : .regular,
|
||||||
.fill(isHovered ? Color.accentColor.opacity(0.35) : Color.clear)
|
in: .rect(cornerRadius: 6)
|
||||||
)
|
)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
@@ -180,23 +181,23 @@ struct TimerStatusRow: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if let onDevTrigger = onDevTrigger {
|
if let onDevTrigger = onDevTrigger {
|
||||||
Button(action: onDevTrigger) {
|
Button(action: onDevTrigger) {
|
||||||
Image(systemName: "bolt.fill")
|
Image(systemName: "bolt.fill")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.yellow)
|
.foregroundColor(.yellow)
|
||||||
.padding(6)
|
.padding(6)
|
||||||
.background(
|
}
|
||||||
Circle()
|
.buttonStyle(.plain)
|
||||||
.fill(isHoveredDevTrigger ? Color.yellow.opacity(0.35) : Color.clear)
|
.glassEffect(
|
||||||
)
|
isHoveredDevTrigger ? .regular.tint(.yellow) : .regular,
|
||||||
|
in: .circle
|
||||||
|
)
|
||||||
|
.help("Trigger \(type.displayName) reminder now (dev)")
|
||||||
|
.onHover { hovering in
|
||||||
|
isHoveredDevTrigger = hovering
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
|
||||||
.help("Trigger \(type.displayName) reminder now (dev)")
|
|
||||||
.onHover { hovering in
|
|
||||||
isHoveredDevTrigger = hovering
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Button(action: onSkip) {
|
Button(action: onSkip) {
|
||||||
@@ -204,25 +205,27 @@ struct TimerStatusRow: View {
|
|||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
.padding(6)
|
.padding(6)
|
||||||
.background(
|
|
||||||
Circle()
|
|
||||||
.fill(isHoveredSkip ? Color.accentColor.opacity(0.35) : Color.clear)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
.glassEffect(
|
||||||
|
isHoveredSkip ? .regular.tint(.accentColor) : .regular,
|
||||||
|
in: .circle
|
||||||
|
)
|
||||||
.help("Skip to next \(type.displayName) reminder")
|
.help("Skip to next \(type.displayName) reminder")
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
isHoveredSkip = hovering
|
isHoveredSkip = hovering
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(.horizontal, 8)
|
||||||
|
.padding(.vertical, 6)
|
||||||
|
.glassEffect(
|
||||||
|
isHoveredBody ? .regular.tint(.accentColor) : .regular,
|
||||||
|
in: .rect(cornerRadius: 6)
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 8)
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
isHoveredBody = hovering
|
isHoveredBody = hovering
|
||||||
}.background(
|
}
|
||||||
RoundedRectangle(cornerRadius: 6).fill(
|
|
||||||
isHoveredBody ? Color.accentColor.opacity(0.35) : Color.clear)
|
|
||||||
)
|
|
||||||
.padding(.horizontal)
|
|
||||||
.padding(.vertical, 4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var iconColor: Color {
|
private var iconColor: Color {
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ struct OnboardingContainerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingsOnboardingView(
|
SettingsOnboardingView(
|
||||||
launchAtLogin: $launchAtLogin
|
launchAtLogin: $launchAtLogin,
|
||||||
|
isOnboarding: true
|
||||||
)
|
)
|
||||||
.tag(4)
|
.tag(4)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SettingsOnboardingView: View {
|
struct SettingsOnboardingView: View {
|
||||||
@Binding var launchAtLogin: Bool
|
@Binding var launchAtLogin: Bool
|
||||||
|
var isOnboarding: Bool = true
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 30) {
|
VStack(spacing: 30) {
|
||||||
@@ -18,7 +19,7 @@ struct SettingsOnboardingView: View {
|
|||||||
.font(.system(size: 80))
|
.font(.system(size: 80))
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
|
|
||||||
Text("Final Settings")
|
Text(isOnboarding ? "Final Settings" : "General Settings")
|
||||||
.font(.system(size: 36, weight: .bold))
|
.font(.system(size: 36, weight: .bold))
|
||||||
|
|
||||||
Text("Configure app preferences and support the project")
|
Text("Configure app preferences and support the project")
|
||||||
@@ -135,12 +136,14 @@ struct SettingsOnboardingView: View {
|
|||||||
|
|
||||||
#Preview("Settings Onboarding - Launch Disabled") {
|
#Preview("Settings Onboarding - Launch Disabled") {
|
||||||
SettingsOnboardingView(
|
SettingsOnboardingView(
|
||||||
launchAtLogin: .constant(false)
|
launchAtLogin: .constant(false),
|
||||||
|
isOnboarding: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Settings Onboarding - Launch Enabled") {
|
#Preview("Settings Onboarding - Launch Enabled") {
|
||||||
SettingsOnboardingView(
|
SettingsOnboardingView(
|
||||||
launchAtLogin: .constant(true)
|
launchAtLogin: .constant(true),
|
||||||
|
isOnboarding: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ struct BlinkReminderView: View {
|
|||||||
var onDismiss: () -> Void
|
var onDismiss: () -> Void
|
||||||
|
|
||||||
@State private var opacity: Double = 0
|
@State private var opacity: Double = 0
|
||||||
|
@State private var scale: CGFloat = 0
|
||||||
@State private var blinkState: BlinkState = .open
|
@State private var blinkState: BlinkState = .open
|
||||||
@State private var blinkCount = 0
|
@State private var blinkCount = 0
|
||||||
|
|
||||||
|
private let screenHeight = NSScreen.main?.frame.height ?? 800
|
||||||
|
private let screenWidth = NSScreen.main?.frame.width ?? 1200
|
||||||
|
|
||||||
enum BlinkState {
|
enum BlinkState {
|
||||||
case open
|
case open
|
||||||
case closed
|
case closed
|
||||||
@@ -21,37 +25,35 @@ struct BlinkReminderView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
RoundedRectangle(cornerRadius: 20)
|
Image(systemName: blinkState == .open ? "eye.circle" : "eye.slash.circle")
|
||||||
.fill(Color.white)
|
.font(.system(size: scale))
|
||||||
.shadow(color: .black.opacity(0.3), radius: 10, x: 0, y: 5)
|
.foregroundColor(.accentColor)
|
||||||
.frame(width: 100, height: 100)
|
.shadow(color: .black.opacity(0.2), radius: 5, x: 0, y: 2)
|
||||||
.overlay(
|
|
||||||
BlinkingFace(isOpen: blinkState == .open)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.opacity(opacity)
|
.opacity(opacity)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||||
.padding(.top, NSScreen.main?.frame.height ?? 800 * 0.1)
|
.padding(.top, screenHeight * 0.1)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
startAnimation()
|
startAnimation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startAnimation() {
|
private func startAnimation() {
|
||||||
// Fade in
|
// Fade in and grow
|
||||||
withAnimation(.easeIn(duration: 0.3)) {
|
withAnimation(.easeOut(duration: 0.3)) {
|
||||||
opacity = 1.0
|
opacity = 1.0
|
||||||
|
scale = screenWidth * 0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start blinking after fade in
|
// Start blinking after fade in
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||||
performBlinks()
|
performBlinks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func performBlinks() {
|
private func performBlinks() {
|
||||||
let blinkDuration = 0.1
|
let blinkDuration = 0.15
|
||||||
let pauseBetweenBlinks = 0.5
|
let pauseBetweenBlinks = 0.2
|
||||||
|
|
||||||
func blink() {
|
func blink() {
|
||||||
// Close eyes
|
// Close eyes
|
||||||
@@ -67,7 +69,7 @@ struct BlinkReminderView: View {
|
|||||||
|
|
||||||
blinkCount += 1
|
blinkCount += 1
|
||||||
|
|
||||||
if blinkCount < 3 {
|
if blinkCount < 2 {
|
||||||
// Pause before next blink
|
// Pause before next blink
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + pauseBetweenBlinks) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + pauseBetweenBlinks) {
|
||||||
blink()
|
blink()
|
||||||
@@ -87,6 +89,7 @@ struct BlinkReminderView: View {
|
|||||||
private func fadeOut() {
|
private func fadeOut() {
|
||||||
withAnimation(.easeOut(duration: 0.3)) {
|
withAnimation(.easeOut(duration: 0.3)) {
|
||||||
opacity = 0
|
opacity = 0
|
||||||
|
scale = screenWidth * 0.05
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
@@ -95,46 +98,6 @@ struct BlinkReminderView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BlinkingFace: View {
|
|
||||||
let isOpen: Bool
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
// Simple face
|
|
||||||
Circle()
|
|
||||||
.fill(Color.yellow)
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
|
|
||||||
// Eyes
|
|
||||||
HStack(spacing: 12) {
|
|
||||||
if isOpen {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.black)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
Circle()
|
|
||||||
.fill(Color.black)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
} else {
|
|
||||||
// Closed eyes (lines)
|
|
||||||
Rectangle()
|
|
||||||
.fill(Color.black)
|
|
||||||
.frame(width: 10, height: 2)
|
|
||||||
Rectangle()
|
|
||||||
.fill(Color.black)
|
|
||||||
.frame(width: 10, height: 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.offset(y: -8)
|
|
||||||
|
|
||||||
// Smile
|
|
||||||
Arc(startAngle: .degrees(20), endAngle: .degrees(160), clockwise: false)
|
|
||||||
.stroke(Color.black, lineWidth: 2)
|
|
||||||
.frame(width: 30, height: 15)
|
|
||||||
.offset(y: 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview("Blink Reminder") {
|
#Preview("Blink Reminder") {
|
||||||
BlinkReminderView(onDismiss: {})
|
BlinkReminderView(onDismiss: {})
|
||||||
.frame(width: 800, height: 600)
|
.frame(width: 800, height: 600)
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ struct SettingsWindowView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingsOnboardingView(
|
SettingsOnboardingView(
|
||||||
launchAtLogin: $launchAtLogin
|
launchAtLogin: $launchAtLogin,
|
||||||
|
isOnboarding: false
|
||||||
)
|
)
|
||||||
.tag(3)
|
.tag(3)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user