Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
A81B98182BDC854F005FD78C /* AboutConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81B98172BDC854F005FD78C /* AboutConfiguration.swift */; };
A81D8D0A2C068B8700188E12 /* LuminarePreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81D8D092C068B8700188E12 /* LuminarePreviewView.swift */; };
A81D8D0C2C06950000188E12 /* LuminareManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81D8D0B2C06950000188E12 /* LuminareManager.swift */; };
A82521EE29E235AC00139654 /* PermissionsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82521ED29E235AC00139654 /* PermissionsManager.swift */; };
A82521EE29E235AC00139654 /* AccessibilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82521ED29E235AC00139654 /* AccessibilityManager.swift */; };
A82740982AB00FCE00B9BDC5 /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82740972AB00FCE00B9BDC5 /* Color+Extensions.swift */; };
A827409A2AB0208500B9BDC5 /* TriggerKeycorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82740992AB0208500B9BDC5 /* TriggerKeycorder.swift */; };
A82B1AEE2BD352A100E2F3F9 /* AccentColorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A82B1AED2BD352A100E2F3F9 /* AccentColorConfiguration.swift */; };
Expand Down Expand Up @@ -110,7 +110,7 @@
A81B98172BDC854F005FD78C /* AboutConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutConfiguration.swift; sourceTree = "<group>"; };
A81D8D092C068B8700188E12 /* LuminarePreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LuminarePreviewView.swift; sourceTree = "<group>"; };
A81D8D0B2C06950000188E12 /* LuminareManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LuminareManager.swift; sourceTree = "<group>"; };
A82521ED29E235AC00139654 /* PermissionsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsManager.swift; sourceTree = "<group>"; };
A82521ED29E235AC00139654 /* AccessibilityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityManager.swift; sourceTree = "<group>"; };
A82740972AB00FCE00B9BDC5 /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = "<group>"; };
A82740992AB0208500B9BDC5 /* TriggerKeycorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TriggerKeycorder.swift; sourceTree = "<group>"; };
A82B1AED2BD352A100E2F3F9 /* AccentColorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentColorConfiguration.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -265,7 +265,7 @@
A87DDD142B50A6A400A32C76 /* ScreenManager.swift */,
A8A2ABE62A3FB0370067B5A9 /* KeybindMonitor.swift */,
A864F4672AA660CD00579738 /* WindowDragManager.swift */,
A82521ED29E235AC00139654 /* PermissionsManager.swift */,
A82521ED29E235AC00139654 /* AccessibilityManager.swift */,
A8EF1F08299C87DF00633440 /* IconManager.swift */,
A869C1A02B38C6E600AD1A84 /* SystemWindowManager.swift */,
);
Expand Down Expand Up @@ -635,7 +635,7 @@
4C6B93E82C1DCF6E00AFF832 /* Updater.swift in Sources */,
A8330ACF2A3AC1E900673C8D /* View+Extensions.swift in Sources */,
A8427E662C02594E00F20759 /* ExcludedAppsConfiguration.swift in Sources */,
A82521EE29E235AC00139654 /* PermissionsManager.swift in Sources */,
A82521EE29E235AC00139654 /* AccessibilityManager.swift in Sources */,
A8E59C4A297F98670064D4BA /* RadialMenuController.swift in Sources */,
0AFE802E2BB98E81009CF06F /* WindowDirection+LocalizedString.swift in Sources */,
A8A583B82BE5A117005F4CB2 /* CycleActionConfigurationView.swift in Sources */,
Expand Down
33 changes: 29 additions & 4 deletions Loop/Extensions/Color+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ extension NSColor {
return 0.299 * rgbColor.redComponent + 0.587 * rgbColor.greenComponent + 0.114 * rgbColor.blueComponent
}

/// Returns the saturation component of the color.
/// Higher values indicate more vibrant colors.
var saturationComponent: CGFloat {
guard let hsbColor = usingColorSpace(.deviceRGB) else { return 0 }
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
hsbColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return saturation
}

/// Determines if two colors are similar based on a threshold.
/// - Parameters:
/// - color: The color to compare with the receiver.
Expand All @@ -71,10 +83,23 @@ extension NSColor {
// Convert both colors to the RGB color space for comparison.
guard let color1 = usingColorSpace(.deviceRGB),
let color2 = color.usingColorSpace(.deviceRGB) else { return false }
// Compare the red, green, and blue components of both colors.
return abs(color1.redComponent - color2.redComponent) < threshold &&
abs(color1.greenComponent - color2.greenComponent) < threshold &&
abs(color1.blueComponent - color2.blueComponent) < threshold

// Calculate difference in HSB space for better perceptual comparison
var hue1: CGFloat = 0, sat1: CGFloat = 0, bri1: CGFloat = 0, alpha1: CGFloat = 0
var hue2: CGFloat = 0, sat2: CGFloat = 0, bri2: CGFloat = 0, alpha2: CGFloat = 0

color1.getHue(&hue1, saturation: &sat1, brightness: &bri1, alpha: &alpha1)
color2.getHue(&hue2, saturation: &sat2, brightness: &bri2, alpha: &alpha2)

// Hue is circular, so we need to account for colors like red (0) and purplish-red (0.95)
let hueDiff = min(abs(hue1 - hue2), 1 - abs(hue1 - hue2))

// For low saturation colors, hue matters less
let hueThreshold = threshold * (1 + (1 - min(sat1, sat2)))

return hueDiff < hueThreshold &&
abs(sat1 - sat2) < threshold * 1.5 &&
abs(bri1 - bri2) < threshold * 1.5
}

/// Quantizes the color to a limited set of values.
Expand Down
4 changes: 4 additions & 0 deletions Loop/Extensions/Defaults+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ extension Defaults.Keys {
static let radialMenuVisibility = Key<Bool>("radialMenuVisibility", default: true, iCloud: true)
static let radialMenuCornerRadius = Key<CGFloat>("radialMenuCornerRadius", default: 50, iCloud: true)
static let radialMenuThickness = Key<CGFloat>("radialMenuThickness", default: 22, iCloud: true)
/// Lock radial menu to the center of the screen
/// Adjust with `defaults write com.MrKai77.Loop lockRadialMenuToCenter -bool true`
/// Reset with `defaults delete com.MrKai77.Loop lockRadialMenuToCenter`
static let lockRadialMenuToCenter = Key<Bool>("lockRadialMenuToCenter", default: false, iCloud: true)

// Preview
static let previewVisibility = Key<Bool>("previewVisibility", default: true, iCloud: true)
Expand Down
Loading