mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Add Click Monitor for ensuring consistent open on click
This commit is contained in:
parent
311e2551c5
commit
72d101b1be
3 changed files with 90 additions and 6 deletions
|
|
@ -126,6 +126,8 @@ struct ContentView: View {
|
||||||
@State private var hoverTask: Task<Void, Never>?
|
@State private var hoverTask: Task<Void, Never>?
|
||||||
@State private var isHovering: Bool = false
|
@State private var isHovering: Bool = false
|
||||||
@State private var lastHapticTime: Date = Date()
|
@State private var lastHapticTime: Date = Date()
|
||||||
|
@State private var clickMonitor: Any?
|
||||||
|
@State private var hostingWindow: NSWindow?
|
||||||
|
|
||||||
@State private var gestureProgress: CGFloat = .zero
|
@State private var gestureProgress: CGFloat = .zero
|
||||||
@State private var skipGestureActiveDirection: MusicManager.SkipDirection?
|
@State private var skipGestureActiveDirection: MusicManager.SkipDirection?
|
||||||
|
|
@ -271,6 +273,7 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
.conditionalModifier(interactionsEnabled) { view in
|
.conditionalModifier(interactionsEnabled) { view in
|
||||||
view
|
view
|
||||||
|
.contentShape(Rectangle())
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
handleHover(hovering)
|
handleHover(hovering)
|
||||||
}
|
}
|
||||||
|
|
@ -389,12 +392,37 @@ struct ContentView: View {
|
||||||
// #endif
|
// #endif
|
||||||
// .keyboardShortcut("E", modifiers: .command)
|
// .keyboardShortcut("E", modifiers: .command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if interactionsEnabled && vm.notchState == .closed {
|
||||||
|
Color.clear
|
||||||
|
.frame(height: 8)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .top)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onHover { hovering in
|
||||||
|
handleHover(hovering)
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
if Defaults[.enableHaptics] {
|
||||||
|
triggerHapticIfAllowed()
|
||||||
|
}
|
||||||
|
openNotch()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.frame(
|
.background(WindowAccessor { window in
|
||||||
maxWidth: dynamicNotchSize.width,
|
hostingWindow = window
|
||||||
maxHeight: dynamicNotchSize.height + currentShadowPadding,
|
})
|
||||||
alignment: .top
|
.onAppear {
|
||||||
)
|
startGlobalClickMonitor()
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
stopGlobalClickMonitor()
|
||||||
|
}
|
||||||
|
.frame(
|
||||||
|
maxWidth: dynamicNotchSize.width,
|
||||||
|
maxHeight: dynamicNotchSize.height + currentShadowPadding,
|
||||||
|
alignment: .top
|
||||||
|
)
|
||||||
.environmentObject(privacyManager)
|
.environmentObject(privacyManager)
|
||||||
.onChange(of: dynamicNotchSize) { oldSize, newSize in
|
.onChange(of: dynamicNotchSize) { oldSize, newSize in
|
||||||
guard oldSize != newSize else { return }
|
guard oldSize != newSize else { return }
|
||||||
|
|
@ -1430,6 +1458,37 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func startGlobalClickMonitor() {
|
||||||
|
guard clickMonitor == nil else { return }
|
||||||
|
clickMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDown]) { _ in
|
||||||
|
handleGlobalClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopGlobalClickMonitor() {
|
||||||
|
if let clickMonitor {
|
||||||
|
NSEvent.removeMonitor(clickMonitor)
|
||||||
|
self.clickMonitor = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleGlobalClick() {
|
||||||
|
guard !lockScreenManager.isLocked else { return }
|
||||||
|
guard vm.notchState == .closed else { return }
|
||||||
|
guard let hostingWindow else { return }
|
||||||
|
|
||||||
|
let cursorLocation = NSEvent.mouseLocation
|
||||||
|
let hitFrame = hostingWindow.frame.insetBy(dx: -2, dy: -2)
|
||||||
|
guard hitFrame.contains(cursorLocation) else { return }
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if Defaults[.enableHaptics] {
|
||||||
|
triggerHapticIfAllowed()
|
||||||
|
}
|
||||||
|
openNotch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Hover Management
|
// MARK: - Hover Management
|
||||||
|
|
||||||
/// Handle hover state changes with debouncing
|
/// Handle hover state changes with debouncing
|
||||||
|
|
@ -1992,6 +2051,24 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct WindowAccessor: NSViewRepresentable {
|
||||||
|
let callback: (NSWindow?) -> Void
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> NSView {
|
||||||
|
let view = NSView(frame: .zero)
|
||||||
|
DispatchQueue.main.async { [weak view] in
|
||||||
|
callback(view?.window)
|
||||||
|
}
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSView(_ nsView: NSView, context: Context) {
|
||||||
|
DispatchQueue.main.async { [weak nsView] in
|
||||||
|
callback(nsView?.window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private enum MusicSecondaryLiveActivity: Equatable {
|
private enum MusicSecondaryLiveActivity: Equatable {
|
||||||
case timer
|
case timer
|
||||||
case reminder(ReminderLiveActivityManager.ReminderEntry)
|
case reminder(ReminderLiveActivityManager.ReminderEntry)
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,12 @@ struct DynamicNotchApp: App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class FirstMouseHostingView<Content: View>: NSHostingView<Content> {
|
||||||
|
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
static var shared: AppDelegate? {
|
static var shared: AppDelegate? {
|
||||||
NSApplication.shared.delegate as? AppDelegate
|
NSApplication.shared.delegate as? AppDelegate
|
||||||
|
|
@ -223,7 +229,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
window.animationBehavior = .none
|
window.animationBehavior = .none
|
||||||
|
|
||||||
window.contentView = NSHostingView(
|
window.contentView = FirstMouseHostingView(
|
||||||
rootView: ContentView()
|
rootView: ContentView()
|
||||||
.environmentObject(viewModel)
|
.environmentObject(viewModel)
|
||||||
.environmentObject(webcamManager)
|
.environmentObject(webcamManager)
|
||||||
|
|
|
||||||
|
|
@ -64,4 +64,5 @@ class DynamicIslandWindow: NSPanel {
|
||||||
override var canBecomeMain: Bool {
|
override var canBecomeMain: Bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue