diff --git a/.build/Pommedoro.dmg b/.build/Pommedoro.dmg index c878a0a..90949db 100644 Binary files a/.build/Pommedoro.dmg and b/.build/Pommedoro.dmg differ diff --git a/Makefile b/Makefile index b03dd41..1fdff7a 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,8 @@ dmg: bundle @mkdir -p $(BUILD_DIR)/dmg-staging @cp -R $(APP_BUNDLE) $(BUILD_DIR)/dmg-staging/ @ln -s /Applications $(BUILD_DIR)/dmg-staging/Applications + @cp Resources/$(APP_NAME).icns $(BUILD_DIR)/dmg-staging/.VolumeIcon.icns + @SetFile -a C $(BUILD_DIR)/dmg-staging @hdiutil create -volname "$(APP_NAME)" \ -srcfolder $(BUILD_DIR)/dmg-staging \ -ov -format UDZO \ diff --git a/Sources/Pommedoro/SettingsMenu.swift b/Sources/Pommedoro/SettingsMenu.swift index 792e81b..c64c65a 100644 --- a/Sources/Pommedoro/SettingsMenu.swift +++ b/Sources/Pommedoro/SettingsMenu.swift @@ -48,38 +48,98 @@ class SliderMenuItemView: NSView { } } -// MARK: - Color Menu Item View -class ColorMenuItemView: NSView { - let colorWell: NSColorWell +// MARK: - Color Swatch Menu Item View +class ColorSwatchMenuItemView: NSView { private let titleLabel: NSTextField + private var swatchButtons: [NSButton] = [] + private var selectedIndex: Int = -1 var onChanged: ((NSColor) -> Void)? - init(title: String, color: NSColor, width: CGFloat = 280) { - titleLabel = NSTextField(labelWithString: title) - colorWell = NSColorWell(frame: .zero) + static let presetColors: [(String, NSColor)] = [ + ("Teal", .systemTeal), + ("Blue", .systemBlue), + ("Purple", .systemPurple), + ("Pink", .systemPink), + ("Red", .systemRed), + ("Orange", .systemOrange), + ("Yellow", .systemYellow), + ("Green", .systemGreen), + ("Indigo", .systemIndigo), + ] - super.init(frame: NSRect(x: 0, y: 0, width: width, height: 34)) + init(title: String, currentColor: NSColor, width: CGFloat = 280) { + titleLabel = NSTextField(labelWithString: title) + super.init(frame: NSRect(x: 0, y: 0, width: width, height: 56)) titleLabel.font = NSFont.systemFont(ofSize: 12, weight: .medium) titleLabel.textColor = .labelColor - titleLabel.frame = NSRect(x: 16, y: 7, width: 120, height: 16) + titleLabel.frame = NSRect(x: 16, y: 34, width: width - 32, height: 16) addSubview(titleLabel) - colorWell.frame = NSRect(x: 140, y: 4, width: 40, height: 26) - colorWell.color = color - colorWell.target = self - colorWell.action = #selector(colorPicked) - addSubview(colorWell) + let swatchSize: CGFloat = 22 + let gap: CGFloat = 4 + let count = Self.presetColors.count + let totalW = CGFloat(count) * swatchSize + CGFloat(count - 1) * gap + var x = (width - totalW) / 2 + + for (i, (_, color)) in Self.presetColors.enumerated() { + let btn = NSButton(frame: NSRect(x: x, y: 6, width: swatchSize, height: swatchSize)) + btn.wantsLayer = true + btn.isBordered = false + btn.title = "" + btn.layer?.backgroundColor = color.cgColor + btn.layer?.cornerRadius = swatchSize / 2 + btn.layer?.borderWidth = 2 + btn.layer?.borderColor = NSColor.clear.cgColor + btn.tag = i + btn.target = self + btn.action = #selector(swatchClicked(_:)) + addSubview(btn) + swatchButtons.append(btn) + x += swatchSize + gap + } + + highlightClosest(to: currentColor) } required init?(coder: NSCoder) { fatalError() } - @objc private func colorPicked() { - onChanged?(colorWell.color) + @objc private func swatchClicked(_ sender: NSButton) { + let idx = sender.tag + guard idx >= 0 && idx < Self.presetColors.count else { return } + setSelected(idx) + onChanged?(Self.presetColors[idx].1) + } + + private func setSelected(_ index: Int) { + // Clear previous + for btn in swatchButtons { + btn.layer?.borderColor = NSColor.clear.cgColor + } + // Highlight new + if index >= 0 && index < swatchButtons.count { + swatchButtons[index].layer?.borderColor = NSColor.white.cgColor + selectedIndex = index + } + } + + func highlightClosest(to target: NSColor) { + let t = target.usingColorSpace(.sRGB) ?? target + var bestIdx = 0 + var bestDist: CGFloat = .greatestFiniteMagnitude + for (i, (_, color)) in Self.presetColors.enumerated() { + let c = color.usingColorSpace(.sRGB) ?? color + let dr = t.redComponent - c.redComponent + let dg = t.greenComponent - c.greenComponent + let db = t.blueComponent - c.blueComponent + let dist = dr*dr + dg*dg + db*db + if dist < bestDist { bestDist = dist; bestIdx = i } + } + setSelected(bestIdx) } func update(color: NSColor) { - colorWell.color = color + highlightClosest(to: color) } } @@ -87,14 +147,13 @@ class ColorMenuItemView: NSView { class SettingsMenuBuilder { private var intensityView: SliderMenuItemView? private var durationView: SliderMenuItemView? - private var colorView: ColorMenuItemView? + private var colorView: ColorSwatchMenuItemView? - /// Build settings items and append them to the given menu as a submenu func buildSettingsSubmenu() -> NSMenuItem { let submenu = NSMenu(title: "Settings") - // Color - let cv = ColorMenuItemView(title: "Gradient Color:", color: Settings.shared.gradientColor) + // Color swatches + let cv = ColorSwatchMenuItemView(title: "Gradient Color:", currentColor: Settings.shared.gradientColor) cv.onChanged = { color in Settings.shared.gradientColor = color } colorView = cv let colorItem = NSMenuItem() diff --git a/releases/Pommedoro.dmg b/releases/Pommedoro.dmg index c878a0a..90949db 100644 Binary files a/releases/Pommedoro.dmg and b/releases/Pommedoro.dmg differ