From fba8b25c9570c2fe506c82f0adb13beab77f604f Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 13 Sep 2024 03:52:48 +0800 Subject: [PATCH 01/12] Add capability to invert the mouse wheel when zooming --- README.md | 6 ++++++ Sources/ColorSamplerWindow.swift | 6 ++++-- Sources/SCColorSamplerConfiguration.swift | 12 ++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7c95504..48675c3 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,12 @@ configuration.zoomValues = [.xxl, .s, .l, .xs] SCColorSampler.sample(configuration: configuration) { ... } ``` +#### Zoom with Mouse Wheel Inverted +```swift +// For example: +configuration.zoomWheelInverse = true +``` + #### Show Color Description

Image showing showColorDescription off option diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index 6a13f2d..99a9de2 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -212,12 +212,14 @@ internal class ColorSamplerWindow: NSWindow { return } - if event.scrollingDeltaY < -1 { + let deltaY = delegate.config.zoomWheelInverse ? -event.scrollingDeltaY : event.scrollingDeltaY + + if deltaY < -1 { guard let nextZoom = zoom?.getNextZoom(available: delegate.config.zoomValues) else { return } zoom = nextZoom - } else if event.scrollingDeltaY > 1 { + } else if deltaY > 1 { guard let previousZoom = zoom?.getPreviousZoom(available: delegate.config.zoomValues) else { return } diff --git a/Sources/SCColorSamplerConfiguration.swift b/Sources/SCColorSamplerConfiguration.swift index b663d9f..5ebe324 100644 --- a/Sources/SCColorSamplerConfiguration.swift +++ b/Sources/SCColorSamplerConfiguration.swift @@ -21,6 +21,7 @@ open class SCColorSamplerConfiguration: NSObject { private var _defaultZoom: ZoomValue = .m private var _loupeShape: LoupeShape = .roundedRect private var _showColorDescription: Bool = true + private var _zoomWheelInverse: Bool = false private var _colorDescriptionMethod: (NSColor) -> String = { color in let red = Int((color.redComponent * 255).rounded()) let green = Int((color.greenComponent * 255).rounded()) @@ -239,6 +240,17 @@ open class SCColorSamplerConfiguration: NSObject { } } + // MARK: - ZOOM + /// SCColorSamplerConfiguration property that specifies if the mouse wheel should be inverted when zooming. It has nothing to do with `event.isDirectionInvertedFromDevice`. It just provides a way to invert the mouse wheel without forcing users to change their system config. + /// + /// - Possible values are: + /// * false (default) + /// * true + open var zoomWheelInverse: Bool { + get { _zoomWheelInverse } + set { _zoomWheelInverse = newValue } + } + /// SCColorSamplerConfiguration property that specifies the possible zoom values. Set to empty array to disable zoom functionality. Set the `defaultZoomValue` property to set the starting zoom value. /// /// Define all the possible zoom values in an array like so: [.s, .m, .l, .xl] From 306d4228176d869af9b45979157bcca3cb2d99aa Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 13 Sep 2024 20:43:40 +0800 Subject: [PATCH 02/12] add noBlock mode to avoid loupe block mouse --- Sources/ColorSampler.swift | 11 ++++++-- Sources/ColorSamplerView.swift | 34 +++++++++++++++++------ Sources/ColorSamplerWindow.swift | 20 +++++++++---- Sources/SCColorSamplerConfiguration.swift | 25 +++++++++++++++++ 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index 9551685..3fa9651 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -40,6 +40,11 @@ internal class ColorSampler: NSObject { return } + // Make window a little bigger than use specified + let loupeSize = configuration.loupeSize.getSize() + let samplerWindowWidth = loupeSize.width + configuration.padding * 2 + let samplerWindowHeight = loupeSize.height + configuration.padding * 2 + var windowInit: ( contentRect: NSRect, styleMask: NSWindow.StyleMask, @@ -47,7 +52,7 @@ internal class ColorSampler: NSObject { defer: Bool ) { return ( - NSRect.init(origin: .zero, size: configuration.loupeSize.getSize()), + NSRect.init(origin: .zero, size: CGSize(width: samplerWindowWidth, height: samplerWindowHeight)), NSWindow.StyleMask.borderless, NSWindow.BackingStoreType.buffered, true @@ -83,7 +88,9 @@ internal class ColorSampler: NSObject { // prepare image for window's contentView in advance self.colorSamplerWindow?.mouseMoved(with: NSEvent()) - NSCursor.hide() + if self.configuration?.loupeFollowMode == .center { + NSCursor.hide() + } } func reset() { diff --git a/Sources/ColorSamplerView.swift b/Sources/ColorSamplerView.swift index b9da2b6..7fc3188 100644 --- a/Sources/ColorSamplerView.swift +++ b/Sources/ColorSamplerView.swift @@ -17,19 +17,23 @@ internal class ColorSamplerView: NSView { var quality: SCColorSamplerConfiguration.Quality! var shape: SCColorSamplerConfiguration.LoupeShape! + var padding: Double! + init( frame frameRect: NSRect, zoom: Binding, image: Binding, loupeColor: Binding, shape: SCColorSamplerConfiguration.LoupeShape, - quality: SCColorSamplerConfiguration.Quality + quality: SCColorSamplerConfiguration.Quality, + padding: Double ) { self.zoom = zoom self.image = image self.quality = quality self.loupeColor = loupeColor self.shape = shape + self.padding = padding super.init( frame: frameRect ) @@ -54,11 +58,18 @@ internal class ColorSamplerView: NSView { let rect = self.bounds - let width: CGFloat = rect.width - let height: CGFloat = rect.height + // Invisible window for debug + // context.setLineWidth(4.0) + // context.setStrokeColor(CGColor(red: 0, green: 0, blue: 255, alpha: 1)) + // context.addPath(shape.path(in: rect)) + // context.strokePath() + // User specified region + let paddedRectOrigin = CGPoint(x: rect.origin.x + padding, y: rect.origin.y + padding) + let paddedRect = CGRect(origin: paddedRectOrigin, size: CGSize(width: rect.size.width - padding * 2, height: rect.size.height - padding * 2)) + // mask - let path = shape.path(in: rect) + let path = shape.path(in: paddedRect) context.addPath(path) context.clip() @@ -68,6 +79,9 @@ internal class ColorSamplerView: NSView { } // draw image + let width: CGFloat = rect.width + let height: CGFloat = rect.height + context.setRenderingIntent(.relativeColorimetric) context.interpolationQuality = .none context.draw(image, in: rect) @@ -85,15 +99,15 @@ internal class ColorSamplerView: NSView { let squareSize = zoom.getSquarePatternSize() let squareDisplacement = zoom.getSquarePatternDisplacement() square.borderWidth = 0.5 - square.borderColor = .black.copy(alpha: 0.15) + square.borderColor = .black.copy(alpha: 0.05) square.frame = CGRect(x: x - (squareSize * 25), y: y - (squareSize * 25), width: squareSize, height: squareSize) - let instanceCount = 50 - - replicatorLayer.instanceCount = instanceCount + let instanceCount: Double = 50 + + replicatorLayer.instanceCount = Int(instanceCount) replicatorLayer.instanceTransform = CATransform3DMakeTranslation(squareSize, squareDisplacement, 0) replicatorLayer.addSublayer(square) @@ -102,7 +116,7 @@ internal class ColorSamplerView: NSView { outerReplicatorLayer.addSublayer(replicatorLayer) - outerReplicatorLayer.instanceCount = instanceCount + outerReplicatorLayer.instanceCount = Int(instanceCount) outerReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(squareDisplacement, squareSize, 0) outerReplicatorLayer.render(in: context) @@ -111,6 +125,7 @@ internal class ColorSamplerView: NSView { let apertureRect = CGRect(x: x, y: y, width: apertureSize, height: apertureSize) context.setLineWidth(zoom.getApertureLineWidth()) context.setStrokeColor(loupeColor.wrappedValue.cgColor) + //context.setStrokeColor(CGColor(red: 255, green: 0, blue: 0, alpha: 1)) context.setShouldAntialias(false) context.stroke(apertureRect.insetBy(dx: zoom.getInsetAmount(), dy: zoom.getInsetAmount())) @@ -118,6 +133,7 @@ internal class ColorSamplerView: NSView { context.setShouldAntialias(true) context.setLineWidth(4.0) context.setStrokeColor(loupeColor.wrappedValue.cgColor) + //context.setStrokeColor(CGColor(red: 0, green: 255, blue: 0, alpha: 1)) context.addPath(path) context.strokePath() } diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index 99a9de2..81a473f 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -133,6 +133,18 @@ internal class ColorSamplerWindow: NSWindow { } } + private func getWindowOriginPoint(_ position: NSPoint) -> NSPoint { + let config = unwrappedDelegate.config + switch config.loupeFollowMode { + case .center: + return .init(x: position.x - (self.frame.size.width / 2), y: position.y - (self.frame.size.height / 2)) + case .noBlock: + return .init( + x: position.x - config.padding + config.loupeFollowDistance, + y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance + ) + } + } // Override NSWindow methods override open func mouseMoved(with event: NSEvent) { let position = NSEvent.mouseLocation @@ -148,10 +160,7 @@ internal class ColorSamplerWindow: NSWindow { self.activeDisplay = screenWithMouse } - let origin: NSPoint = .init( - x: position.x - (self.frame.size.width / 2), - y: position.y - (self.frame.size.height / 2) - ) + let origin: NSPoint = getWindowOriginPoint(position) self.setFrameOrigin(origin) if let image = croppedImageBinding.wrappedValue, @@ -272,7 +281,8 @@ extension ColorSamplerWindow { image: croppedImageBinding, loupeColor: loupeColorBinding, shape: delegate.config.loupeShape, - quality: delegate.config.quality + quality: delegate.config.quality, + padding: delegate.config.loupeFollowMode == .noBlock ? delegate.config.padding : 0 ) self.contentView = contentView if unwrappedDelegate.config.showColorDescription { diff --git a/Sources/SCColorSamplerConfiguration.swift b/Sources/SCColorSamplerConfiguration.swift index 5ebe324..da85729 100644 --- a/Sources/SCColorSamplerConfiguration.swift +++ b/Sources/SCColorSamplerConfiguration.swift @@ -34,6 +34,8 @@ open class SCColorSamplerConfiguration: NSObject { return String(format: "%02x%02x%02x%02x", red, green, blue, alpha).uppercased() } } + private var _loupeFollowMode: LoupeFollowMode = .center + private var _loupeFollowDistance: Double = 10 // MARK: - Loupe shape public enum LoupeShape { @@ -57,6 +59,26 @@ open class SCColorSamplerConfiguration: NSObject { } } + // MARK: - Loupe follow + public enum LoupeFollowMode { + case center + case noBlock + } + + /// SCColorSamplerConfiguration property that specifies the distance from the loupe to the mouse. + /// + open var loupeFollowMode: LoupeFollowMode { get { _loupeFollowMode } set { _loupeFollowMode = newValue } } + + /// SCColorSamplerConfiguration property that specifies the color sampler loupe shape. + /// + /// It should be set to the approximate mouse size. + open var loupeFollowDistance: Double { get { _loupeFollowDistance } set { _loupeFollowDistance = newValue } } + + /// SCColorSamplerConfiguration property that specifies the invisible padding, used to initialize an invisible window to listen on mouse event + /// + /// It should be a little greater than `loupeFollowDistance` + var padding: Double { get { _loupeFollowDistance + 5 } } + /// SCColorSamplerConfiguration property that specifies the color sampler loupe shape. /// /// - Possible values are: @@ -71,6 +93,7 @@ open class SCColorSamplerConfiguration: NSObject { case medium case large case custom(CGFloat) + case recOnly(CGFloat, CGFloat) internal func getSize() -> CGSize { switch self { @@ -82,6 +105,8 @@ open class SCColorSamplerConfiguration: NSObject { return .init(width: 160, height: 160) case .custom(let value): return .init(width: value, height: value) + case .recOnly(let width, let height): + return .init(width: width, height: height) } } } From 7d4f433e69106ca911a3975c9542ab7960da78fb Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 13 Sep 2024 20:52:27 +0800 Subject: [PATCH 03/12] update readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 48675c3..7cbf446 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,18 @@ SCColorSampler.sample(configuration: configuration) { ... } configuration.zoomWheelInverse = true ``` +#### Change Loupe Location to avoid sight blocking +```swift +// For example: +configuration.loupeShape = .rect +configuration.loupeFollowDistance = 10 +configuration.loupeSize = .recOnly(256, 64) + +//configuration.loupeShape = .circle +//configuration.loupeFollowDistance = 1 +//configuration.loupeSize = .large +``` + #### Show Color Description

Image showing showColorDescription off option From b8108e2d5cde70016f0aec1d84b0e59ba8adfb31 Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 20 Sep 2024 21:41:41 +0800 Subject: [PATCH 04/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=93=E6=88=AA?= =?UTF-8?q?=E5=8F=96=E5=8C=BA=E5=9F=9F=E4=B8=BA=E7=9F=A9=E5=BD=A2=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=EF=BC=8C=E5=9B=BE=E5=83=8F=E5=8F=91=E7=94=9F?= =?UTF-8?q?=E6=8B=89=E4=BC=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSamplerWindow.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index 81a473f..9cd4846 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -404,14 +404,17 @@ internal extension ColorSamplerWindow { if captureSize.truncatingRemainder(dividingBy: 2) != 0 { captureSize += 1 } + let loupeSize = delegate.config.loupeSize.getSize() + let captureSizeY = captureSize * loupeSize.height / loupeSize.width + let x = (position.x - display.frame.origin.x) * delegate.config.quality.getMultiplier() let y = (display.frame.height - (position.y - display.frame.origin.y)) * delegate.config.quality.getMultiplier() let captureRect = NSRect( x: x - (captureSize / 2), - y: y - (captureSize / 2), + y: y - (captureSizeY / 2), width: captureSize, - height: captureSize + height: captureSizeY ) guard let croppedImage = image.cropping(to: captureRect) else { From b79bf718b03a535da0281bb6e9c18b7511bd0470 Mon Sep 17 00:00:00 2001 From: tb Date: Sat, 21 Sep 2024 00:04:31 +0800 Subject: [PATCH 05/12] now the sampler window can dodge the edge of displays, including multiple displays --- Sources/ColorSamplerWindow.swift | 41 +++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index 9cd4846..c632b5e 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -133,22 +133,51 @@ internal class ColorSamplerWindow: NSWindow { } } - private func getWindowOriginPoint(_ position: NSPoint) -> NSPoint { + // Get origin point(zero point) of the rectangle area(loupe) + private func getWindowOriginPoint(_ position: NSPoint, _ display: NSScreen) -> NSPoint { + let displayOrigin = display.frame.origin + // should minus display origin point for multiple displays + let position = NSPoint(x: position.x - displayOrigin.x, y: position.y - displayOrigin.y) let config = unwrappedDelegate.config + let safeAreaDistance: CGFloat = 100 + + switch config.loupeFollowMode { case .center: return .init(x: position.x - (self.frame.size.width / 2), y: position.y - (self.frame.size.height / 2)) case .noBlock: + // Need dodge when mouse reach edge of screen, especially bottom and right edge + var origin: NSPoint + if position.x + self.frame.size.width >= display.frame.width - safeAreaDistance { + // right + origin = .init( + x: position.x - self.frame.size.width + config.padding, + y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance + ) + } else if position.y - self.frame.size.height <= safeAreaDistance { + // bottom + origin = .init( + x: position.x - config.padding, + y: position.y - config.padding + ) + } else { + // top and left + origin = .init( + x: position.x - config.padding + config.loupeFollowDistance, + y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance + ) + } + // should add origin back cause we want an absolute value caculated base on (0,0) return .init( - x: position.x - config.padding + config.loupeFollowDistance, - y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance + x: origin.x + displayOrigin.x, + y: origin.y + displayOrigin.y ) } } // Override NSWindow methods override open func mouseMoved(with event: NSEvent) { let position = NSEvent.mouseLocation - + print(position) guard let screenWithMouse = NSScreen.screens.first( where: { NSMouseInRect(position, $0.frame, false) } ) @@ -159,8 +188,8 @@ internal class ColorSamplerWindow: NSWindow { if self.activeDisplay != screenWithMouse { self.activeDisplay = screenWithMouse } - - let origin: NSPoint = getWindowOriginPoint(position) + + let origin: NSPoint = getWindowOriginPoint(position, screenWithMouse) self.setFrameOrigin(origin) if let image = croppedImageBinding.wrappedValue, From 51f4511204b6575f3d285e6e47745b631d673281 Mon Sep 17 00:00:00 2001 From: tb Date: Sat, 21 Sep 2024 00:06:20 +0800 Subject: [PATCH 06/12] remove debug statement --- Sources/ColorSamplerWindow.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index c632b5e..fdce311 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -177,7 +177,6 @@ internal class ColorSamplerWindow: NSWindow { // Override NSWindow methods override open func mouseMoved(with event: NSEvent) { let position = NSEvent.mouseLocation - print(position) guard let screenWithMouse = NSScreen.screens.first( where: { NSMouseInRect(position, $0.frame, false) } ) From 4827c99d1598a6f60e35f54ee1b5ce4aa4847123 Mon Sep 17 00:00:00 2001 From: tb Date: Thu, 10 Oct 2024 22:38:39 +0800 Subject: [PATCH 07/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=93=E5=89=8D?= =?UTF-8?q?=E5=8F=96=E8=89=B2=E5=99=A8=E6=98=AFkey=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=E5=8F=96=E8=89=B2=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSampler.swift | 94 ++++++++++++++++++++++++++++++-- Sources/ColorSamplerWindow.swift | 33 ++++++++--- 2 files changed, 116 insertions(+), 11 deletions(-) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index 3fa9651..cf275cb 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -10,6 +10,7 @@ import Combine import Foundation import ScreenCaptureKit import struct SwiftUI.Binding +import Carbon.HIToolbox internal class ColorSampler: NSObject { // Properties @@ -20,6 +21,8 @@ internal class ColorSampler: NSObject { var onMouseMovedHandlerBlock: ((NSColor) -> Void)? var selectionHandlerBlock: ((NSColor?) -> Void)? + var monitors: [Any?] = [] + var isRunning: Bool = false; // Functions func sample( @@ -80,14 +83,15 @@ internal class ColorSampler: NSObject { name: NSWindow.didResignKeyNotification, object: self.colorSamplerWindow ) - - NSApplication.shared.activate(ignoringOtherApps: true) - self.colorSamplerWindow?.makeKeyAndOrderFront(self) + addMouseMonitor() + // 这里有问题,激活放大镜后,其它程序都变灰色了,取色就不对了 +// NSApplication.shared.activate(ignoringOtherApps: false) + self.colorSamplerWindow?.orderFront(self) // 不能变成 key self.colorSamplerWindow?.orderedIndex = 0 - // prepare image for window's contentView in advance self.colorSamplerWindow?.mouseMoved(with: NSEvent()) + self.isRunning = true if self.configuration?.loupeFollowMode == .center { NSCursor.hide() } @@ -106,4 +110,86 @@ internal class ColorSampler: NSObject { self.onMouseMovedHandlerBlock = nil self.selectionHandlerBlock = nil } + + func colorSelected() { + self.isRunning = false + self.colorSamplerWindow?.finalizeColor() + self.removeMonitors() + } + + func cancel() { + self.isRunning = false + self.colorSamplerWindow?.cancel() + self.removeMonitors() + } + + func addMouseMonitor() { + + // 鼠标移动过快导致窗口跟不上,需要全局事件监听来辅助 + let global_mouseMoved = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { e in + self.colorSamplerWindow?.mouseMoved(with: e) + } + monitors.append(global_mouseMoved) + + // 该事件只监听除了自身以外的程序,用于在鼠标按下捕获颜色 + let global_mouse_down = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown) { e in + guard self.isRunning else { + return + } + self.colorSelected() + } + monitors.append(global_mouse_down) + + // 用于在按下ESC关闭取色窗口,回车取色 + let global_key = NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { e in + guard self.isRunning else { + return + } + if e.keyCode == kVK_Escape { + self.cancel() + } + if e.keyCode == kVK_Return { + self.colorSelected() + } + } + monitors.append(global_key) + // 鼠标左键取色 + let local_mouse = NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { e in + guard self.isRunning else { + return e + } + self.colorSelected() + return e + } + monitors.append(local_mouse) + + // 用于在按下ESC关闭取色窗口,回车取色 + let local_key = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { e in + guard self.isRunning else { + return e + } + if e.keyCode == kVK_Escape { + self.cancel() + } + if e.keyCode == kVK_Return { + self.colorSelected() + } + return e + } + monitors.append(local_key) + } + + func removeMonitors() { + print("removing monitors") + for i in 0 ..< self.monitors.count { + if let m = self.monitors[i] { + do { + NSEvent.removeMonitor(m) + } catch { + } + } + } + // 防止出现 Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT) + self.monitors = [] + } } diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index fdce311..c9357db 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -66,6 +66,7 @@ internal class ColorSamplerWindow: NSWindow { self.level = .screenSaver self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] self.ignoresMouseEvents = false + self.acceptsMouseMovedEvents = true // Start stream Task { await startStream() @@ -175,6 +176,7 @@ internal class ColorSamplerWindow: NSWindow { } } // Override NSWindow methods + // 这个方法需要采样窗口一直是key,但是这样其它窗口就会失去焦点,颜色会变,因此不能再用了 override open func mouseMoved(with event: NSEvent) { let position = NSEvent.mouseLocation guard let screenWithMouse = NSScreen.screens.first( @@ -211,7 +213,16 @@ internal class ColorSamplerWindow: NSWindow { super.mouseMoved(with: event) } - override open func mouseDown(with event: NSEvent) { +// override open func mouseDown(with event: NSEvent) { +// if let color = self.croppedImageBinding.wrappedValue?.colorAtCenter(), +// let delegate = self.delegate as? ColorSamplerDelegate { +// delegate.callSelectionHandler(color: color) +// } +// self.orderOut(self) +// } +// + func finalizeColor() { + print("finalize color down") if let color = self.croppedImageBinding.wrappedValue?.colorAtCenter(), let delegate = self.delegate as? ColorSamplerDelegate { delegate.callSelectionHandler(color: color) @@ -267,14 +278,22 @@ internal class ColorSamplerWindow: NSWindow { super.scrollWheel(with: event) } - override func keyDown(with event: NSEvent) { - if event.keyCode == kVK_Escape { - if let delegate = self.delegate as? ColorSamplerDelegate { - delegate.callSelectionHandler(color: nil) - } - self.orderOut(self) + func cancel() { + if let delegate = self.delegate as? ColorSamplerDelegate { + delegate.callSelectionHandler(color: nil) } + self.orderOut(self) } + + // 取消置顶后,这里的keydonw就不能用了 +// override func keyDown(with event: NSEvent) { +// if event.keyCode == kVK_Escape { +// if let delegate = self.delegate as? ColorSamplerDelegate { +// delegate.callSelectionHandler(color: nil) +// } +// self.orderOut(self) +// } +// } } extension ColorSamplerWindow { From f13acd3fd753763b85aa390b7bdd87cf83095a37 Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 11 Oct 2024 09:37:03 +0800 Subject: [PATCH 08/12] Add mutilple screen support to .center mode --- Sources/ColorSampler.swift | 2 ++ Sources/ColorSamplerWindow.swift | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index cf275cb..f401ac9 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -115,12 +115,14 @@ internal class ColorSampler: NSObject { self.isRunning = false self.colorSamplerWindow?.finalizeColor() self.removeMonitors() + self.reset() } func cancel() { self.isRunning = false self.colorSamplerWindow?.cancel() self.removeMonitors() + self.reset() } func addMouseMonitor() { diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index c9357db..bc0dd2d 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -142,10 +142,11 @@ internal class ColorSamplerWindow: NSWindow { let config = unwrappedDelegate.config let safeAreaDistance: CGFloat = 100 + var origin: NSPoint = .zero switch config.loupeFollowMode { case .center: - return .init(x: position.x - (self.frame.size.width / 2), y: position.y - (self.frame.size.height / 2)) + origin = .init(x: position.x - (self.frame.size.width / 2), y: position.y - (self.frame.size.height / 2)) case .noBlock: // Need dodge when mouse reach edge of screen, especially bottom and right edge var origin: NSPoint @@ -168,12 +169,12 @@ internal class ColorSamplerWindow: NSWindow { y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance ) } - // should add origin back cause we want an absolute value caculated base on (0,0) - return .init( - x: origin.x + displayOrigin.x, - y: origin.y + displayOrigin.y - ) } + // should add origin back cause we want an absolute value caculated base on (0,0) + return .init( + x: origin.x + displayOrigin.x, + y: origin.y + displayOrigin.y + ) } // Override NSWindow methods // 这个方法需要采样窗口一直是key,但是这样其它窗口就会失去焦点,颜色会变,因此不能再用了 From a25a7b3247dfba6ca65bcf97eb5c7fed63c06205 Mon Sep 17 00:00:00 2001 From: tb Date: Fri, 11 Oct 2024 13:31:31 +0800 Subject: [PATCH 09/12] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=E5=8F=96=E8=89=B2?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E8=8E=B7=E5=BE=97=E7=84=A6=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E5=85=B6=E5=AE=83=E7=AA=97=E5=8F=A3=E5=A4=B1=E5=8E=BB=E7=84=A6?= =?UTF-8?q?=E7=82=B9=EF=BC=8C=E5=AF=BC=E8=87=B4=E9=A2=9C=E8=89=B2=E4=B8=8D?= =?UTF-8?q?=E5=AF=B9=E7=9A=84=E9=97=AE=E9=A2=98=202.=20=E9=A1=BA=E5=B8=A6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=96=E8=89=B2=E7=AA=97=E5=8F=A3=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E8=84=B1=E7=A6=BB=E9=BC=A0=E6=A0=87=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E5=AE=9E=E6=97=B6=E9=A2=9C=E8=89=B2=E6=A1=86=E5=8F=96?= =?UTF-8?q?=E8=89=B2=E4=B8=8D=E5=AF=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSampler.swift | 13 ++-- Sources/ColorSamplerView.swift | 79 +++++++++++++++-------- Sources/ColorSamplerWindow.swift | 36 ++++++----- Sources/SCColorSamplerConfiguration.swift | 2 +- 4 files changed, 80 insertions(+), 50 deletions(-) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index f401ac9..f3c78f5 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -84,8 +84,8 @@ internal class ColorSampler: NSObject { object: self.colorSamplerWindow ) addMouseMonitor() - // 这里有问题,激活放大镜后,其它程序都变灰色了,取色就不对了 -// NSApplication.shared.activate(ignoringOtherApps: false) + // 这里有问题,激活放大镜后,其它程序都变灰色了,取色就不对了(已修复) + // NSApplication.shared.activate(ignoringOtherApps: false) self.colorSamplerWindow?.orderFront(self) // 不能变成 key self.colorSamplerWindow?.orderedIndex = 0 // prepare image for window's contentView in advance @@ -127,11 +127,13 @@ internal class ColorSampler: NSObject { func addMouseMonitor() { - // 鼠标移动过快导致窗口跟不上,需要全局事件监听来辅助 - let global_mouseMoved = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { e in + // 假如鼠标移动过快导致窗口跟不上,需要此函数来找回监听 + // 目前已经将隐形窗口放大(SCColorSamplerConfiguration.padding),这种情况应该很少出现了,如果出现,就需要这里发挥作用 + let local_mouseExited = NSEvent.addLocalMonitorForEvents(matching: .mouseExited) { e in self.colorSamplerWindow?.mouseMoved(with: e) + return e } - monitors.append(global_mouseMoved) + monitors.append(local_mouseExited) // 该事件只监听除了自身以外的程序,用于在鼠标按下捕获颜色 let global_mouse_down = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown) { e in @@ -182,7 +184,6 @@ internal class ColorSampler: NSObject { } func removeMonitors() { - print("removing monitors") for i in 0 ..< self.monitors.count { if let m = self.monitors[i] { do { diff --git a/Sources/ColorSamplerView.swift b/Sources/ColorSamplerView.swift index 7fc3188..353803d 100644 --- a/Sources/ColorSamplerView.swift +++ b/Sources/ColorSamplerView.swift @@ -14,31 +14,34 @@ internal class ColorSamplerView: NSView { var image: Binding! var loupeColor: Binding! - var quality: SCColorSamplerConfiguration.Quality! - var shape: SCColorSamplerConfiguration.LoupeShape! - - var padding: Double! + var config: SCColorSamplerConfiguration! + var frameRect: NSRect! init( frame frameRect: NSRect, zoom: Binding, image: Binding, loupeColor: Binding, - shape: SCColorSamplerConfiguration.LoupeShape, - quality: SCColorSamplerConfiguration.Quality, - padding: Double + config: SCColorSamplerConfiguration ) { self.zoom = zoom self.image = image - self.quality = quality self.loupeColor = loupeColor - self.shape = shape - self.padding = padding + self.config = config + self.frameRect = frameRect super.init( frame: frameRect ) } + private func getUserViewFrame() -> NSRect { + let windowFrame = self.window!.frame + var size: CGSize = config.loupeSize.getSize() + let originOffset = config.loupeFollowMode == .noBlock ? config.padding : 0 + var origin: CGPoint = .init(x: self.frameRect.origin.x + config.padding, y: self.frameRect.origin.y + originOffset) + return .init(origin: origin, size: size) + } + required init?(coder: NSCoder) { super.init(coder: coder) } @@ -52,24 +55,43 @@ internal class ColorSamplerView: NSView { // Weird ?? fatalError() } - - // Clear the drawing rect. - context.clear(self.bounds) - - let rect = self.bounds - - // Invisible window for debug - // context.setLineWidth(4.0) - // context.setStrokeColor(CGColor(red: 0, green: 0, blue: 255, alpha: 1)) - // context.addPath(shape.path(in: rect)) - // context.strokePath() + let quality = config.quality + let shape = config.loupeShape + let windowRect: NSRect = window!.frame // User specified region - let paddedRectOrigin = CGPoint(x: rect.origin.x + padding, y: rect.origin.y + padding) - let paddedRect = CGRect(origin: paddedRectOrigin, size: CGSize(width: rect.size.width - padding * 2, height: rect.size.height - padding * 2)) - + // 这个 rect 是放大镜的绘画区域,它的坐标系是相对于这个view本身,因此它的原点不是零点,而是(P, P), P=config.padding + let rect: NSRect = .init(origin: .init(x: config.padding, y: config.padding), size: config.loupeSize.getSize()) + + // 以下debug信息非常重要,保留 +// print("window frame \(self.window!.frame.debugDescription)") +// print("view frame \(self.frame.debugDescription)") +// print("draw zone: \(rect.debugDescription)") +// +// // Invisible window for debug +// context.setLineWidth(4.0) +// context.setStrokeColor(CGColor(red: 255, green: 0, blue: 0, alpha: 1)) +// var shape1: SCColorSamplerConfiguration.LoupeShape = .rect +// context.addPath(shape1.path(in: rect)) +// context.strokePath() +// +// // Inviisible bounds window for debug +// context.setLineWidth(4.0) +// context.setStrokeColor(CGColor(red: 0, green: 255, blue: 0, alpha: 1)) +// var shape2: SCColorSamplerConfiguration.LoupeShape = .rect +// context.addPath(shape2.path(in: self.bounds)) +// context.strokePath() +// +// // Inviisible Out window for debug +// context.setLineWidth(4.0) +// context.setStrokeColor(CGColor(red: 0, green: 0, blue: 255, alpha: 1)) +// var shape3: SCColorSamplerConfiguration.LoupeShape = .rect +// context.addPath(shape3.path(in: windowRect)) +// context.strokePath() + // 以上debug信息非常重要,保留 + // mask - let path = shape.path(in: paddedRect) + let path = shape.path(in: rect) context.addPath(path) context.clip() @@ -89,8 +111,9 @@ internal class ColorSamplerView: NSView { // Get dimensions let apertureSize: CGFloat = zoom.getApertureSize() - let x: CGFloat = (width / 2.0) - (apertureSize / 2.0) - let y: CGFloat = (height / 2.0) - (apertureSize / 2.0) + // 孔径位置 + let x: CGFloat = (self.frameRect.width / 2.0) - (apertureSize / 2.0) + let y: CGFloat = (self.frameRect.height / 2.0) - (apertureSize / 2.0) // Square pattern let replicatorLayer = CAReplicatorLayer() @@ -125,7 +148,7 @@ internal class ColorSamplerView: NSView { let apertureRect = CGRect(x: x, y: y, width: apertureSize, height: apertureSize) context.setLineWidth(zoom.getApertureLineWidth()) context.setStrokeColor(loupeColor.wrappedValue.cgColor) - //context.setStrokeColor(CGColor(red: 255, green: 0, blue: 0, alpha: 1)) +// context.setStrokeColor(CGColor(red: 255, green: 0, blue: 0, alpha: 1)) context.setShouldAntialias(false) context.stroke(apertureRect.insetBy(dx: zoom.getInsetAmount(), dy: zoom.getInsetAmount())) diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index bc0dd2d..b6cc1dd 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -62,7 +62,7 @@ internal class ColorSamplerWindow: NSWindow { // NSWindow properties self.delegate = delegate self.isOpaque = false - self.backgroundColor = .clear + self.backgroundColor = .init(red: 1, green: 1, blue: 1, alpha: 0.001) // 让隐形窗口不可见,但是不能透传点击事件到底部 self.level = .screenSaver self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] self.ignoresMouseEvents = false @@ -119,7 +119,7 @@ internal class ColorSamplerWindow: NSWindow { contentRect: .init( origin: .init( x: self.frame.midX - 50, - y: self.frame.minY - 35 + y: self.frame.minY - 35 + delegate.config.padding // 实时颜色的位置根据用户可见区域计算 ), size: .init( width: 100, @@ -134,6 +134,13 @@ internal class ColorSamplerWindow: NSWindow { } } + /// This function get user view frame from calculated window frame. + /// + /// Be cautious this function should only be used after window frame is set by `getWindowOriginPoint` + private func getUserViewSize() -> CGSize { + return unwrappedDelegate.config.loupeSize.getSize() + } + // Get origin point(zero point) of the rectangle area(loupe) private func getWindowOriginPoint(_ position: NSPoint, _ display: NSScreen) -> NSPoint { let displayOrigin = display.frame.origin @@ -143,20 +150,20 @@ internal class ColorSamplerWindow: NSWindow { let safeAreaDistance: CGFloat = 100 var origin: NSPoint = .zero - + // 在隐形窗口之内的用户可见区域 + let size: CGSize = getUserViewSize() + // Need dodge when mouse reach edge of screen, especially bottom and right edge switch config.loupeFollowMode { case .center: - origin = .init(x: position.x - (self.frame.size.width / 2), y: position.y - (self.frame.size.height / 2)) + origin = .init(x: position.x - self.frame.size.width / 2, y: position.y - (self.frame.size.height / 2)) case .noBlock: - // Need dodge when mouse reach edge of screen, especially bottom and right edge - var origin: NSPoint - if position.x + self.frame.size.width >= display.frame.width - safeAreaDistance { + if position.x + size.width >= display.frame.width - safeAreaDistance { // 使用用户可见区域判断 // right origin = .init( x: position.x - self.frame.size.width + config.padding, - y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance + y: position.y - self.frame.size.height + config.padding - config.loupeFollowDistance // 但是使用窗口大小计算,因为计算的不是可见区域的原点,而是外部窗口的原点 ) - } else if position.y - self.frame.size.height <= safeAreaDistance { + } else if position.y - size.height <= safeAreaDistance { // bottom origin = .init( x: position.x - config.padding, @@ -170,6 +177,7 @@ internal class ColorSamplerWindow: NSWindow { ) } } + // should add origin back cause we want an absolute value caculated base on (0,0) return .init( x: origin.x + displayOrigin.x, @@ -223,7 +231,7 @@ internal class ColorSamplerWindow: NSWindow { // } // func finalizeColor() { - print("finalize color down") +// print("finalize color down") if let color = self.croppedImageBinding.wrappedValue?.colorAtCenter(), let delegate = self.delegate as? ColorSamplerDelegate { delegate.callSelectionHandler(color: color) @@ -328,9 +336,7 @@ extension ColorSamplerWindow { zoom: zoomBinding, image: croppedImageBinding, loupeColor: loupeColorBinding, - shape: delegate.config.loupeShape, - quality: delegate.config.quality, - padding: delegate.config.loupeFollowMode == .noBlock ? delegate.config.padding : 0 + config: delegate.config ) self.contentView = contentView if unwrappedDelegate.config.showColorDescription { @@ -342,7 +348,7 @@ extension ColorSamplerWindow { NSRect.init( origin: .init( x: self.frame.midX - newWidth / 2, - y: self.frame.minY - 35 + y: self.frame.minY - 35 + delegate.config.padding ), size: .init( width: newWidth, @@ -446,7 +452,7 @@ internal extension ColorSamplerWindow { var captureSize: CGFloat = round( round( - self.frame.size.width / self.zoom!.getPixelZoom(quality: delegate.config.quality) + delegate.config.loupeSize.getSize().width / self.zoom!.getPixelZoom(quality: delegate.config.quality) ) * delegate.config.quality.getMultiplier() ) diff --git a/Sources/SCColorSamplerConfiguration.swift b/Sources/SCColorSamplerConfiguration.swift index da85729..1381496 100644 --- a/Sources/SCColorSamplerConfiguration.swift +++ b/Sources/SCColorSamplerConfiguration.swift @@ -77,7 +77,7 @@ open class SCColorSamplerConfiguration: NSObject { /// SCColorSamplerConfiguration property that specifies the invisible padding, used to initialize an invisible window to listen on mouse event /// /// It should be a little greater than `loupeFollowDistance` - var padding: Double { get { _loupeFollowDistance + 5 } } + var padding: Double { get { _loupeFollowDistance + 300 } } /// SCColorSamplerConfiguration property that specifies the color sampler loupe shape. /// From 8be9b035a09419683a32431276e0db6885365dcf Mon Sep 17 00:00:00 2001 From: travisbikkle Date: Sun, 13 Oct 2024 21:02:13 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E8=BF=99=E4=B8=80=E6=AE=B5=E4=BB=A3=E7=A0=81=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E6=98=AF=E6=B2=A1=E6=9C=89=E7=94=A8=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSampler.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index f3c78f5..4a9224f 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -127,7 +127,7 @@ internal class ColorSampler: NSObject { func addMouseMonitor() { - // 假如鼠标移动过快导致窗口跟不上,需要此函数来找回监听 + // 假如鼠标移动过快导致窗口跟不上,需要此函数来找回监听。和键盘相关的全局事件需要辅助功能权限 // 目前已经将隐形窗口放大(SCColorSamplerConfiguration.padding),这种情况应该很少出现了,如果出现,就需要这里发挥作用 let local_mouseExited = NSEvent.addLocalMonitorForEvents(matching: .mouseExited) { e in self.colorSamplerWindow?.mouseMoved(with: e) @@ -135,7 +135,7 @@ internal class ColorSampler: NSObject { } monitors.append(local_mouseExited) - // 该事件只监听除了自身以外的程序,用于在鼠标按下捕获颜色 + // 该事件只监听除了自身以外的程序,用于在鼠标按下捕获颜色,和键盘相关的全局事件需要辅助功能权限 let global_mouse_down = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown) { e in guard self.isRunning else { return @@ -144,7 +144,7 @@ internal class ColorSampler: NSObject { } monitors.append(global_mouse_down) - // 用于在按下ESC关闭取色窗口,回车取色 + // 用于在按下ESC关闭取色窗口,回车取色,和键盘相关的全局事件需要辅助功能权限 let global_key = NSEvent.addGlobalMonitorForEvents(matching: .keyDown) { e in guard self.isRunning else { return From 4b5d3f2c40308158c57fd8ef542fb36da7ec9bff Mon Sep 17 00:00:00 2001 From: travisbikkle Date: Sun, 20 Oct 2024 17:01:44 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E7=9B=91=E5=90=AC=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E9=BC=A0=E6=A0=87=E8=84=B1=E7=A6=BB=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSampler.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/ColorSampler.swift b/Sources/ColorSampler.swift index 4a9224f..647f28a 100644 --- a/Sources/ColorSampler.swift +++ b/Sources/ColorSampler.swift @@ -129,6 +129,11 @@ internal class ColorSampler: NSObject { // 假如鼠标移动过快导致窗口跟不上,需要此函数来找回监听。和键盘相关的全局事件需要辅助功能权限 // 目前已经将隐形窗口放大(SCColorSamplerConfiguration.padding),这种情况应该很少出现了,如果出现,就需要这里发挥作用 + let global_mouseMoved = NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { e in + self.colorSamplerWindow?.mouseMoved(with: e) + } + monitors.append(global_mouseMoved) + let local_mouseExited = NSEvent.addLocalMonitorForEvents(matching: .mouseExited) { e in self.colorSamplerWindow?.mouseMoved(with: e) return e From 64bf0097e0d9e8c73024d3b03a23a480261323d1 Mon Sep 17 00:00:00 2001 From: travisbikkle Date: Sun, 20 Oct 2024 19:52:47 +0800 Subject: [PATCH 12/12] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=94=BE=E5=A4=A7?= =?UTF-8?q?=E9=95=9C=E7=A7=BB=E5=8A=A8=E5=88=B0=E5=B1=8F=E5=B9=95=E5=8F=B3?= =?UTF-8?q?=E4=B8=8B=E8=A7=92=E5=8C=BA=E5=9F=9F=E6=97=B6=E7=9A=84=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/ColorSamplerWindow.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/ColorSamplerWindow.swift b/Sources/ColorSamplerWindow.swift index b6cc1dd..4163fbe 100644 --- a/Sources/ColorSamplerWindow.swift +++ b/Sources/ColorSamplerWindow.swift @@ -147,7 +147,7 @@ internal class ColorSamplerWindow: NSWindow { // should minus display origin point for multiple displays let position = NSPoint(x: position.x - displayOrigin.x, y: position.y - displayOrigin.y) let config = unwrappedDelegate.config - let safeAreaDistance: CGFloat = 100 + let safeAreaDistance: CGFloat = 10 var origin: NSPoint = .zero // 在隐形窗口之内的用户可见区域 @@ -157,7 +157,13 @@ internal class ColorSamplerWindow: NSWindow { case .center: origin = .init(x: position.x - self.frame.size.width / 2, y: position.y - (self.frame.size.height / 2)) case .noBlock: - if position.x + size.width >= display.frame.width - safeAreaDistance { // 使用用户可见区域判断 + if position.x + size.width >= display.frame.width - safeAreaDistance && position.y - size.height <= safeAreaDistance { + // right and bottom + origin = .init( + x: position.x - self.frame.size.width + config.padding, + y: position.y - config.padding + ) + } else if position.x + size.width >= display.frame.width - safeAreaDistance { // 使用用户可见区域判断 // right origin = .init( x: position.x - self.frame.size.width + config.padding,