feat: navigate with up/down arrow keys (closes #270)

This commit is contained in:
Louis Pontoise authored and lwouis committed 2020-04-27 19:01:51 +09:00
1 parent d64a6a520a
commit cc61ed4607
4 files changed
+65 -16

No files matched your search

+1
View File
@@ -14,6 +14,7 @@ class Window {
var axUiElement: AXUIElement
var application: Application
var axObserver: AXObserver?
var row: Int?
static let notifications = [
kAXUIElementDestroyedNotification,
+11 -7
View File
@@ -134,11 +134,15 @@ class App: NSApplication, NSApplicationDelegate {
func showUi() {
appIsBeingUsed = true
appIsBeingUsed = true
DispatchQueue.main.async { () -> () in self.showUiOrCycleSelection(0) }
DispatchQueue.main.async { () -> () in self.showUiOrCycleSelection(.neutral) }
}
func cycleSelection(_ step: Int) {
Windows.cycleFocusedWindowIndex(step)
func cycleSelection(_ direction: Direction) {
if direction == .left || direction == .right || direction == .neutral {
Windows.cycleFocusedWindowIndex(direction.step())
} else {
thumbnailsPanel.thumbnailsView.navigateUpOrDown(direction)
}
}
func focusSelectedWindow(_ window: Window?) {
@@ -183,8 +187,8 @@ class App: NSApplication, NSApplicationDelegate {
}
}
func showUiOrCycleSelection(_ step: Int) {
debugPrint("showUiOrCycleSelection", step)
func showUiOrCycleSelection(_ direction: Direction) {
debugPrint("showUiOrCycleSelection", direction)
if isFirstSummon {
debugPrint("showUiOrCycleSelection: isFirstSummon")
isFirstSummon = false
@@ -197,12 +201,12 @@ class App: NSApplication, NSApplicationDelegate {
Windows.refreshWhichWindowsToShowTheUser(screen)
if Windows.list.first(where: { $0.shouldShowTheUser }) == nil { hideUi(); return }
Windows.updateFocusedWindowIndex(0)
Windows.cycleFocusedWindowIndex(step)
Windows.cycleFocusedWindowIndex(direction.step())
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Preferences.windowDisplayDelay) { () -> () in
self.rebuildUi()
}
} else {
cycleSelection(step)
cycleSelection(direction)
}
}
+43
View File
@@ -3,6 +3,7 @@ import Cocoa
class ThumbnailsView: NSVisualEffectView {
let scrollView = ScrollView()
static var recycledViews = [ThumbnailView]()
var rows = [[ThumbnailView]]()
convenience init() {
self.init(frame: .zero)
@@ -15,6 +16,26 @@ class ThumbnailsView: NSVisualEffectView {
(1...100).forEach { _ in ThumbnailsView.recycledViews.append(ThumbnailView()) }
}
func nextRow(_ direction: Direction) -> [ThumbnailView] {
let step = direction == .down ? 1 : -1
let indexAfterStep = (Windows.focusedWindow()!.row! + step) % rows.count
let targetRowIndex = indexAfterStep < 0 ? rows.count + indexAfterStep : indexAfterStep
return rows[targetRowIndex]
}
func navigateUpOrDown(_ direction: Direction) {
let focusedViewFrame = ThumbnailsView.recycledViews[Windows.focusedWindowIndex].frame
let originCenter = NSMidX(focusedViewFrame)
let targetRow = nextRow(direction)
let leftSide = originCenter < NSMidX(frame)
let iterable = leftSide ? targetRow : targetRow.reversed()
let targetView = iterable.first(where: {
leftSide ? NSMaxX($0.frame) > originCenter : NSMinX($0.frame) < originCenter
}) ?? iterable.last!
let targetIndex = ThumbnailsView.recycledViews.firstIndex(of: targetView)!
Windows.updateFocusedWindowIndex(targetIndex)
}
func updateItems(_ screen: NSScreen) {
let widthMax = ThumbnailsPanel.widthMax(screen).rounded()
let heightMax = ThumbnailsPanel.heightMax(screen).rounded()
@@ -24,6 +45,8 @@ class ThumbnailsView: NSVisualEffectView {
var maxX = CGFloat(0)
var maxY = height
var newViews = [ThumbnailView]()
rows.removeAll()
rows.append([ThumbnailView]())
for (index, window) in Windows.list.enumerated() {
guard App.app.appIsBeingUsed else { return }
guard window.shouldShowTheUser else { continue }
@@ -37,12 +60,15 @@ class ThumbnailsView: NSVisualEffectView {
currentX = CGFloat(0)
currentY = (currentY + Preferences.interCellPadding + height).rounded(.down)
maxY = max(currentY + height, maxY)
rows.append([ThumbnailView]())
} else {
maxX = max(currentX + width, maxX)
}
view.frame.origin = CGPoint(x: currentX, y: currentY)
currentX = (currentX + Preferences.interCellPadding + width).rounded(.down)
newViews.append(view)
rows[rows.count - 1].append(view)
window.row = rows.count - 1
}
scrollView.documentView!.subviews = newViews
frame.size = NSSize(width: min(maxX, widthMax) + Preferences.windowPadding * 2, height: min(maxY, heightMax) + Preferences.windowPadding * 2)
@@ -126,3 +152,20 @@ class ScrollView: NSScrollView {
class FlippedView: NSView {
override var isFlipped: Bool { true }
}
enum Direction {
case neutral
case left
case right
case up
case down
func step() -> Int {
if self == .left {
return -1
} else if self == .right {
return 1
}
return 0
}
}
@@ -11,10 +11,12 @@ class GeneralTab: NSViewController, PreferencePane {
static var shortcutsDependentOnHoldShortcut = [NSControl]()
static var shortcutsActionsBlocks = [
"holdShortcut": { App.app.focusTarget() },
"nextWindowShortcut": { App.app.showUiOrCycleSelection(1) },
"previousWindowShortcut": { App.app.showUiOrCycleSelection(-1) },
"→": { App.app.cycleSelection(1) },
"←": { App.app.cycleSelection(-1) },
"nextWindowShortcut": { App.app.showUiOrCycleSelection(.right) },
"previousWindowShortcut": { App.app.showUiOrCycleSelection(.left) },
"→": { App.app.cycleSelection(.right) },
"←": { App.app.cycleSelection(.left) },
"↑": { App.app.cycleSelection(.up) },
"↓": { App.app.cycleSelection(.down) },
"cancelShortcut": { App.app.hideUi() },
"closeWindowShortcut": { App.app.closeSelectedWindow() },
"minDeminWindowShortcut": { App.app.minDeminSelectedWindow() },
@@ -86,7 +88,7 @@ class GeneralTab: NSViewController, PreferencePane {
App.app.isFirstSummon = true
}
let fn = shortcutsActionsBlocks[controlId]!
let isShortcutAffectingTheUi = ["previousWindowShortcut", "nextWindowShortcut", "←", "→", "holdShortcut"].contains(controlId)
let isShortcutAffectingTheUi = ["previousWindowShortcut", "nextWindowShortcut", "←", "→", "↑", "↓", "holdShortcut"].contains(controlId)
if isShortcutAffectingTheUi || isShortcutClosingTheUi {
DispatchQueue.main.async { () -> () in fn() }
} else {
@@ -115,12 +117,11 @@ class GeneralTab: NSViewController, PreferencePane {
}
@objc static func arrowKeysEnabledCallback(_ sender: NSControl) {
let keys = ["←", "→", "↑", "↓"]
if (sender as! NSButton).state == .on {
addShortcut(.down, Shortcut(keyEquivalent: Preferences.holdShortcut + "→")!, "→")
addShortcut(.down, Shortcut(keyEquivalent: Preferences.holdShortcut + "←")!, "←")
keys.forEach { addShortcut(.down, Shortcut(keyEquivalent: Preferences.holdShortcut + $0)!, $0) }
} else {
removeShortcutIfExists("→", .down)
removeShortcutIfExists("←", .down)
keys.forEach { removeShortcutIfExists($0, .down) }
}
}