Compare commits
10 Commits
58948e1614
...
38fdaf9aa3
| Author | SHA1 | Date | |
|---|---|---|---|
| 38fdaf9aa3 | |||
| 5d0247fcaf | |||
| ff63e31f4c | |||
| 76eca30e24 | |||
| d232f498d2 | |||
| 815a349e69 | |||
| 7e8addb543 | |||
| a38f067d7c | |||
| 540480f2db | |||
| 42fe921dbc |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
arm64
|
arm64
|
||||||
x86_64
|
x86_64
|
||||||
|
build
|
||||||
Grapp
|
Grapp
|
||||||
Grapp.app
|
Grapp.app
|
||||||
|
ids
|
||||||
|
|||||||
5
CHANGELOG.txt
Normal file
5
CHANGELOG.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
1.94.1
|
||||||
|
- Fixed a bug where the new path was not selectable with Finder file picker
|
||||||
|
- Fixed a bug that caused crashing when removing a new path
|
||||||
|
- Search now includes the bundle extension
|
||||||
|
- Search now adds a background highlight to searched items
|
||||||
45
Makefile
Normal file
45
Makefile
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
APPLE_ID := $(shell cat ./ids/APPLE_ID)
|
||||||
|
TEAM_ID := $(shell cat ./ids/TEAM_ID)
|
||||||
|
APP_SPECIFIC_PASSWORD := $(shell cat ./ids/APP_SPECIFIC_PASSWORD)
|
||||||
|
|
||||||
|
exe = Grapp
|
||||||
|
|
||||||
|
$(exe).app:
|
||||||
|
$(MAKE) -C src FLAGS=-O CFLAGS=-O3 UNIVERSAL=1 default
|
||||||
|
|
||||||
|
container:
|
||||||
|
ditto -c -k --keepParent ./src/$(exe).app ./build/$(exe).zip
|
||||||
|
|
||||||
|
notarize:
|
||||||
|
xcrun notarytool submit ./build/$(exe).zip \
|
||||||
|
--apple-id "$(APPLE_ID)" --team-id "$(TEAM_ID)" \
|
||||||
|
--password "$(APP_SPECIFIC_PASSWORD)" --wait
|
||||||
|
|
||||||
|
staple:
|
||||||
|
cd build && \
|
||||||
|
ditto -xk $(exe).zip . && \
|
||||||
|
rm $(exe).zip && \
|
||||||
|
xcrun stapler staple $(exe).app
|
||||||
|
|
||||||
|
zip:
|
||||||
|
cd build && \
|
||||||
|
ditto -c -k --keepParent $(exe).app $(exe).zip
|
||||||
|
|
||||||
|
dmg:
|
||||||
|
cp background.png build && cd build && \
|
||||||
|
create-dmg --volname "$(exe) Installer" --window-size 600 400 \
|
||||||
|
--background "background.png" --icon "$(exe).app" 200 170 \
|
||||||
|
--app-drop-link 400 170 --icon-size 100 "$(exe).dmg" "$(exe)"
|
||||||
|
rm build/background.png
|
||||||
|
|
||||||
|
all: $(exe).app container notarize staple zip dmg
|
||||||
|
|
||||||
|
status:
|
||||||
|
@echo 'xcrun notarytool log "" --apple-id "$(APPLE_ID)" --team-id "$(TEAM_ID)" --password "$(APP_SPECIFIC_PASSWORD)" developer_log.json'
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
|
||||||
|
clean-all: clean
|
||||||
|
mkdir build
|
||||||
|
@$(MAKE) -C src clean-all
|
||||||
@@ -2,21 +2,14 @@ import AppKit
|
|||||||
|
|
||||||
// TODO: Change to appropriate links.
|
// TODO: Change to appropriate links.
|
||||||
fileprivate enum AboutLinks {
|
fileprivate enum AboutLinks {
|
||||||
// static let website = "https://cmdbar.app"
|
static let website = "https://cmdbar.rednera.com"
|
||||||
// static let documentation = "https://cmdbar.app/documentation"
|
// static let documentation = "https://cmdbar.app/documentation"
|
||||||
// static let privacy = "https://cmdbar.app/#privacy-policy"
|
static let privacy = "https://cmdbar.rednera.com/#privacy-policy"
|
||||||
static let author = "https://kolokolnikov.org"
|
static let author = "https://kolokolnikov.dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Strings {
|
enum Strings {
|
||||||
static let copyright = "Copyright © 2024\nGarikMI. All rights reserved."
|
static let copyright = "Copyright © 2026 Rednera.\nAll rights reserved."
|
||||||
static let evaluationTitle = "License - Evaluation"
|
|
||||||
static let evaluationMessage = "You are currently using evaluation license. CmdBar will quit after 20 minutes. If you already own a license, enter it below or purchase a license."
|
|
||||||
static let activate = "Activate"
|
|
||||||
static let proTitle = "License - Activated"
|
|
||||||
static let proMessage = "Thank you for purchasing CmdBar! Enjoy!"
|
|
||||||
static let deactivate = "Deactivate"
|
|
||||||
static let activating = "Activating..."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AboutViewController: NSViewController, NSTextFieldDelegate {
|
class AboutViewController: NSViewController, NSTextFieldDelegate {
|
||||||
@@ -83,26 +76,26 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
|
|||||||
return textField
|
return textField
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private var authorButton: NSButton = {
|
// private var authorButton: NSButton = {
|
||||||
let button = NSButton()
|
|
||||||
button.title = "Author"
|
|
||||||
button.sizeToFit()
|
|
||||||
button.bezelStyle = .rounded
|
|
||||||
button.action = #selector(author)
|
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
return button
|
|
||||||
}()
|
|
||||||
|
|
||||||
// private var privacyButton: NSButton = {
|
|
||||||
// let button = NSButton()
|
// let button = NSButton()
|
||||||
// button.title = "Privacy Policy"
|
// button.title = "Author"
|
||||||
// button.sizeToFit()
|
// button.sizeToFit()
|
||||||
// button.bezelStyle = .rounded
|
// button.bezelStyle = .rounded
|
||||||
// button.action = #selector(privacy)
|
// button.action = #selector(author)
|
||||||
// button.translatesAutoresizingMaskIntoConstraints = false
|
// button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// return button
|
// return button
|
||||||
// }()
|
// }()
|
||||||
|
|
||||||
|
private var privacyButton: NSButton = {
|
||||||
|
let button = NSButton()
|
||||||
|
button.title = "Privacy Policy"
|
||||||
|
button.sizeToFit()
|
||||||
|
button.bezelStyle = .rounded
|
||||||
|
button.action = #selector(privacy)
|
||||||
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
// private var documentationButton: NSButton = {
|
// private var documentationButton: NSButton = {
|
||||||
// let button = NSButton()
|
// let button = NSButton()
|
||||||
// button.title = "Docs"
|
// button.title = "Docs"
|
||||||
@@ -112,16 +105,16 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
|
|||||||
// button.translatesAutoresizingMaskIntoConstraints = false
|
// button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// return button
|
// return button
|
||||||
// }()
|
// }()
|
||||||
//
|
|
||||||
// private var websiteButton: NSButton = {
|
private var websiteButton: NSButton = {
|
||||||
// let button = NSButton()
|
let button = NSButton()
|
||||||
// button.title = "CmdBar.app"
|
button.title = "Grapp"
|
||||||
// button.sizeToFit()
|
button.sizeToFit()
|
||||||
// button.bezelStyle = .rounded
|
button.bezelStyle = .rounded
|
||||||
// button.action = #selector(website)
|
button.action = #selector(website)
|
||||||
// button.translatesAutoresizingMaskIntoConstraints = false
|
button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// return button
|
return button
|
||||||
// }()
|
}()
|
||||||
|
|
||||||
private var buttonsContainer: NSLayoutGuide = {
|
private var buttonsContainer: NSLayoutGuide = {
|
||||||
let container = NSLayoutGuide()
|
let container = NSLayoutGuide()
|
||||||
@@ -139,10 +132,10 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
|
|||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
view.addLayoutGuide(buttonsContainer)
|
view.addLayoutGuide(buttonsContainer)
|
||||||
// view.addSubview(privacyButton)
|
view.addSubview(privacyButton)
|
||||||
// view.addSubview(documentationButton)
|
// view.addSubview(documentationButton)
|
||||||
// view.addSubview(websiteButton)
|
view.addSubview(websiteButton)
|
||||||
view.addSubview(authorButton)
|
// view.addSubview(authorButton)
|
||||||
|
|
||||||
setupConstraints()
|
setupConstraints()
|
||||||
}
|
}
|
||||||
@@ -208,51 +201,51 @@ class AboutViewController: NSViewController, NSTextFieldDelegate {
|
|||||||
buttonsContainer.centerXAnchor
|
buttonsContainer.centerXAnchor
|
||||||
.constraint(equalTo: view.centerXAnchor),
|
.constraint(equalTo: view.centerXAnchor),
|
||||||
|
|
||||||
authorButton.topAnchor
|
// authorButton.topAnchor
|
||||||
.constraint(equalTo: buttonsContainer.topAnchor),
|
|
||||||
authorButton.bottomAnchor
|
|
||||||
.constraint(equalTo: buttonsContainer.bottomAnchor),
|
|
||||||
authorButton.leadingAnchor
|
|
||||||
.constraint(equalTo: buttonsContainer.leadingAnchor),
|
|
||||||
authorButton.trailingAnchor
|
|
||||||
.constraint(equalTo: buttonsContainer.trailingAnchor),
|
|
||||||
|
|
||||||
// privacyButton.topAnchor
|
|
||||||
// .constraint(equalTo: buttonsContainer.topAnchor),
|
// .constraint(equalTo: buttonsContainer.topAnchor),
|
||||||
// privacyButton.bottomAnchor
|
// authorButton.bottomAnchor
|
||||||
// .constraint(equalTo: buttonsContainer.bottomAnchor),
|
// .constraint(equalTo: buttonsContainer.bottomAnchor),
|
||||||
// privacyButton.leadingAnchor
|
// authorButton.leadingAnchor
|
||||||
// .constraint(equalTo: buttonsContainer.leadingAnchor),
|
// .constraint(equalTo: buttonsContainer.leadingAnchor),
|
||||||
//
|
// authorButton.trailingAnchor
|
||||||
|
// .constraint(equalTo: buttonsContainer.trailingAnchor),
|
||||||
|
|
||||||
|
privacyButton.topAnchor
|
||||||
|
.constraint(equalTo: buttonsContainer.topAnchor),
|
||||||
|
privacyButton.bottomAnchor
|
||||||
|
.constraint(equalTo: buttonsContainer.bottomAnchor),
|
||||||
|
privacyButton.leadingAnchor
|
||||||
|
.constraint(equalTo: buttonsContainer.leadingAnchor),
|
||||||
|
|
||||||
// documentationButton.firstBaselineAnchor
|
// documentationButton.firstBaselineAnchor
|
||||||
// .constraint(equalTo: privacyButton.firstBaselineAnchor),
|
// .constraint(equalTo: privacyButton.firstBaselineAnchor),
|
||||||
// documentationButton.leadingAnchor
|
// documentationButton.leadingAnchor
|
||||||
// .constraint(equalTo: privacyButton.trailingAnchor,
|
// .constraint(equalTo: privacyButton.trailingAnchor,
|
||||||
// constant: ViewConstants.spacing10),
|
// constant: ViewConstants.spacing10),
|
||||||
//
|
|
||||||
// websiteButton.firstBaselineAnchor
|
websiteButton.firstBaselineAnchor
|
||||||
// .constraint(equalTo: privacyButton.firstBaselineAnchor),
|
.constraint(equalTo: privacyButton.firstBaselineAnchor),
|
||||||
// websiteButton.leadingAnchor
|
websiteButton.leadingAnchor
|
||||||
// .constraint(equalTo: documentationButton.trailingAnchor,
|
.constraint(equalTo: privacyButton.trailingAnchor,
|
||||||
// constant: ViewConstants.spacing10),
|
constant: ViewConstants.spacing10),
|
||||||
// websiteButton.trailingAnchor
|
websiteButton.trailingAnchor
|
||||||
// .constraint(equalTo: buttonsContainer.trailingAnchor),
|
.constraint(equalTo: buttonsContainer.trailingAnchor),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func author() {
|
// @objc private func author() {
|
||||||
NSWorkspace.shared.open(URL(string: AboutLinks.author)!)
|
// NSWorkspace.shared.open(URL(string: AboutLinks.author)!)
|
||||||
|
// }
|
||||||
|
|
||||||
|
@objc private func privacy() {
|
||||||
|
NSWorkspace.shared.open(URL(string: AboutLinks.privacy)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @objc private func privacy() {
|
|
||||||
// NSWorkspace.shared.open(URL(string: AboutLinks.privacy)!)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @objc private func documentation() {
|
// @objc private func documentation() {
|
||||||
// NSWorkspace.shared.open(URL(string: AboutLinks.documentation)!)
|
// NSWorkspace.shared.open(URL(string: AboutLinks.documentation)!)
|
||||||
// }
|
// }
|
||||||
//
|
|
||||||
// @objc private func website() {
|
@objc private func website() {
|
||||||
// NSWorkspace.shared.open(URL(string: AboutLinks.website)!)
|
NSWorkspace.shared.open(URL(string: AboutLinks.website)!)
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
|||||||
|
|
||||||
window.delegate = self
|
window.delegate = self
|
||||||
|
|
||||||
|
// TODO: Move down.
|
||||||
// NOTE: Here we check wether the program was launched by the
|
// NOTE: Here we check wether the program was launched by the
|
||||||
// system (e.g. launch-at-login). If it was not, then display the
|
// system (e.g. launch-at-login). If it was not, then display the
|
||||||
// window.
|
// window.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
<string>AppIcon</string>
|
<string>AppIcon</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.garikme.Grapp</string>
|
<string>com.rednera.Grapp</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2</string>
|
<string>0.2.0</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>MacOSX</string>
|
<string>MacOSX</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.2</string>
|
<string>0.2.0</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>macosx</string>
|
<string>macosx</string>
|
||||||
<key>DTPlatformVersion</key>
|
<key>DTPlatformVersion</key>
|
||||||
@@ -40,5 +40,7 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>Copyright © 2026 Rednera. All rights reserved.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
APPLE_DEVELOPMENT := $(shell cat ../ids/APPLE_DEVELOPMENT)
|
||||||
|
APPLE_DEVELOPER_ID_APPLICATION := $(shell cat ../ids/APPLE_DEVELOPER_ID_APPLICATION)
|
||||||
|
|
||||||
FLAGS = -g
|
FLAGS = -g
|
||||||
CFLAGS = -g
|
CFLAGS = -g
|
||||||
#-O
|
#-O
|
||||||
@@ -23,12 +26,6 @@ FRAMEWORKS = -framework AppKit -framework ServiceManagement
|
|||||||
|
|
||||||
default: $(EXEC).app
|
default: $(EXEC).app
|
||||||
|
|
||||||
# HACK: Target is getting touched because timestamps of the generated
|
|
||||||
# object file don't change unless there's an actual change in the
|
|
||||||
# outputted object code. This results in this target running every
|
|
||||||
# single time. I'm not sure whether that's the exact reason, but
|
|
||||||
# I can't imagine why timestamps wouldn't change. When clang
|
|
||||||
# generates same exact executable, timestamps do change.
|
|
||||||
./arm64/%.o: %.swift
|
./arm64/%.o: %.swift
|
||||||
swift -frontend -c \
|
swift -frontend -c \
|
||||||
-target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \
|
-target arm64-apple-macos$(MACOS_VERSION) $(FLAGS) \
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
|
fileprivate let INDEX_DEEPNESS = 2
|
||||||
|
|
||||||
struct Program {
|
struct Program {
|
||||||
var path: String = ""
|
var path: String = ""
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
@@ -19,9 +21,9 @@ final class PathManager {
|
|||||||
"/Applications",
|
"/Applications",
|
||||||
"/System/Applications",
|
"/System/Applications",
|
||||||
"/System/Applications/Utilities",
|
"/System/Applications/Utilities",
|
||||||
"/System/Library/CoreServices",
|
"/System/Library/CoreServices", // TODO: NOTE: Remove this one? This one contains Finder and Siri.
|
||||||
"/Applications/Xcode.app/Contents/Applications",
|
"/System/Library/CoreServices/Applications",
|
||||||
"/System/Library/CoreServices/Applications"
|
"/Applications/Xcode.app/Contents/Applications"
|
||||||
]
|
]
|
||||||
private(set) var paths: [String: [Program]] = [:]
|
private(set) var paths: [String: [Program]] = [:]
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ final class PathManager {
|
|||||||
// allocations.
|
// allocations.
|
||||||
public func rebuildIndex(at path: String) {
|
public func rebuildIndex(at path: String) {
|
||||||
paths[path] = []
|
paths[path] = []
|
||||||
paths[path] = indexDirs(at: path, deepness: 2)
|
paths[path] = indexDirs(at: path, deepness: INDEX_DEEPNESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func indexDirs(at path: String, deepness: Int) -> [Program] {
|
public func indexDirs(at path: String, deepness: Int) -> [Program] {
|
||||||
@@ -134,7 +136,6 @@ final class PathManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func updateIndex() {
|
public func updateIndex() {
|
||||||
print("updateIndex()")
|
|
||||||
for path in paths {
|
for path in paths {
|
||||||
rebuildIndex(at: path.key)
|
rebuildIndex(at: path.key)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class ProgramsTableViewCell: NSTableCellView {
|
|||||||
|
|
||||||
public var indexLabel: NSTextField = {
|
public var indexLabel: NSTextField = {
|
||||||
let field = NSTextField(labelWithString: "-")
|
let field = NSTextField(labelWithString: "-")
|
||||||
|
field.isEditable = false
|
||||||
field.alignment = .center
|
field.alignment = .center
|
||||||
|
|
||||||
// field.drawsBackground = true
|
// field.drawsBackground = true
|
||||||
@@ -49,16 +50,18 @@ class ProgramsTableViewCell: NSTableCellView {
|
|||||||
|
|
||||||
public var titleField: NSTextField = {
|
public var titleField: NSTextField = {
|
||||||
let field = NSTextField()
|
let field = NSTextField()
|
||||||
|
field.isEditable = false
|
||||||
field.isBordered = false
|
field.isBordered = false
|
||||||
field.drawsBackground = false
|
field.drawsBackground = false
|
||||||
field.lineBreakMode = .byTruncatingTail
|
field.lineBreakMode = .byTruncatingTail
|
||||||
field.textColor = NSColor.secondaryLabelColor
|
field.textColor = NSColor.textColor
|
||||||
field.translatesAutoresizingMaskIntoConstraints = false
|
field.translatesAutoresizingMaskIntoConstraints = false
|
||||||
return field
|
return field
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public var progPathLabel: NSTextField = {
|
public var progPathLabel: NSTextField = {
|
||||||
let field = NSTextField()
|
let field = NSTextField()
|
||||||
|
field.isEditable = false
|
||||||
field.isBordered = false
|
field.isBordered = false
|
||||||
field.drawsBackground = false
|
field.drawsBackground = false
|
||||||
field.lineBreakMode = .byTruncatingTail
|
field.lineBreakMode = .byTruncatingTail
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
|||||||
textField.lineBreakMode = .byTruncatingHead
|
textField.lineBreakMode = .byTruncatingHead
|
||||||
textField.focusRingType = .none
|
textField.focusRingType = .none
|
||||||
textField.placeholderString = "Program Search"
|
textField.placeholderString = "Program Search"
|
||||||
|
textField.isAutomaticTextCompletionEnabled = false
|
||||||
textField.bezelStyle = .roundedBezel
|
textField.bezelStyle = .roundedBezel
|
||||||
textField.font = NSFont
|
textField.font = NSFont
|
||||||
.systemFont(ofSize: NSFontDescriptor
|
.systemFont(ofSize: NSFontDescriptor
|
||||||
@@ -346,7 +347,9 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
|||||||
|
|
||||||
private func reloadProgramsTableViewData() {
|
private func reloadProgramsTableViewData() {
|
||||||
if listIndex > 0 {
|
if listIndex > 0 {
|
||||||
tableViewHeightAnchor?.constant = 210
|
// TODO: Why is this located here, randomly? Make it a global
|
||||||
|
// config variable.
|
||||||
|
tableViewHeightAnchor?.constant = 250
|
||||||
} else {
|
} else {
|
||||||
tableViewHeightAnchor?.constant = 0
|
tableViewHeightAnchor?.constant = 0
|
||||||
}
|
}
|
||||||
@@ -388,7 +391,7 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
|||||||
if listIndex >= maxItems { break outerloop }
|
if listIndex >= maxItems { break outerloop }
|
||||||
let prog = path.value[i]
|
let prog = path.value[i]
|
||||||
|
|
||||||
if prog.name.lowercased()
|
if (prog.name.lowercased() + prog.ext)
|
||||||
.contains(searchInput.stringValue.lowercased())
|
.contains(searchInput.stringValue.lowercased())
|
||||||
{
|
{
|
||||||
programsList[listIndex].path = prog.path
|
programsList[listIndex].path = prog.path
|
||||||
@@ -477,9 +480,9 @@ class SearchViewController: NSViewController, NSTextFieldDelegate,
|
|||||||
)
|
)
|
||||||
let attributedString = NSMutableAttributedString(string: app)
|
let attributedString = NSMutableAttributedString(string: app)
|
||||||
attributedString
|
attributedString
|
||||||
.addAttributes([.foregroundColor: NSColor.labelColor],
|
.addAttributes([.backgroundColor:
|
||||||
|
NSColor.systemYellow.withAlphaComponent(0.5)],
|
||||||
range: rangeToHighlight)
|
range: rangeToHighlight)
|
||||||
|
|
||||||
cell.titleField.attributedStringValue = attributedString
|
cell.titleField.attributedStringValue = attributedString
|
||||||
cell.progPathLabel.stringValue = program.path
|
cell.progPathLabel.stringValue = program.path
|
||||||
cell.appIconImage.image = program.img
|
cell.appIconImage.image = program.img
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import ServiceManagement
|
|||||||
// TODO: Rework the paths table and selection. Right now, it is very
|
// TODO: Rework the paths table and selection. Right now, it is very
|
||||||
// disfunctional and error-prone.
|
// disfunctional and error-prone.
|
||||||
class SettingsViewController: NSViewController,
|
class SettingsViewController: NSViewController,
|
||||||
NSTextFieldDelegate, KeyDetectorButtonDelegate,
|
NSTextFieldDelegate, KeyDetectorButtonDelegate, NSTableViewDataSource,
|
||||||
NSTableViewDataSource, NSTableViewDelegate,
|
NSTableViewDelegate, PathsTableCellViewDelegate
|
||||||
PathsTableCellViewDelegate
|
|
||||||
{
|
{
|
||||||
private var keyboardEvents: EventMonitor?
|
private var keyboardEvents: EventMonitor?
|
||||||
|
|
||||||
@@ -15,6 +14,8 @@ class SettingsViewController: NSViewController,
|
|||||||
|
|
||||||
// NOTE: This is the default shortcut. If you were to change it, don't
|
// NOTE: This is the default shortcut. If you were to change it, don't
|
||||||
// forget to change other places in this file and delegate, too.
|
// forget to change other places in this file and delegate, too.
|
||||||
|
// TODO: Come up with a way where we don't have to specify these in
|
||||||
|
// multiple locations.
|
||||||
private var keyCode = Int(kVK_Space)
|
private var keyCode = Int(kVK_Space)
|
||||||
private var modifiers = Int(optionKey)
|
private var modifiers = Int(optionKey)
|
||||||
|
|
||||||
@@ -356,7 +357,7 @@ class SettingsViewController: NSViewController,
|
|||||||
let modifiers = event.modifierFlags.rawValue
|
let modifiers = event.modifierFlags.rawValue
|
||||||
|
|
||||||
if modsContains(keys: OSCmd, in: modifiers) &&
|
if modsContains(keys: OSCmd, in: modifiers) &&
|
||||||
key == kVK_ANSI_Q || modsContainsNone(in: modifiers)
|
key == kVK_ANSI_Q
|
||||||
{
|
{
|
||||||
NSApplication.shared.terminate(self)
|
NSApplication.shared.terminate(self)
|
||||||
}
|
}
|
||||||
@@ -467,7 +468,7 @@ class SettingsViewController: NSViewController,
|
|||||||
|
|
||||||
@objc
|
@objc
|
||||||
private func reset() {
|
private func reset() {
|
||||||
keyCode = Int(kVK_Space)
|
keyCode = Int(kVK_Space) // TODO: Put into something like Defaults.swift file.
|
||||||
modifiers = Int(optionKey)
|
modifiers = Int(optionKey)
|
||||||
HotKeyManager.shared.registerHotKey(key: keyCode,
|
HotKeyManager.shared.registerHotKey(key: keyCode,
|
||||||
modifiers: modifiers)
|
modifiers: modifiers)
|
||||||
@@ -553,6 +554,10 @@ class SettingsViewController: NSViewController,
|
|||||||
) as? PathsTableCellView)?.startEditing()
|
) as? PathsTableCellView)?.startEditing()
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
|
(pathsTableView
|
||||||
|
.view(atColumn: 0, row: pathsTableView.selectedRow,
|
||||||
|
makeIfNecessary: false
|
||||||
|
) as? PathsTableCellView)?.stopEditing()
|
||||||
if pathsTableView.selectedRow > -1 {
|
if pathsTableView.selectedRow > -1 {
|
||||||
paths.remove(at: pathsTableView.selectedRow)
|
paths.remove(at: pathsTableView.selectedRow)
|
||||||
pathsTableView.reloadData()
|
pathsTableView.reloadData()
|
||||||
@@ -623,37 +628,22 @@ class SettingsViewController: NSViewController,
|
|||||||
func selectionButtonClicked(tag: Int) {
|
func selectionButtonClicked(tag: Int) {
|
||||||
if dirPicker == nil {
|
if dirPicker == nil {
|
||||||
dirPicker = NSOpenPanel()
|
dirPicker = NSOpenPanel()
|
||||||
dirPicker!.message = "Select a directory to search applications in..."
|
dirPicker!.message = "Select a directory with applications..."
|
||||||
dirPicker!.canChooseDirectories = true
|
dirPicker!.canChooseDirectories = true
|
||||||
dirPicker!.canChooseFiles = false
|
dirPicker!.canChooseFiles = false
|
||||||
dirPicker!.allowsMultipleSelection = false
|
dirPicker!.allowsMultipleSelection = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARN:
|
|
||||||
// FIX: There is a bug where the program crashes when adding a new
|
|
||||||
// path. This happens because the settings popup is closed before
|
|
||||||
// displaying the selection modal, as a result the new path item
|
|
||||||
// is cleared (well, b/c it's empty) and the path gets set into
|
|
||||||
// non-existent memory which results in segmentation fault.
|
|
||||||
|
|
||||||
NSRunningApplication.current.activate(options: .activateAllWindows)
|
|
||||||
delegate.window.level = .normal
|
|
||||||
delegate.aboutWindow.performClose(nil)
|
|
||||||
|
|
||||||
if dirPicker!.runModal() == .OK {
|
if dirPicker!.runModal() == .OK {
|
||||||
if let url = dirPicker!.url {
|
if let url = dirPicker!.url {
|
||||||
|
print("tag=\(tag) url.path=\(url.path)")
|
||||||
|
(pathsTableView
|
||||||
|
.view(atColumn: 0, row: tag, makeIfNecessary: false
|
||||||
|
) as? PathsTableCellView)?.titleField.stringValue = url.path
|
||||||
paths[tag] = url.path
|
paths[tag] = url.path
|
||||||
pathsTableView.reloadData()
|
pathsTableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate.window.level = .statusBar
|
|
||||||
delegate.window.makeKeyAndOrderFront(nil)
|
|
||||||
if let controller =
|
|
||||||
delegate.window.contentViewController as? SearchViewController
|
|
||||||
{
|
|
||||||
controller.openSettings()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
@@ -663,9 +653,9 @@ class SettingsViewController: NSViewController,
|
|||||||
func tableView(_ tableView: NSTableView,
|
func tableView(_ tableView: NSTableView,
|
||||||
viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
|
viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
|
||||||
{
|
{
|
||||||
let rect = NSRect(x: 0, y: 0, width: tableColumn!.width,
|
let rect = NSRect(x: 0, y: 0, width: tableColumn!.width, height: 20)
|
||||||
height: 20)
|
|
||||||
let cell = PathsTableCellView(frame: rect)
|
let cell = PathsTableCellView(frame: rect)
|
||||||
|
cell.titleField.textColor = isDirectory(paths[row]) ? NSColor.labelColor : NSColor.systemRed
|
||||||
cell.titleField.stringValue = paths[row]
|
cell.titleField.stringValue = paths[row]
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
cell.id = row
|
cell.id = row
|
||||||
|
|||||||
Reference in New Issue
Block a user