Files
shotscreen/ShotScreen/Sources/GridCellView.swift
Nick Roodenrijs 0dabed11d2 🎉 ShotScreen v1.0 - Initial Release
🚀 First official release of ShotScreen with complete feature set:

 Core Features:
- Advanced screenshot capture system
- Multi-monitor support
- Professional UI/UX design
- Automated update system with Sparkle
- Apple notarized & code signed

🛠 Technical Excellence:
- Native Swift macOS application
- Professional build & deployment pipeline
- Comprehensive error handling
- Memory optimized performance

📦 Distribution Ready:
- Professional DMG packaging
- Apple notarization complete
- No security warnings for users
- Ready for public distribution

This is the foundation release that establishes ShotScreen as a premium screenshot tool for macOS.
2025-06-28 16:15:15 +02:00

194 lines
8.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import AppKit
// MARK: - Grid Cell View for Action Grid
class GridCellView: NSView {
let index: Int
private let label: NSTextField = NSTextField(labelWithString: "")
private let iconImageView: NSImageView = NSImageView()
private var iconCenterConstraint: NSLayoutConstraint!
private let originalBackgroundColor = NSColor.clear
private let highlightBackgroundColor = NSColor.clear // geen highlight
private var isHovered: Bool = false
private let iconStartOffset: CGFloat // <-- maak property
init(frame frameRect: NSRect, index: Int, text: String) {
self.index = index
self.iconStartOffset = 30 // <-- property initialiseren
super.init(frame: frameRect)
wantsLayer = true
// Setup theme change observer
ThemeManager.shared.observeThemeChanges { [weak self] in
DispatchQueue.main.async {
self?.updateThemeColors()
}
}
// Voeg mouse tracking toe
let trackingArea = NSTrackingArea(rect: bounds,
options: [.mouseEnteredAndExited, .activeAlways],
owner: self,
userInfo: nil)
addTrackingArea(trackingArea)
// Glas-achtergrond (dubbele blur)
#if false
let blur1 = NSVisualEffectView()
blur1.blendingMode = .behindWindow
blur1.material = .hudWindow
blur1.state = .active
blur1.frame = bounds
blur1.autoresizingMask = [.width, .height]
let blur2 = NSVisualEffectView()
blur2.blendingMode = .behindWindow
blur2.material = .hudWindow
blur2.state = .active
blur2.alphaValue = 0.6
blur2.frame = bounds
blur2.autoresizingMask = [.width, .height]
addSubview(blur1, positioned: .below, relativeTo: nil)
addSubview(blur2, positioned: .below, relativeTo: nil)
#endif
layer?.cornerRadius = 0
layer?.masksToBounds = false
label.stringValue = text
label.font = NSFont.systemFont(ofSize: 12, weight: .semibold)
label.textColor = ThemeManager.shared.gridCellTextColor(isHovered: false)
label.alignment = .left
label.alphaValue = 1.0 // Use theme opacity directly
label.translatesAutoresizingMaskIntoConstraints = false
addSubview(label)
// Cirkel-achtergrond voor het icoon
let iconBackground = NSView()
iconBackground.wantsLayer = true
iconBackground.translatesAutoresizingMaskIntoConstraints = false
iconBackground.layer?.cornerRadius = 18 // voor 36×36 cirkel
iconBackground.layer?.backgroundColor = ThemeManager.shared.gridCellIconBackground.cgColor
addSubview(iconBackground)
// Configuratie icon based on text content
let symbolName: String
if text.contains("Rename") {
symbolName = "pencil"
} else if text.contains("Stash") {
symbolName = "archivebox"
} else if text.contains("Text") {
symbolName = "text.viewfinder"
} else if text.contains("Clipboard") {
symbolName = "doc.on.clipboard"
} else if text.contains("Remove BG") {
symbolName = "person.and.background.dotted"
} else if text.contains("Cancel") {
symbolName = "xmark"
} else if text.contains("Remove") {
symbolName = "trash"
} else {
symbolName = "square"
}
let baseImage = NSImage(systemSymbolName: symbolName, accessibilityDescription: nil)
let configuredImage = baseImage?.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 18, weight: .semibold))
iconImageView.image = configuredImage
iconImageView.contentTintColor = ThemeManager.shared.primaryTextColor
iconImageView.translatesAutoresizingMaskIntoConstraints = false
iconBackground.addSubview(iconImageView)
// AutoLayout (start meer naar rechts)
let iconStartOffset: CGFloat = 30 // Pas dit getal aan voor meer/minder ruimte links
iconCenterConstraint = iconBackground.centerXAnchor.constraint(equalTo: centerXAnchor, constant: iconStartOffset)
NSLayoutConstraint.activate([
iconCenterConstraint,
iconBackground.centerYAnchor.constraint(equalTo: centerYAnchor),
iconBackground.widthAnchor.constraint(equalToConstant: 36),
iconBackground.heightAnchor.constraint(equalTo: iconBackground.widthAnchor),
iconImageView.centerXAnchor.constraint(equalTo: iconBackground.centerXAnchor),
iconImageView.centerYAnchor.constraint(equalTo: iconBackground.centerYAnchor),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
label.trailingAnchor.constraint(equalTo: iconBackground.leadingAnchor, constant: -8),
label.centerYAnchor.constraint(equalTo: centerYAnchor)
])
// Zet initiële transform
layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
layer?.transform = CATransform3DIdentity
// Voeg subtiele gloed toe aan het label
let shadow = NSShadow()
shadow.shadowColor = ThemeManager.shared.primaryTextColor.withAlphaComponent(0.4)
shadow.shadowBlurRadius = 4
shadow.shadowOffset = NSSize(width: 0, height: -1)
label.shadow = shadow
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func mouseEntered(with event: NSEvent) {
let shift = (self.bounds.width / 2) - 18 - 2
NSAnimationContext.runAnimationGroup { ctx in
ctx.duration = 0.15
ctx.timingFunction = CAMediaTimingFunction(name: .easeOut)
self.iconCenterConstraint.animator().constant = shift
self.label.animator().textColor = ThemeManager.shared.gridCellTextColor(isHovered: true)
}
}
override func mouseExited(with event: NSEvent) {
NSAnimationContext.runAnimationGroup { context in
context.duration = 0.15
context.timingFunction = CAMediaTimingFunction(name: .easeOut)
// Icoon terug naar startpositie
self.iconCenterConstraint.animator().constant = self.iconStartOffset
// Text color terug naar normal
self.label.animator().textColor = ThemeManager.shared.gridCellTextColor(isHovered: false)
}
}
func setHovered(_ hovered: Bool) {
guard hovered != isHovered else { return }
isHovered = hovered
let shift = (self.bounds.width / 2) - 18 - 5
NSAnimationContext.runAnimationGroup { ctx in
ctx.duration = 0.15
ctx.timingFunction = CAMediaTimingFunction(name: .easeOut)
if hovered {
self.iconCenterConstraint.animator().constant = shift
self.label.animator().textColor = ThemeManager.shared.gridCellTextColor(isHovered: true)
} else {
self.iconCenterConstraint.animator().constant = self.iconStartOffset // Terug naar startpositie
self.label.animator().textColor = ThemeManager.shared.gridCellTextColor(isHovered: false)
}
}
}
func setHighlighted(_ highlighted: Bool) {
// Do nothing (no color change)
}
// MARK: - Theme Management
private func updateThemeColors() {
// Update text color
label.textColor = ThemeManager.shared.gridCellTextColor(isHovered: isHovered)
// Update icon background
if let iconBackground = iconImageView.superview {
iconBackground.layer?.backgroundColor = ThemeManager.shared.gridCellIconBackground.cgColor
}
// Update icon tint color
iconImageView.contentTintColor = ThemeManager.shared.primaryTextColor
// Update shadow color
let shadow = NSShadow()
shadow.shadowColor = ThemeManager.shared.primaryTextColor.withAlphaComponent(0.4)
shadow.shadowBlurRadius = 4
shadow.shadowOffset = NSSize(width: 0, height: -1)
label.shadow = shadow
}
}