mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Changes applied: Still fucked but working
This commit is contained in:
parent
4136f6d9b9
commit
310dde6573
8 changed files with 18739 additions and 1538 deletions
|
|
@ -355,7 +355,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
|
@ -397,7 +397,7 @@
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
CODE_SIGN_ENTITLEMENTS = DynamicIsland/DynamicIsland.entitlements;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
1615
DynamicIsland/Localizable.xcstrings.backup
Normal file
1615
DynamicIsland/Localizable.xcstrings.backup
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -71,35 +71,28 @@ class SettingsWindowController: NSWindowController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func showWindow() {
|
func showWindow() {
|
||||||
// Set app to regular mode first
|
// Ensure window exists
|
||||||
NSApp.setActivationPolicy(.regular)
|
_ = window
|
||||||
|
|
||||||
// If window is already visible, bring it to front properly
|
// If window is already visible, bring it to front properly
|
||||||
if window?.isVisible == true {
|
if window?.isVisible == true {
|
||||||
forceWindowToFront()
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
window?.orderFrontRegardless()
|
||||||
|
window?.makeKeyAndOrderFront(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the window with proper ordering
|
// Show the window with proper ordering
|
||||||
window?.center()
|
|
||||||
forceWindowToFront()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func forceWindowToFront() {
|
|
||||||
// Multi-step approach to ensure window gets focus
|
|
||||||
window?.orderFrontRegardless()
|
window?.orderFrontRegardless()
|
||||||
window?.makeKeyAndOrderFront(nil)
|
window?.makeKeyAndOrderFront(nil)
|
||||||
|
window?.center()
|
||||||
|
|
||||||
// Activate the app with maximum priority
|
// Activate the app and ensure window gets focus
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
|
||||||
// Double-check after a brief delay
|
// Force window to front after activation
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
guard let window = self?.window else { return }
|
self?.window?.makeKeyAndOrderFront(nil)
|
||||||
if !window.isKeyWindow {
|
|
||||||
window.makeKeyAndOrderFront(nil)
|
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,97 +5,107 @@
|
||||||
// Created by Richard Kunkli on 21/08/2024.
|
// Created by Richard Kunkli on 21/08/2024.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
extension View {
|
|
||||||
func panGesture(direction: PanDirection, action: @escaping (CGFloat, NSEvent.Phase) -> Void) -> some View {
|
|
||||||
background(
|
|
||||||
PanGestureView(direction: direction, action: action)
|
|
||||||
.frame(maxWidth: 0, maxHeight: 0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PanGestureView: NSViewRepresentable {
|
|
||||||
let direction: PanDirection
|
|
||||||
let action: (CGFloat, NSEvent.Phase) -> Void
|
|
||||||
|
|
||||||
func makeNSView(context: Context) -> NSView {
|
|
||||||
let view = NSView()
|
|
||||||
NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { event in
|
|
||||||
if event.window == view.window {
|
|
||||||
context.coordinator.handleEvent(event)
|
|
||||||
}
|
|
||||||
return event
|
|
||||||
}
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateNSView(_ nsView: NSView, context: Context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCoordinator() -> Coordinator {
|
|
||||||
Coordinator(direction: direction, action: action)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Coordinator: NSObject {
|
|
||||||
let direction: PanDirection
|
|
||||||
let action: (CGFloat, NSEvent.Phase) -> Void
|
|
||||||
|
|
||||||
var accumulatedScrollDeltaX: CGFloat = 0
|
|
||||||
var accumulatedScrollDeltaY: CGFloat = 0
|
|
||||||
|
|
||||||
init(direction: PanDirection, action: @escaping (CGFloat, NSEvent.Phase) -> Void) {
|
|
||||||
self.direction = direction
|
|
||||||
self.action = action
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func handleEvent(_ event: NSEvent) {
|
|
||||||
if event.type == .scrollWheel {
|
|
||||||
accumulatedScrollDeltaX += event.scrollingDeltaX
|
|
||||||
accumulatedScrollDeltaY += event.scrollingDeltaY
|
|
||||||
|
|
||||||
switch direction {
|
|
||||||
case .down:
|
|
||||||
if accumulatedScrollDeltaY > 0 {
|
|
||||||
handle()
|
|
||||||
}
|
|
||||||
case .up:
|
|
||||||
if accumulatedScrollDeltaY < 0 {
|
|
||||||
handle()
|
|
||||||
}
|
|
||||||
case .left:
|
|
||||||
if accumulatedScrollDeltaX < 0 {
|
|
||||||
handle()
|
|
||||||
}
|
|
||||||
case .right:
|
|
||||||
if accumulatedScrollDeltaX > 0 {
|
|
||||||
handle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle() {
|
|
||||||
if (direction == .left || direction == .right) {
|
|
||||||
action(abs(accumulatedScrollDeltaX), event.phase)
|
|
||||||
} else {
|
|
||||||
action(abs(accumulatedScrollDeltaY), event.phase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.phase == .ended {
|
|
||||||
accumulatedScrollDeltaY = 0
|
|
||||||
accumulatedScrollDeltaX = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PanDirection {
|
enum PanDirection {
|
||||||
case left
|
case left, right, up, down
|
||||||
case right
|
|
||||||
case up
|
var isHorizontal: Bool { self == .left || self == .right }
|
||||||
case down
|
var sign: CGFloat { (self == .right || self == .down) ? 1 : -1 }
|
||||||
|
|
||||||
|
func signed(from translation: CGSize) -> CGFloat { (isHorizontal ? translation.width : translation.height) * sign }
|
||||||
|
func signed(deltaX: CGFloat, deltaY: CGFloat) -> CGFloat { (isHorizontal ? deltaX : deltaY) * sign }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func panGesture(direction: PanDirection, threshold: CGFloat = 4, action: @escaping (CGFloat, NSEvent.Phase) -> Void) -> some View {
|
||||||
|
self
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { value in
|
||||||
|
let s = direction.signed(from: value.translation)
|
||||||
|
guard s > 0, s.magnitude >= threshold else { return }
|
||||||
|
action(s.magnitude, .changed)
|
||||||
|
}
|
||||||
|
.onEnded { _ in action(0, .ended) }
|
||||||
|
)
|
||||||
|
.background(ScrollMonitor(direction: direction, threshold: threshold, action: action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ScrollMonitor: NSViewRepresentable {
|
||||||
|
let direction: PanDirection
|
||||||
|
let threshold: CGFloat
|
||||||
|
let action: (CGFloat, NSEvent.Phase) -> Void
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> NSView {
|
||||||
|
let view = NSView()
|
||||||
|
context.coordinator.installMonitor(on: view)
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
func updateNSView(_ nsView: NSView, context: Context) {}
|
||||||
|
static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) { coordinator.removeMonitor() }
|
||||||
|
|
||||||
|
func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator(direction: direction, threshold: threshold, action: action)
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor final class Coordinator: NSObject {
|
||||||
|
private let direction: PanDirection
|
||||||
|
private let threshold: CGFloat
|
||||||
|
private let action: (CGFloat, NSEvent.Phase) -> Void
|
||||||
|
private var monitor: Any?
|
||||||
|
private var accumulated: CGFloat = 0
|
||||||
|
private var active = false
|
||||||
|
private let noiseThreshold: CGFloat = 0.2
|
||||||
|
|
||||||
|
init(direction: PanDirection, threshold: CGFloat, action: @escaping (CGFloat, NSEvent.Phase) -> Void) {
|
||||||
|
self.direction = direction
|
||||||
|
self.threshold = threshold
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
func installMonitor(on view: NSView) {
|
||||||
|
removeMonitor()
|
||||||
|
monitor = NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel]) { [weak self, weak view] event in
|
||||||
|
guard let self = self, event.window === view?.window else { return event }
|
||||||
|
self.handleScroll(event)
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeMonitor() {
|
||||||
|
if let monitor = monitor {
|
||||||
|
NSEvent.removeMonitor(monitor)
|
||||||
|
self.monitor = nil
|
||||||
|
}
|
||||||
|
accumulated = 0
|
||||||
|
active = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleScroll(_ event: NSEvent) {
|
||||||
|
if event.phase == .ended || event.momentumPhase == .ended {
|
||||||
|
if active {
|
||||||
|
action(accumulated.magnitude, .ended)
|
||||||
|
} else {
|
||||||
|
action(0, .ended)
|
||||||
|
}
|
||||||
|
active = false
|
||||||
|
accumulated = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = direction.signed(deltaX: event.scrollingDeltaX, deltaY: event.scrollingDeltaY)
|
||||||
|
guard s.magnitude > noiseThreshold else { return }
|
||||||
|
accumulated = s > 0 ? accumulated + s : 0
|
||||||
|
|
||||||
|
if !active && accumulated >= threshold {
|
||||||
|
active = true
|
||||||
|
action(accumulated.magnitude, .began)
|
||||||
|
} else if active {
|
||||||
|
action(accumulated.magnitude, .changed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class DynamicIslandStatusMenu: NSMenu {
|
class StatusBarMenu: NSMenu {
|
||||||
|
|
||||||
var statusItem: NSStatusItem!
|
var statusItem: NSStatusItem!
|
||||||
|
|
||||||
|
|
@ -11,7 +11,6 @@ class DynamicIslandStatusMenu: NSMenu {
|
||||||
|
|
||||||
convenience init() {
|
convenience init() {
|
||||||
self.init(title: "DynamicIsland")
|
self.init(title: "DynamicIsland")
|
||||||
setupStatusItem()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init(coder: NSCoder) {
|
required init(coder: NSCoder) {
|
||||||
|
|
@ -23,22 +22,14 @@ class DynamicIslandStatusMenu: NSMenu {
|
||||||
// Initialize the status item
|
// Initialize the status item
|
||||||
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||||
|
|
||||||
|
// Set the menu bar icon
|
||||||
if let button = statusItem.button {
|
if let button = statusItem.button {
|
||||||
button.image = NSImage(systemSymbolName: "music.note", accessibilityDescription: "DynamicIsland")
|
button.image = NSImage(named: "logo")
|
||||||
button.action = #selector(showMenu)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the menu
|
// Set up the menu
|
||||||
self.addItem(NSMenuItem(title: "Quit", action: #selector(quitAction), keyEquivalent: "q"))
|
self.addItem(NSMenuItem(title: "Quit", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q"))
|
||||||
statusItem.menu = self
|
statusItem.menu = self
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func showMenu() {
|
|
||||||
// Handle menu show action
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func quitAction() {
|
|
||||||
NSApp.terminate(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,18 @@ struct PlaybackState {
|
||||||
var lastUpdated: Date = Date.distantPast
|
var lastUpdated: Date = Date.distantPast
|
||||||
var artwork: Data?
|
var artwork: Data?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension PlaybackState: Equatable {
|
||||||
|
static func == (lhs: PlaybackState, rhs: PlaybackState) -> Bool {
|
||||||
|
return lhs.bundleIdentifier == rhs.bundleIdentifier
|
||||||
|
&& lhs.isPlaying == rhs.isPlaying
|
||||||
|
&& lhs.title == rhs.title
|
||||||
|
&& lhs.artist == rhs.artist
|
||||||
|
&& lhs.album == rhs.album
|
||||||
|
&& lhs.currentTime == rhs.currentTime
|
||||||
|
&& lhs.duration == rhs.duration
|
||||||
|
&& lhs.isShuffled == rhs.isShuffled
|
||||||
|
&& lhs.repeatMode == rhs.repeatMode
|
||||||
|
&& lhs.artwork == rhs.artwork
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,12 @@ import Defaults
|
||||||
import MacroVisionKit
|
import MacroVisionKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
|
||||||
class FullscreenMediaDetector: ObservableObject {
|
class FullscreenMediaDetector: ObservableObject {
|
||||||
static let shared = FullscreenMediaDetector()
|
static let shared = FullscreenMediaDetector()
|
||||||
private let detector: MacroVisionKit
|
private let detector: MacroVisionKit
|
||||||
@ObservedObject private var musicManager = MusicManager.shared
|
@ObservedObject private var musicManager = MusicManager.shared
|
||||||
|
@MainActor @Published private(set) var fullscreenStatus: [String: Bool] = [:]
|
||||||
@Published private(set) var fullscreenStatus: [String: Bool] = [:]
|
private var notificationTask: Task<Void, Never>?
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
self.detector = MacroVisionKit.shared
|
self.detector = MacroVisionKit.shared
|
||||||
|
|
@ -25,17 +24,34 @@ class FullscreenMediaDetector: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupNotificationObservers() {
|
private func setupNotificationObservers() {
|
||||||
let nc = NSWorkspace.shared.notificationCenter
|
notificationTask = Task { @Sendable [weak self] in
|
||||||
nc.addObserver(self, selector: #selector(handleChange),
|
await withTaskGroup(of: Void.self) { group in
|
||||||
name: NSWorkspace.activeSpaceDidChangeNotification, object: nil)
|
group.addTask {
|
||||||
nc.addObserver(self, selector: #selector(handleChange),
|
let activeSpaceNotifications = NSWorkspace.shared.notificationCenter.notifications(
|
||||||
name: NSApplication.didChangeScreenParametersNotification, object: nil)
|
named: NSWorkspace.activeSpaceDidChangeNotification
|
||||||
|
)
|
||||||
|
|
||||||
|
for await _ in activeSpaceNotifications {
|
||||||
|
await self?.handleChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group.addTask {
|
||||||
|
let screenParameterNotifications = NSWorkspace.shared.notificationCenter.notifications(
|
||||||
|
named: NSApplication.didChangeScreenParametersNotification
|
||||||
|
)
|
||||||
|
|
||||||
|
for await _ in screenParameterNotifications {
|
||||||
|
await self?.handleChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handleChange() {
|
private func handleChange() async {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
try? await Task.sleep(for: .milliseconds(500))
|
||||||
self.updateFullScreenStatus()
|
self.updateFullScreenStatus()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateFullScreenStatus() {
|
private func updateFullScreenStatus() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue