| | |
| | | |
| | | // MARK: - Tag Color Mapping |
| | | |
| | | struct TagCustomColor: Codable, Equatable { |
| | | var red: Double |
| | | var green: Double |
| | | var blue: Double |
| | | var alpha: Double |
| | | |
| | | init(red: Double, green: Double, blue: Double, alpha: Double = 1.0) { |
| | | self.red = Self.clamp(red) |
| | | self.green = Self.clamp(green) |
| | | self.blue = Self.clamp(blue) |
| | | self.alpha = Self.clamp(alpha) |
| | | } |
| | | |
| | | init(nsColor: NSColor) { |
| | | let rgbColor = nsColor.usingColorSpace(.sRGB) ?? nsColor |
| | | self.init( |
| | | red: Double(rgbColor.redComponent), |
| | | green: Double(rgbColor.greenComponent), |
| | | blue: Double(rgbColor.blueComponent), |
| | | alpha: Double(rgbColor.alphaComponent) |
| | | ) |
| | | } |
| | | |
| | | var nsColor: NSColor { |
| | | NSColor( |
| | | srgbRed: CGFloat(red), |
| | | green: CGFloat(green), |
| | | blue: CGFloat(blue), |
| | | alpha: CGFloat(alpha) |
| | | ) |
| | | } |
| | | |
| | | var signature: String { |
| | | [ |
| | | String(format: "%.4f", red), |
| | | String(format: "%.4f", green), |
| | | String(format: "%.4f", blue), |
| | | String(format: "%.4f", alpha) |
| | | ].joined(separator: ":") |
| | | } |
| | | |
| | | private static func clamp(_ value: Double) -> Double { |
| | | min(1.0, max(0.0, value)) |
| | | } |
| | | } |
| | | |
| | | enum TagColor { |
| | | static func nsColor(for index: Int) -> NSColor { |
| | | switch index { |
| | |
| | | default: return NSColor.systemGray |
| | | } |
| | | } |
| | | |
| | | static func nsColor(for index: Int, customColor: TagCustomColor?) -> NSColor { |
| | | customColor?.nsColor ?? nsColor(for: index) |
| | | } |
| | | |
| | | static func prefersDarkText(for color: NSColor) -> Bool { |
| | | let rgbColor = color.usingColorSpace(.sRGB) ?? color |
| | | let red = Double(rgbColor.redComponent) |
| | | let green = Double(rgbColor.greenComponent) |
| | | let blue = Double(rgbColor.blueComponent) |
| | | let luminance = (0.299 * red) + (0.587 * green) + (0.114 * blue) |
| | | return luminance > 0.62 |
| | | } |
| | | |
| | | static let allIndices: [Int] = [1, 2, 3, 4, 5, 6, 7] |
| | | } |
| | | |
| | |
| | | struct TagDef: Codable, Equatable { |
| | | var color: Int |
| | | var systemCategoryID: SmartCategoryID? = nil |
| | | var customColor: TagCustomColor? = nil |
| | | } |
| | | |
| | | struct Store: Codable { |
| | |
| | | let previousStore = store |
| | | var tagDef = store.tags[tag] ?? TagDatabase.TagDef(color: color, systemCategoryID: nil) |
| | | tagDef.color = color |
| | | tagDef.customColor = nil |
| | | store.tags[tag] = tagDef |
| | | TagDatabase.saveUserCategorySchemeMutation( |
| | | store, |
| | |
| | | reason: "set-tag-color" |
| | | ) |
| | | } |
| | | |
| | | /// Set a Pro custom color for a tag without changing its fallback base color. |
| | | @discardableResult |
| | | static func setCustomColor(_ customColor: TagCustomColor, for tag: String) -> Bool { |
| | | guard ProEntitlementPolicy.isUnlocked(.customTagColors) else { |
| | | return false |
| | | } |
| | | var store = TagDatabase.load() |
| | | let previousStore = store |
| | | var tagDef = store.tags[tag] ?? TagDatabase.TagDef(color: 0, systemCategoryID: nil) |
| | | tagDef.customColor = customColor |
| | | store.tags[tag] = tagDef |
| | | TagDatabase.saveUserCategorySchemeMutation( |
| | | store, |
| | | previous: previousStore, |
| | | reason: "set-tag-custom-color" |
| | | ) |
| | | return true |
| | | } |
| | | } |