Redesign of the window.

This commit is contained in:
2025-01-10 22:22:07 -08:00
parent ad6e1709c3
commit bfd7b14db7
4 changed files with 128 additions and 21 deletions

View File

@@ -11,8 +11,8 @@ SRCMODULES = Helpers.swift EditableNSTextField.swift EventMonitor.swift \
GlobalEventTap.swift PopoverPanel.swift SearchViewController.swift \ GlobalEventTap.swift PopoverPanel.swift SearchViewController.swift \
SettingsViewController.swift HotKeyManager.swift \ SettingsViewController.swift HotKeyManager.swift \
KeyDetectorButton.swift PathManager.swift PathsTableCellView.swift \ KeyDetectorButton.swift PathManager.swift PathsTableCellView.swift \
ProgramTableViewCell.swift ProgramsTableView.swift AppDelegate.swift \ ProgramTableViewCell.swift ProgramsTableView.swift ShadowView.swift \
main.swift AppDelegate.swift main.swift
ARMOBJMODULES = $(addprefix ./arm64/,$(SRCMODULES:.swift=.o)) ARMOBJMODULES = $(addprefix ./arm64/,$(SRCMODULES:.swift=.o))
X86OBJMODULES = $(addprefix ./x86_64/,$(SRCMODULES:.swift=.o)) X86OBJMODULES = $(addprefix ./x86_64/,$(SRCMODULES:.swift=.o))

View File

@@ -7,20 +7,22 @@ class PopoverPanel: NSPanel {
init(viewController: NSViewController) { init(viewController: NSViewController) {
super.init( super.init(
contentRect: CGRect(x: 0, y: 0, width: 100, height: 100), contentRect: CGRect(x: 0, y: 0, width: 100, height: 100),
styleMask: [.titled, .nonactivatingPanel, .utilityWindow, styleMask: [.borderless, .nonactivatingPanel, .utilityWindow,
.fullSizeContentView], .fullSizeContentView],
backing: .buffered, backing: .buffered,
defer: false defer: false
) )
super.contentViewController = viewController super.contentViewController = viewController
isOpaque = false
backgroundColor = NSColor.clear
hasShadow = false
title = "" title = ""
isMovable = true isMovable = true
isMovableByWindowBackground = true isMovableByWindowBackground = true
isFloatingPanel = true isFloatingPanel = true
isOpaque = false
level = .statusBar level = .statusBar
titleVisibility = .hidden
titlebarAppearsTransparent = true titlebarAppearsTransparent = true
animationBehavior = .none animationBehavior = .none
@@ -28,10 +30,6 @@ class PopoverPanel: NSPanel {
.transient] .transient]
isReleasedWhenClosed = false isReleasedWhenClosed = false
hidesOnDeactivate = false hidesOnDeactivate = false
standardWindowButton(.closeButton)?.isHidden = true
standardWindowButton(.miniaturizeButton)?.isHidden = true
standardWindowButton(.zoomButton)?.isHidden = true
} }
override func performKeyEquivalent(with event: NSEvent) -> Bool { override func performKeyEquivalent(with event: NSEvent) -> Bool {

View File

@@ -1,6 +1,11 @@
import AppKit import AppKit
import Carbon import Carbon
// NOTE: This is the corner radius of the backgrounView view that acts as
// a window frame and an NSViewController's view that clips all
// elements inside of it.
fileprivate let windowCornerRadius = 20.0
class SearchViewController: NSViewController, NSTextFieldDelegate, class SearchViewController: NSViewController, NSTextFieldDelegate,
NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate NSPopoverDelegate, NSTableViewDataSource, NSTableViewDelegate
{ {
@@ -18,6 +23,42 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
return popover return popover
}() }()
private var shadowView: ShadowView = {
let view = ShadowView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private var backgroundView: NSVisualEffectView = {
let effect = NSVisualEffectView()
effect.blendingMode = .behindWindow
effect.state = .active
effect.material = .popover
effect.wantsLayer = true
effect.layer?.masksToBounds = true
effect.layer?.borderColor = NSColor.labelColor.withAlphaComponent(0.1).cgColor
effect.layer?.borderWidth = 1
effect.layer?.cornerRadius = windowCornerRadius
effect.translatesAutoresizingMaskIntoConstraints = false
return effect
}()
private var contentView: NSView = {
let view = NSView()
// Clip all content to window's rounded frame emulated by
// backgroundView.
view.wantsLayer = true
view.layer?.masksToBounds = true
view.layer?.cornerRadius = windowCornerRadius
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private var appIconImage: NSImageView = { private var appIconImage: NSImageView = {
let image = NSImageView() let image = NSImageView()
image.image = image.image =
@@ -53,7 +94,8 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
private var tableScrollView: NSScrollView = { private var tableScrollView: NSScrollView = {
let scroll = NSScrollView() let scroll = NSScrollView()
scroll.contentInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) scroll.contentInsets = NSEdgeInsets(top: 0, left: 0, bottom: 0,
right: 0)
scroll.drawsBackground = false scroll.drawsBackground = false
scroll.translatesAutoresizingMaskIntoConstraints = false scroll.translatesAutoresizingMaskIntoConstraints = false
return scroll return scroll
@@ -81,10 +123,14 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
}() }()
private func addSubviews() { private func addSubviews() {
view.addSubview(appIconImage) view.addSubview(shadowView)
view.addSubview(searchInput) view.addSubview(backgroundView)
view.addSubview(settingsButton) view.addSubview(contentView)
view.addSubview(tableScrollView)
contentView.addSubview(appIconImage)
contentView.addSubview(searchInput)
contentView.addSubview(settingsButton)
contentView.addSubview(tableScrollView)
} }
var viewBottomAnchorTable: NSLayoutConstraint? var viewBottomAnchorTable: NSLayoutConstraint?
@@ -92,24 +138,52 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
private func setConstraints() { private func setConstraints() {
viewBottomAnchorTable = tableScrollView.bottomAnchor.constraint( viewBottomAnchorTable = tableScrollView.bottomAnchor.constraint(
equalTo: view.bottomAnchor, equalTo: contentView.bottomAnchor,
constant: -ViewConstants.spacing10) constant: -ViewConstants.spacing10)
viewBottomAnchorImage = appIconImage.bottomAnchor.constraint( viewBottomAnchorImage = appIconImage.bottomAnchor.constraint(
equalTo: view.bottomAnchor, equalTo: contentView.bottomAnchor,
constant: -ViewConstants.spacing10) constant: -ViewConstants.spacing10)
viewBottomAnchorTable?.isActive = false viewBottomAnchorTable?.isActive = false
viewBottomAnchorImage?.isActive = true viewBottomAnchorImage?.isActive = true
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: contentView.topAnchor,
constant: -100),
view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor,
constant: 100),
view.leadingAnchor.constraint(
equalTo: contentView.leadingAnchor, constant: -100),
view.trailingAnchor.constraint(
equalTo: contentView.trailingAnchor, constant: 100),
shadowView.topAnchor.constraint(
equalTo: backgroundView.topAnchor),
shadowView.bottomAnchor.constraint(
equalTo: backgroundView.bottomAnchor),
shadowView.leadingAnchor.constraint(
equalTo: backgroundView.leadingAnchor),
shadowView.trailingAnchor.constraint(
equalTo: backgroundView.trailingAnchor),
backgroundView.topAnchor.constraint(
equalTo: contentView.topAnchor),
backgroundView.bottomAnchor.constraint(
equalTo: contentView.bottomAnchor),
backgroundView.leadingAnchor.constraint(
equalTo: contentView.leadingAnchor),
backgroundView.trailingAnchor.constraint(
equalTo: contentView.trailingAnchor),
appIconImage.widthAnchor.constraint(equalToConstant: 60), appIconImage.widthAnchor.constraint(equalToConstant: 60),
appIconImage.heightAnchor.constraint( appIconImage.heightAnchor.constraint(
equalTo: appIconImage.widthAnchor, multiplier: 1), equalTo: appIconImage.widthAnchor, multiplier: 1),
appIconImage.topAnchor.constraint(equalTo: view.topAnchor, appIconImage.topAnchor.constraint(
equalTo: contentView.topAnchor,
constant: ViewConstants.spacing10), constant: ViewConstants.spacing10),
appIconImage.leadingAnchor.constraint( appIconImage.leadingAnchor.constraint(
equalTo: view.leadingAnchor, equalTo: contentView.leadingAnchor,
constant: ViewConstants.spacing10), constant: ViewConstants.spacing10),
searchInput.widthAnchor.constraint(equalToConstant: 300), searchInput.widthAnchor.constraint(equalToConstant: 300),
@@ -125,7 +199,7 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
equalTo: searchInput.trailingAnchor, equalTo: searchInput.trailingAnchor,
constant: ViewConstants.spacing10), constant: ViewConstants.spacing10),
settingsButton.trailingAnchor.constraint( settingsButton.trailingAnchor.constraint(
equalTo: view.trailingAnchor, equalTo: contentView.trailingAnchor,
constant: -ViewConstants.spacing10), constant: -ViewConstants.spacing10),
tableScrollView.heightAnchor.constraint(equalToConstant: 210), tableScrollView.heightAnchor.constraint(equalToConstant: 210),
@@ -133,15 +207,18 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
equalTo: appIconImage.bottomAnchor, equalTo: appIconImage.bottomAnchor,
constant: ViewConstants.spacing10), constant: ViewConstants.spacing10),
tableScrollView.leadingAnchor.constraint( tableScrollView.leadingAnchor.constraint(
equalTo: view.leadingAnchor), equalTo: contentView.leadingAnchor),
tableScrollView.trailingAnchor.constraint( tableScrollView.trailingAnchor.constraint(
equalTo: view.trailingAnchor) equalTo: contentView.trailingAnchor)
]) ])
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.clear.cgColor
keyboardEvents = LocalEventMonitor(mask: [.keyDown], handler: keyboardEvents = LocalEventMonitor(mask: [.keyDown], handler:
{ [weak self] event in { [weak self] event in
let key = event.keyCode let key = event.keyCode

32
src/ShadowView.swift Normal file
View File

@@ -0,0 +1,32 @@
import AppKit
class ShadowView: NSView {
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
wantsLayer = true
guard let layer = layer else { return }
let shadow = NSShadow()
shadow.shadowColor = NSColor.black.withAlphaComponent(0.4)
shadow.shadowBlurRadius = 20.0
shadow.shadowOffset = CGSize(width: 0, height: -10)
shadow.set()
layer.shadowPath = CGPath(rect: bounds, transform: nil)
self.shadow = shadow
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
setupView()
}
}