From 94717a5ea3dd1d0d64f5341443efb8b61701b747 Mon Sep 17 00:00:00 2001 From: Serg Date: Sat, 26 Oct 2019 17:22:19 +0700 Subject: [PATCH 1/5] missed a single userdefaults --- MTMR/AppSettings.swift | 3 +++ MTMR/Widgets/AppScrubberTouchBarItem.swift | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/MTMR/AppSettings.swift b/MTMR/AppSettings.swift index c3f7cdf..6f573ef 100644 --- a/MTMR/AppSettings.swift +++ b/MTMR/AppSettings.swift @@ -12,6 +12,9 @@ struct AppSettings { @UserDefault(key: "com.toxblh.mtmr.blackListedApps", defaultValue: []) static var blacklistedAppIds: [String] + + @UserDefault(key: "com.toxblh.mtmr.dock.persistent", defaultValue: []) + static var dockPersistentAppIds: [String] } @propertyWrapper diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index 0466be7..b5c4d13 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -58,10 +58,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didTerminateApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil) - if let persistent = UserDefaults.standard.stringArray(forKey: "com.toxblh.mtmr.dock.persistent") { - persistentAppIdentifiers = persistent - } - + persistentAppIdentifiers = AppSettings.dockPersistentAppIds updateRunningApplication() } @@ -208,8 +205,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub persistentAppIdentifiers.append(bundleIdentifier!) } - UserDefaults.standard.set(persistentAppIdentifiers, forKey: "com.toxblh.mtmr.dock.persistent") - UserDefaults.standard.synchronize() + AppSettings.dockPersistentAppIds = persistentAppIdentifiers } ticks = 0 updateRunningApplication() From f82d7694eb5e4ce266a418757b502032968563a8 Mon Sep 17 00:00:00 2001 From: Serg Date: Sat, 26 Oct 2019 21:23:50 +0700 Subject: [PATCH 2/5] rewrite in scrollview; get rid of custom gesture handling --- MTMR/CustomButtonTouchBarItem.swift | 2 + MTMR/Widgets/AppScrubberTouchBarItem.swift | 231 ++++++++------------- 2 files changed, 85 insertions(+), 148 deletions(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index 5ae98de..e56356f 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -15,6 +15,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat longClick.isEnabled = longTapClosure != nil } } + var finishViewConfiguration: ()->() = {} private var button: NSButton! private var singleClick: HapticClickGestureRecognizer! @@ -100,6 +101,7 @@ class CustomButtonTouchBarItem: NSCustomTouchBarItem, NSGestureRecognizerDelegat view.addGestureRecognizer(longClick) view.addGestureRecognizer(singleClick) + finishViewConfiguration() } func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool { diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index b5c4d13..8428bb4 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -7,14 +7,8 @@ import Cocoa -class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrubberDataSource { - private var scrubber: NSScrubber! - - private var timer: Timer! - private var ticks: Int = 0 - private let minTicks: Int = 5 - private let maxTicks: Int = 20 - private var lastSelected: Int = 0 +class AppScrubberTouchBarItem: NSCustomTouchBarItem { + private var scrollView = NSScrollView() private var autoResize: Bool = false private var widthConstraint: NSLayoutConstraint? @@ -22,37 +16,16 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub private var runningAppsIdentifiers: [String] = [] private var frontmostApplicationIdentifier: String? { - guard let frontmostId = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return nil } - return frontmostId + return NSWorkspace.shared.frontmostApplication?.bundleIdentifier } private var applications: [DockItem] = [] - - convenience override init(identifier: NSTouchBarItem.Identifier) { - self.init(identifier: identifier, autoResize: false) - } - - static var iconWidth = 36 - static var spacingWidth = 2 + private var items: [CustomButtonTouchBarItem] = [] - init(identifier: NSTouchBarItem.Identifier, autoResize: Bool) { + init(identifier: NSTouchBarItem.Identifier, autoResize: Bool = false) { super.init(identifier: identifier) - self.autoResize = autoResize - - scrubber = NSScrubber() - scrubber.delegate = self - scrubber.dataSource = self - scrubber.mode = .free // .fixed - let layout = NSScrubberFlowLayout() - layout.itemSize = NSSize(width: AppScrubberTouchBarItem.iconWidth, height: 32) - layout.itemSpacing = CGFloat(AppScrubberTouchBarItem.spacingWidth) - scrubber.scrubberLayout = layout - scrubber.selectionBackgroundStyle = .roundedBackground - scrubber.showsAdditionalContentIndicators = true - - view = scrubber - - scrubber.register(NSScrubberImageItemView.self, forItemIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier")) + self.autoResize = autoResize //todo + view = scrollView NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil) NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didTerminateApplicationNotification, object: nil) @@ -75,111 +48,52 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub } func updateRunningApplication() { - let newApplications = launchedApplications() - - let index = newApplications.firstIndex { - $0.bundleIdentifier == frontmostApplicationIdentifier - } - - applications = newApplications + applications = launchedApplications() applications += getDockPersistentAppsList() - scrubber.reloadData() + reloadData() updateSize() - - scrubber.selectedIndex = index ?? 0 } func updateSize() { if self.autoResize { - if let constraint: NSLayoutConstraint = self.widthConstraint { - constraint.isActive = false - self.scrubber.removeConstraint(constraint) - } - let width = (AppScrubberTouchBarItem.iconWidth + AppScrubberTouchBarItem.spacingWidth) * self.applications.count - AppScrubberTouchBarItem.spacingWidth - self.widthConstraint = self.scrubber.widthAnchor.constraint(equalToConstant: CGFloat(width)) + self.widthConstraint?.isActive = false + + let width = self.scrollView.documentView?.fittingSize.width ?? 0 + self.widthConstraint = self.scrollView.widthAnchor.constraint(equalToConstant: width) self.widthConstraint!.isActive = true } } - - public func numberOfItems(for _: NSScrubber) -> Int { - return applications.count + + func reloadData() { + let frontMostAppId = self.frontmostApplicationIdentifier + items = applications.map { self.createAppButton(for: $0, isFrontmost: $0.bundleIdentifier == frontMostAppId) } + let stackView = NSStackView(views: items.compactMap { $0.view }) + stackView.spacing = 1 + stackView.orientation = .horizontal + let visibleRect = self.scrollView.documentVisibleRect + scrollView.documentView = stackView + stackView.scroll(visibleRect.origin) } - public func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView { - let item = scrubber.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "ScrubberApplicationsItemReuseIdentifier"), owner: self) as? NSScrubberImageItemView ?? NSScrubberImageItemView() - item.imageView.imageScaling = .scaleProportionallyDown - - let app = applications[index] - - if let icon = app.icon { - item.image = icon - item.image.size = NSSize(width: 26, height: 26) + public func createAppButton(for app: DockItem, isFrontmost: Bool) -> CustomButtonTouchBarItem { + let item = DockBarItem(app, isRunning: runningAppsIdentifiers.contains(app.bundleIdentifier), isFrontmost: isFrontmost) + item.isBordered = false + item.tapClosure = { [weak self] in + self?.switchToApp(app: app) } - - item.removeFromSuperview() - - let dotView = NSView(frame: .zero) - dotView.wantsLayer = true - if runningAppsIdentifiers.contains(app.bundleIdentifier!) { - dotView.layer?.backgroundColor = NSColor.white.cgColor - } else { - dotView.layer?.backgroundColor = NSColor.black.cgColor + item.longTapClosure = { [weak self] in + self?.handleHalfLongPress(item: app) } - dotView.layer?.cornerRadius = 1.5 - dotView.setFrameOrigin(NSPoint(x: 17, y: 1)) - dotView.frame.size = NSSize(width: 3, height: 3) - item.addSubview(dotView) - + return item } - - public func didBeginInteracting(with _: NSScrubber) { - stopTimer() - ticks = 0 - timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkTimer), userInfo: nil, repeats: true) - } - - @objc private func checkTimer() { - ticks += 1 - - if ticks == minTicks { - HapticFeedback.shared?.tap(strong: 2) - } - - if ticks > maxTicks { - stopTimer() - HapticFeedback.shared?.tap(strong: 6) - } - } - - private func stopTimer() { - timer?.invalidate() - timer = nil - lastSelected = 0 - } - - public func didCancelInteracting(with _: NSScrubber) { - stopTimer() - } - - public func didFinishInteracting(with scrubber: NSScrubber) { - stopTimer() - - if ticks == 0 { - return - } - - if ticks >= minTicks && scrubber.selectedIndex > 0 { - longPress(selected: scrubber.selectedIndex) - return - } - - let bundleIdentifier = applications[scrubber.selectedIndex].bundleIdentifier + + public func switchToApp(app: DockItem) { + let bundleIdentifier = app.bundleIdentifier if bundleIdentifier!.contains("file://") { NSWorkspace.shared.openFile(bundleIdentifier!.replacingOccurrences(of: "file://", with: "")) } else { NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil) - HapticFeedback.shared?.tap(strong: 6) } updateRunningApplication() @@ -187,30 +101,27 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub // "When switching to an application, switch to a Space with open windows for the application" // in Mission control settings } - - private func longPress(selected: Int) { - let bundleIdentifier = applications[selected].bundleIdentifier - - if ticks > maxTicks { - if let processIdentifier = applications[selected].pid { - if !(NSRunningApplication(processIdentifier: processIdentifier)?.terminate())! { - NSRunningApplication(processIdentifier: processIdentifier)?.forceTerminate() - } + + //todo + private func handleLongPress(item: DockItem) { + if let pid = item.pid, let app = NSRunningApplication(processIdentifier: pid) { + if !app.terminate() { + app.forceTerminate() } - } else { - HapticFeedback.shared?.tap(strong: 6) - if let index = self.persistentAppIdentifiers.firstIndex(of: bundleIdentifier!) { - persistentAppIdentifiers.remove(at: index) - } else { - persistentAppIdentifiers.append(bundleIdentifier!) - } - - AppSettings.dockPersistentAppIds = persistentAppIdentifiers + updateRunningApplication() } - ticks = 0 - updateRunningApplication() } + + private func handleHalfLongPress(item: DockItem) { + if let index = self.persistentAppIdentifiers.firstIndex(of: item.bundleIdentifier) { + persistentAppIdentifiers.remove(at: index) + } else { + persistentAppIdentifiers.append(item.bundleIdentifier) + } + AppSettings.dockPersistentAppIds = persistentAppIdentifiers + } + private func launchedApplications() -> [DockItem] { runningAppsIdentifiers = [] var returnable: [DockItem] = [] @@ -220,21 +131,19 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem, NSScrubberDelegate, NSScrub runningAppsIdentifiers.append(bundleIdentifier) - let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier) + let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: app.icon ?? getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier) returnable.append(dockItem) } return returnable } - public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil, orType _: String? = nil) -> NSImage { - if bundleIdentifier != nil { - if let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier!) { - return NSWorkspace.shared.icon(forFile: appPath) - } + public func getIcon(forBundleIdentifier bundleIdentifier: String? = nil, orPath path: String? = nil) -> NSImage { + if let bundleIdentifier = bundleIdentifier, let appPath = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier) { + return NSWorkspace.shared.icon(forFile: appPath) } - if path != nil { - return NSWorkspace.shared.icon(forFile: path!) + if let path = path { + return NSWorkspace.shared.icon(forFile: path) } let genericIcon = NSImage(contentsOfFile: "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericDocumentIcon.icns") @@ -265,3 +174,29 @@ public class DockItem: NSObject { self.pid = pid } } + +private let iconWidth = 32.0 +class DockBarItem: CustomButtonTouchBarItem { + + init(_ app: DockItem, isRunning: Bool, isFrontmost: Bool) { + super.init(identifier: .init(app.bundleIdentifier), title: "") + + image = app.icon + image?.size = NSSize(width: iconWidth, height: iconWidth) + + let dotColor: NSColor = isRunning ? .white : .black + self.finishViewConfiguration = { [weak self] in + let dotView = NSView(frame: .zero) + dotView.wantsLayer = true + dotView.layer?.backgroundColor = dotColor.cgColor + dotView.layer?.cornerRadius = 1.5 + dotView.frame.size = NSSize(width: isFrontmost ? iconWidth - 14 : 3, height: 3) + self?.view.addSubview(dotView) + dotView.setFrameOrigin(NSPoint(x: 18.0 - Double(dotView.frame.size.width) / 2.0, y: iconWidth - 5)) + } + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} From dd23a3bda84794c1ff612f0d13991f1a71aaf4b3 Mon Sep 17 00:00:00 2001 From: Serg Date: Sat, 26 Oct 2019 22:43:01 +0700 Subject: [PATCH 3/5] don't reload list of apps every time --- MTMR/Widgets/AppScrubberTouchBarItem.swift | 71 ++++++++++++++-------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index 8428bb4..b6430f6 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -20,40 +20,42 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { } private var applications: [DockItem] = [] - private var items: [CustomButtonTouchBarItem] = [] + private var items: [DockBarItem] = [] init(identifier: NSTouchBarItem.Identifier, autoResize: Bool = false) { super.init(identifier: identifier) self.autoResize = autoResize //todo view = scrollView - NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didLaunchApplicationNotification, object: nil) - NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didTerminateApplicationNotification, object: nil) - NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(activeApplicationChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil) + NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil) + NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didTerminateApplicationNotification, object: nil) + NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(softReloadItems), name: NSWorkspace.didActivateApplicationNotification, object: nil) persistentAppIdentifiers = AppSettings.dockPersistentAppIds - updateRunningApplication() + hardReloadItems() } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } - @objc func activeApplicationChanged(n _: Notification) { - updateRunningApplication() - } - - override func observeValue(forKeyPath _: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) { - updateRunningApplication() - } - - func updateRunningApplication() { + @objc func hardReloadItems() { applications = launchedApplications() applications += getDockPersistentAppsList() reloadData() updateSize() } + @objc func softReloadItems() { + let frontMostAppId = self.frontmostApplicationIdentifier + let runningAppsIds = NSWorkspace.shared.runningApplications.map { $0.bundleIdentifier } + for barItem in items { + let bundleId = barItem.dockItem.bundleIdentifier + barItem.isRunning = runningAppsIds.contains(bundleId) + barItem.isFrontmost = frontMostAppId == bundleId + } + } + func updateSize() { if self.autoResize { self.widthConstraint?.isActive = false @@ -75,7 +77,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { stackView.scroll(visibleRect.origin) } - public func createAppButton(for app: DockItem, isFrontmost: Bool) -> CustomButtonTouchBarItem { + public func createAppButton(for app: DockItem, isFrontmost: Bool) -> DockBarItem { let item = DockBarItem(app, isRunning: runningAppsIdentifiers.contains(app.bundleIdentifier), isFrontmost: isFrontmost) item.isBordered = false item.tapClosure = { [weak self] in @@ -95,7 +97,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { } else { NSWorkspace.shared.launchApplication(withBundleIdentifier: bundleIdentifier!, options: [.default], additionalEventParamDescriptor: nil, launchIdentifier: nil) } - updateRunningApplication() + softReloadItems() // NB: if you can't open app which on another space, try to check mark // "When switching to an application, switch to a Space with open windows for the application" @@ -108,7 +110,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { if !app.terminate() { app.forceTerminate() } - updateRunningApplication() + hardReloadItems() } } @@ -177,25 +179,44 @@ public class DockItem: NSObject { private let iconWidth = 32.0 class DockBarItem: CustomButtonTouchBarItem { + let dotView = NSView(frame: .zero) + let dockItem: DockItem + + var isRunning = false { + didSet { + redrawDotView() + } + } + + var isFrontmost = false { + didSet { + redrawDotView() + } + } init(_ app: DockItem, isRunning: Bool, isFrontmost: Bool) { + self.dockItem = app super.init(identifier: .init(app.bundleIdentifier), title: "") + dotView.wantsLayer = true + self.isRunning = isRunning image = app.icon image?.size = NSSize(width: iconWidth, height: iconWidth) - let dotColor: NSColor = isRunning ? .white : .black self.finishViewConfiguration = { [weak self] in - let dotView = NSView(frame: .zero) - dotView.wantsLayer = true - dotView.layer?.backgroundColor = dotColor.cgColor - dotView.layer?.cornerRadius = 1.5 - dotView.frame.size = NSSize(width: isFrontmost ? iconWidth - 14 : 3, height: 3) - self?.view.addSubview(dotView) - dotView.setFrameOrigin(NSPoint(x: 18.0 - Double(dotView.frame.size.width) / 2.0, y: iconWidth - 5)) + guard let selfie = self else { return } + selfie.dotView.layer?.cornerRadius = 1.5 + selfie.view.addSubview(selfie.dotView) + selfie.redrawDotView() } } + func redrawDotView() { + dotView.layer?.backgroundColor = isRunning ? NSColor.white.cgColor : NSColor.clear.cgColor + dotView.frame.size = NSSize(width: isFrontmost ? iconWidth - 14 : 3, height: 3) + dotView.setFrameOrigin(NSPoint(x: 18.0 - Double(dotView.frame.size.width) / 2.0, y: iconWidth - 5)) + } + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } From e9a7b6d32a1c607b8b138216d01299b6c3dd7a59 Mon Sep 17 00:00:00 2001 From: Serg Date: Sun, 27 Oct 2019 13:01:22 +0700 Subject: [PATCH 4/5] get long tap to kill back into place --- MTMR/CustomButtonTouchBarItem.swift | 2 +- MTMR/Widgets/AppScrubberTouchBarItem.swift | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/MTMR/CustomButtonTouchBarItem.swift b/MTMR/CustomButtonTouchBarItem.swift index e56356f..6b734d2 100644 --- a/MTMR/CustomButtonTouchBarItem.swift +++ b/MTMR/CustomButtonTouchBarItem.swift @@ -189,7 +189,7 @@ class HapticClickGestureRecognizer: NSClickGestureRecognizer { } class LongPressGestureRecognizer: NSPressGestureRecognizer { - private let recognizeTimeout = 0.4 + var recognizeTimeout = 0.4 private var timer: Timer? override func touchesBegan(with event: NSEvent) { diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index b6430f6..95b341b 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -86,6 +86,9 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { item.longTapClosure = { [weak self] in self?.handleHalfLongPress(item: app) } + item.killAppClosure = {[weak self] in + self?.handleLongPress(item: app) + } return item } @@ -117,6 +120,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { private func handleHalfLongPress(item: DockItem) { if let index = self.persistentAppIdentifiers.firstIndex(of: item.bundleIdentifier) { persistentAppIdentifiers.remove(at: index) + hardReloadItems() } else { persistentAppIdentifiers.append(item.bundleIdentifier) } @@ -181,6 +185,8 @@ private let iconWidth = 32.0 class DockBarItem: CustomButtonTouchBarItem { let dotView = NSView(frame: .zero) let dockItem: DockItem + fileprivate var killGestureRecognizer: LongPressGestureRecognizer! + var killAppClosure: () -> Void = { } var isRunning = false { didSet { @@ -203,11 +209,18 @@ class DockBarItem: CustomButtonTouchBarItem { image = app.icon image?.size = NSSize(width: iconWidth, height: iconWidth) + killGestureRecognizer = LongPressGestureRecognizer(target: self, action: #selector(firePanGestureRecognizer)) + killGestureRecognizer.allowedTouchTypes = .direct + killGestureRecognizer.recognizeTimeout = 1.5 + killGestureRecognizer.minimumPressDuration = 1.5 + killGestureRecognizer.isEnabled = isRunning + self.finishViewConfiguration = { [weak self] in guard let selfie = self else { return } selfie.dotView.layer?.cornerRadius = 1.5 selfie.view.addSubview(selfie.dotView) selfie.redrawDotView() + selfie.view.addGestureRecognizer(selfie.killGestureRecognizer) } } @@ -217,6 +230,10 @@ class DockBarItem: CustomButtonTouchBarItem { dotView.setFrameOrigin(NSPoint(x: 18.0 - Double(dotView.frame.size.width) / 2.0, y: iconWidth - 5)) } + @objc func firePanGestureRecognizer() { + self.killAppClosure() + } + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } From 6920664fad600d7122cf4edca4e5f45142711274 Mon Sep 17 00:00:00 2001 From: Serg Date: Sun, 27 Oct 2019 15:29:52 +0700 Subject: [PATCH 5/5] implement regex filter for dock items --- MTMR/ItemsParsing.swift | 18 ++++------------ MTMR/TouchBarController.swift | 14 ++++++++++--- MTMR/Widgets/AppScrubberTouchBarItem.swift | 24 ++++++++++++++-------- README.md | 1 + 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift index ac43767..ab9b5ae 100644 --- a/MTMR/ItemsParsing.swift +++ b/MTMR/ItemsParsing.swift @@ -211,18 +211,6 @@ class SupportedTypesHolder { ) }, - "dock": { decoder in - enum CodingKeys: String, CodingKey { case autoResize } - let container = try decoder.container(keyedBy: CodingKeys.self) - let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false - return ( - item: .dock(autoResize: autoResize), - action: .none, - longAction: .none, - parameters: [:] - ) - }, - "inputsource": { _ in ( item: .inputsource, @@ -346,7 +334,7 @@ enum ItemType: Decodable { case shellScriptTitledButton(source: SourceProtocol, refreshInterval: Double) case timeButton(formatTemplate: String, timeZone: String?, locale: String?) case battery - case dock(autoResize: Bool) + case dock(autoResize: Bool, filter: String?) case volume case brightness(refreshInterval: Double) case weather(interval: Double, units: String, api_key: String, icon_type: String) @@ -383,6 +371,7 @@ enum ItemType: Decodable { case restTime case flip case autoResize + case filter case disableMarquee } @@ -437,7 +426,8 @@ enum ItemType: Decodable { case .dock: let autoResize = try container.decodeIfPresent(Bool.self, forKey: .autoResize) ?? false - self = .dock(autoResize: autoResize) + let filterRegexString = try container.decodeIfPresent(String.self, forKey: .filter) + self = .dock(autoResize: autoResize, filter: filterRegexString) case .volume: self = .volume diff --git a/MTMR/TouchBarController.swift b/MTMR/TouchBarController.swift index 941711d..6ff9a5e 100644 --- a/MTMR/TouchBarController.swift +++ b/MTMR/TouchBarController.swift @@ -29,7 +29,7 @@ extension ItemType { return "com.toxblh.mtmr.timeButton." case .battery: return "com.toxblh.mtmr.battery." - case .dock(autoResize: _): + case .dock(autoResize: _, filter: _): return "com.toxblh.mtmr.dock" case .volume: return "com.toxblh.mtmr.volume" @@ -248,8 +248,16 @@ class TouchBarController: NSObject, NSTouchBarDelegate { barItem = TimeTouchBarItem(identifier: identifier, formatTemplate: template, timeZone: timeZone, locale: locale) case .battery: barItem = BatteryBarItem(identifier: identifier) - case let .dock(autoResize: autoResize): - barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize) + case let .dock(autoResize: autoResize, filter: regexString): + if let regexString = regexString { + guard let regex = try? NSRegularExpression(pattern: regexString, options: []) else { + barItem = CustomButtonTouchBarItem(identifier: identifier, title: "Bad regex") + break + } + barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize, filter: regex) + } else { + barItem = AppScrubberTouchBarItem(identifier: identifier, autoResize: autoResize) + } case .volume: if case let .image(source)? = item.additionalParameters[.image] { barItem = VolumeViewController(identifier: identifier, image: source.image) diff --git a/MTMR/Widgets/AppScrubberTouchBarItem.swift b/MTMR/Widgets/AppScrubberTouchBarItem.swift index 95b341b..adc5541 100644 --- a/MTMR/Widgets/AppScrubberTouchBarItem.swift +++ b/MTMR/Widgets/AppScrubberTouchBarItem.swift @@ -11,6 +11,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { private var scrollView = NSScrollView() private var autoResize: Bool = false private var widthConstraint: NSLayoutConstraint? + private let filter: NSRegularExpression? private var persistentAppIdentifiers: [String] = [] private var runningAppsIdentifiers: [String] = [] @@ -22,9 +23,10 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { private var applications: [DockItem] = [] private var items: [DockBarItem] = [] - init(identifier: NSTouchBarItem.Identifier, autoResize: Bool = false) { + init(identifier: NSTouchBarItem.Identifier, autoResize: Bool = false, filter: NSRegularExpression? = nil) { + self.filter = filter super.init(identifier: identifier) - self.autoResize = autoResize //todo + self.autoResize = autoResize view = scrollView NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(hardReloadItems), name: NSWorkspace.didLaunchApplicationNotification, object: nil) @@ -43,6 +45,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { applications = launchedApplications() applications += getDockPersistentAppsList() reloadData() + softReloadItems() updateSize() } @@ -67,8 +70,7 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { } func reloadData() { - let frontMostAppId = self.frontmostApplicationIdentifier - items = applications.map { self.createAppButton(for: $0, isFrontmost: $0.bundleIdentifier == frontMostAppId) } + items = applications.map { self.createAppButton(for: $0) } let stackView = NSStackView(views: items.compactMap { $0.view }) stackView.spacing = 1 stackView.orientation = .horizontal @@ -77,8 +79,8 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { stackView.scroll(visibleRect.origin) } - public func createAppButton(for app: DockItem, isFrontmost: Bool) -> DockBarItem { - let item = DockBarItem(app, isRunning: runningAppsIdentifiers.contains(app.bundleIdentifier), isFrontmost: isFrontmost) + public func createAppButton(for app: DockItem) -> DockBarItem { + let item = DockBarItem(app) item.isBordered = false item.tapClosure = { [weak self] in self?.switchToApp(app: app) @@ -134,7 +136,12 @@ class AppScrubberTouchBarItem: NSCustomTouchBarItem { for app in NSWorkspace.shared.runningApplications { guard app.activationPolicy == NSApplication.ActivationPolicy.regular else { continue } guard let bundleIdentifier = app.bundleIdentifier else { continue } - + if let filter = self.filter, + let name = app.localizedName, + filter.numberOfMatches(in: name, options: [], range: NSRange(location: 0, length: name.count)) == 0 { + continue + } + runningAppsIdentifiers.append(bundleIdentifier) let dockItem = DockItem(bundleIdentifier: bundleIdentifier, icon: app.icon ?? getIcon(forBundleIdentifier: bundleIdentifier), pid: app.processIdentifier) @@ -200,11 +207,10 @@ class DockBarItem: CustomButtonTouchBarItem { } } - init(_ app: DockItem, isRunning: Bool, isFrontmost: Bool) { + init(_ app: DockItem) { self.dockItem = app super.init(identifier: .init(app.bundleIdentifier), title: "") dotView.wantsLayer = true - self.isRunning = isRunning image = app.icon image?.size = NSSize(width: iconWidth, height: iconWidth) diff --git a/README.md b/README.md index c895537..2bab332 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,7 @@ To close a group, use the button: ```js { "type": "dock", + "filter": "(^Xcode$)|(Safari)|(.*player)", "autoResize": true }, ```