Redesign of the window.
This commit is contained in:
@@ -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))
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
32
src/ShadowView.swift
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user