Skip to content

Commit

Permalink
Workspace.mruWindows -> TreeNode.mostRecentChild
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Oct 3, 2023
1 parent a2caa1c commit 9ee93a4
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 44 deletions.
28 changes: 22 additions & 6 deletions src/command/FocusCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,35 @@ struct FocusCommand: Command { // todo speed up. Now it's slightly slow (probabl
})!
guard let parent = topMostChild.parent as? TilingContainer else { return }
precondition(parent.orientation == direction.orientation)
guard let index = topMostChild.ownIndexOrNil else { return }
let mruIndexMap = currentWindow.workspace.mruWindows.mruIndexMap
let windowToFocus: Window? = parent.children
.getOrNil(atIndex: index + direction.focusOffset)?
.allLeafWindowsRecursive(snappedTo: direction.opposite)
.minBy { mruIndexMap[$0] ?? Int.max }
let windowToFocus = parent.children
.getOrNil(atIndex: topMostChild.ownIndexOrNil! + direction.focusOffset)?
.findFocusTargetRecursive(snappedTo: direction.opposite)

windowToFocus?.focus()
} else {
// todo direction == .child || direction == .parent
}
}
}

private extension TreeNode {
func findFocusTargetRecursive(snappedTo direction: CardinalDirection) -> Window? {
switch kind {
case .workspace(let workspace):
return workspace.rootTilingContainer.findFocusTargetRecursive(snappedTo: direction)
case .window(let window):
return window
case .tilingContainer(let container):
if direction.orientation == container.orientation {
return (direction.isPositive ? container.children.last : container.children.first)?
.findFocusTargetRecursive(snappedTo: direction)
} else {
return mostRecentChild?.findFocusTargetRecursive(snappedTo: direction)
}
}
}
}

extension FocusCommand.Direction {
var cardinalOrNil: CardinalDirection? {
switch self {
Expand Down
23 changes: 7 additions & 16 deletions src/command/MoveThroughCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,43 +60,34 @@ private func moveOut(window: Window, direction: CardinalDirection) {
}

private func deepMoveIn(window: Window, into container: TilingContainer, moveDirection: CardinalDirection) {
let mruIndexMap = window.workspace.mruWindows.mruIndexMap
let preferredPath: [TreeNode] = container.allLeafWindowsRecursive
.minBy { mruIndexMap[$0] ?? Int.max }!
.parentsWithSelf
.reversed()
.drop(while: { $0 != container })
.dropFirst()
.toArray()
let deepTarget = container.findDeepMoveInTargetRecursive(moveDirection.orientation, preferredPath)
let deepTarget = container.findDeepMoveInTargetRecursive(moveDirection.orientation)
switch deepTarget.kind {
case .tilingContainer:
window.unbindFromParent()
window.bindTo(parent: deepTarget, adaptiveWeight: WEIGHT_AUTO, index: 0)
case .window(let target):
case .window(let deepTarget):
window.unbindFromParent()
window.bindTo(
parent: (target.parent as! TilingContainer),
parent: (deepTarget.parent as! TilingContainer),
adaptiveWeight: WEIGHT_AUTO,
index: target.ownIndex + 1
index: deepTarget.ownIndex + 1
)
case .workspace:
error("Impossible")
}
}

private extension TreeNode {
func findDeepMoveInTargetRecursive(_ orientation: Orientation, _ preferredPath: [TreeNode]) -> TreeNode {
func findDeepMoveInTargetRecursive(_ orientation: Orientation) -> TreeNode {
switch kind {
case .window:
return self
case .tilingContainer(let container):
if container.orientation == orientation {
return container
} else {
assert(children.contains(preferredPath.first!))
return preferredPath.first!
.findDeepMoveInTargetRecursive(orientation, Array(preferredPath.dropFirst()))
return (mostRecentChild ?? errorT("Empty containers must be detached during normalization"))
.findDeepMoveInTargetRecursive(orientation)
}
case .workspace:
error("Impossible")
Expand Down
2 changes: 1 addition & 1 deletion src/command/WorkspaceCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct WorkspaceCommand : Command {
precondition(Thread.current.isMainThread)
let workspace = Workspace.get(byName: workspaceName)
debug("Switch to workspace: \(workspace.name)")
if let window = workspace.mruWindows.mostRecent ?? workspace.anyLeafWindowRecursive { // switch to not empty workspace
if let window = workspace.mostRecentWindow ?? workspace.anyLeafWindowRecursive { // switch to not empty workspace
window.focus()
// The switching itself will be done by refreshWorkspaces and layoutWorkspaces later in refresh
} else { // switch to empty workspace
Expand Down
2 changes: 1 addition & 1 deletion src/focused.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ var focusedApp: AeroApp? {
var focusedWindow: Window? { focusedApp?.focusedWindow }

var focusedWindowOrEffectivelyFocused: Window? {
focusedWindow ?? Workspace.focused.mruWindows.mostRecent ?? Workspace.focused.anyLeafWindowRecursive
focusedWindow ?? Workspace.focused.mostRecentWindow ?? Workspace.focused.anyLeafWindowRecursive
}
3 changes: 1 addition & 2 deletions src/refresh.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ func refreshObs(_ obs: AXObserver, ax: AXUIElement, notif: CFString, data: Unsaf
}

func updateLastActiveWindow() {
guard let window = focusedWindow else { return }
window.workspace.mruWindows.pushOrRaise(window)
focusedWindow?.markAsMostRecentChild()
}

private func refreshWorkspaces() {
Expand Down
4 changes: 2 additions & 2 deletions src/tree/MacWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class MacWindow: Window {
if shouldFloat || config.debugAllWindowsAreFloating {
parent = workspace
} else {
let tilingParent = workspace.mruWindows.mostRecent?.parent as? TilingContainer ?? workspace.rootTilingContainer
let tilingParent = workspace.mostRecentWindow?.parent as? TilingContainer ?? workspace.rootTilingContainer
parent = tilingParent
}
let window = MacWindow(id, app, axWindow, parent: parent, adaptiveWeight: WEIGHT_AUTO)
Expand Down Expand Up @@ -85,7 +85,7 @@ final class MacWindow: Window {
@discardableResult
override func focus() -> Bool {
if app.nsApp.activate(options: .activateIgnoringOtherApps) && axWindow.raise() {
workspace.mruWindows.pushOrRaise(self)
markAsMostRecentChild()
return true
} else {
return false
Expand Down
23 changes: 15 additions & 8 deletions src/tree/TreeNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class TreeNode: Equatable {
fileprivate weak var _parent: TreeNode? = nil
var parent: TreeNode? { _parent }
private var adaptiveWeight: CGFloat
private var _mostRecentChild: TreeNode?
var mostRecentChild: TreeNode? { _mostRecentChild ?? children.first }

init(parent: TreeNode, adaptiveWeight: CGFloat) {
self.adaptiveWeight = adaptiveWeight
Expand Down Expand Up @@ -90,12 +92,12 @@ class TreeNode: Equatable {
}
newParent._children.insert(self, at: index == -1 ? newParent._children.count : index)
_parent = newParent
markAsMostRecentChild()
if let window = anyLeafWindowRecursive {
let newParentWorkspace = newParent.workspace
newParentWorkspace.mruWindows.pushOrRaise(window)
newParentWorkspace.assignedMonitor = window.getCenter()?.monitorApproximation
//?? NSScreen.focusedMonitorOrNilIfDesktop // todo uncomment once Monitor mock is done
//?? errorT("Can't set assignedMonitor") // todo uncomment once Monitor mock is done
//?? NSScreen.focusedMonitorOrNilIfDesktop // todo uncomment once Monitor mock is done
//?? errorT("Can't set assignedMonitor") // todo uncomment once Monitor mock is done
// Update currentEmptyWorkspace since it's no longer effectively empty
if newParentWorkspace == currentEmptyWorkspace {
currentEmptyWorkspace = getOrCreateNextEmptyWorkspace()
Expand All @@ -107,13 +109,12 @@ class TreeNode: Equatable {
private func unbindIfPossible() -> PreviousBindingData? {
guard let _parent else { return nil }
let workspace = workspace
if let window = self as? Window {
// todo lock screen -> false assert (for some reasons all windows are placed into current active workspace)
// todo chrome close "cmd F" window with esc -> false assert
precondition(workspace.mruWindows.remove(window), "mru.remove \(window.title)")
}

let index = _parent._children.remove(element: self) ?? errorT("Can't find child in its parent")
// todo lock screen -> windows are reset
if _parent._mostRecentChild == self {
_parent._mostRecentChild = nil
}
self._parent = nil

if workspace.isEffectivelyEmpty { // It became empty
Expand All @@ -123,6 +124,12 @@ class TreeNode: Equatable {
return PreviousBindingData(adaptiveWeight: adaptiveWeight, index: index)
}

func markAsMostRecentChild() {
guard let _parent else { return }
_parent._mostRecentChild = self
_parent.markAsMostRecentChild()
}

@discardableResult
func unbindFromParent() -> PreviousBindingData {
unbindIfPossible() ?? errorT("\(self) is already unbinded")
Expand Down
4 changes: 4 additions & 0 deletions src/tree/TreeNodeEx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ extension TreeNode {
self as? Workspace ?? parent?.workspace ?? errorT("Unknown type \(Self.self)")
}

var mostRecentWindow: Window? {
self as? Window ?? mostRecentChild?.mostRecentWindow
}

func allLeafWindowsRecursive(snappedTo direction: CardinalDirection) -> [Window] {
switch kind {
case .workspace(let workspace):
Expand Down
2 changes: 0 additions & 2 deletions src/tree/Workspace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class Workspace: TreeNode, Hashable, Identifiable {
let name: String
var id: String { name } // satisfy Identifiable
var assignedMonitor: Monitor? = nil
var mruWindows: MruStack<Window> = MruStack()
//weak var lastActiveWindow: MacWindow?

private init(_ name: String) {
self.name = name
Expand Down
10 changes: 5 additions & 5 deletions test/command/FocusCommandTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,20 +84,20 @@ final class FocusCommandTest: XCTestCase {
}
}

XCTAssertEqual(workspace.mruWindows.mostRecent?.windowId, 3) // The latest binded
XCTAssertEqual(workspace.mostRecentWindow?.windowId, 3) // The latest binded
await FocusCommand(direction: .right).runWithoutRefresh()
XCTAssertEqual(focusedWindow?.windowId, 3)

startWindow.focus()
workspace.mruWindows.pushOrRaise(window2)
window2.markAsMostRecentChild()
await FocusCommand(direction: .right).runWithoutRefresh()
XCTAssertEqual(focusedWindow?.windowId, 2)

startWindow.focus()
workspace.mruWindows.pushOrRaise(window3)
workspace.mruWindows.pushOrRaise(unrelatedWindow)
window3.markAsMostRecentChild()
unrelatedWindow.markAsMostRecentChild()
await FocusCommand(direction: .right).runWithoutRefresh()
XCTAssertEqual(focusedWindow?.windowId, 3)
XCTAssertEqual(focusedWindow?.windowId, 2)
}

func testFocusOutsideOfTheContainer() async {
Expand Down
2 changes: 1 addition & 1 deletion test/command/MoveThroughCommandTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ final class MoveThroughCommandTest: XCTestCase {
TestWindow(id: 4, parent: $0)
}
}
root.workspace.mruWindows.pushOrRaise(window3)
window3.markAsMostRecentChild()

await MoveThroughCommand(direction: .right).runWithoutRefresh()
XCTAssertEqual(
Expand Down

0 comments on commit 9ee93a4

Please sign in to comment.