From b7c7ffc7fb0582f9959acc3c2974bd0b42079c80 Mon Sep 17 00:00:00 2001 From: Nikita Bobko Date: Sun, 1 Oct 2023 18:56:59 +0200 Subject: [PATCH] Cleanup: TreeNodeKind + MoveThroughCommand cleanup --- AeroSpace.xcodeproj/project.pbxproj | 4 ++ .../MoveContainerToWorkspaceCommand.swift | 9 +---- src/command/MoveThroughCommand.swift | 38 +++++++++++-------- src/tree/MacWindow.swift | 5 ++- src/tree/TreeNode.swift | 21 ++++++++-- src/tree/TreeNodeEx.swift | 28 +++++++++----- src/tree/TreeNodeKind.swift | 5 +++ src/tree/Window.swift | 4 ++ src/util/util.swift | 4 ++ test/command/MoveThroughCommandTest.swift | 9 ++--- 10 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 src/tree/TreeNodeKind.swift diff --git a/AeroSpace.xcodeproj/project.pbxproj b/AeroSpace.xcodeproj/project.pbxproj index 54a60ba1..7b2c5fa9 100644 --- a/AeroSpace.xcodeproj/project.pbxproj +++ b/AeroSpace.xcodeproj/project.pbxproj @@ -44,6 +44,7 @@ A2CBF9674964F9083BB198D2 /* ArrayEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 883D7F7F87FBE7D0BDE4E87F /* ArrayEx.swift */; }; A55F31B0CC357B37108B8F54 /* MoveContainerToWorkspaceCommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DBD002C4A68AED07BB63EFA /* MoveContainerToWorkspaceCommandTest.swift */; }; A5BFF75CF8021A585BC1F9D5 /* parseCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB03A4736BC3F6D19E4E69F3 /* parseCommand.swift */; }; + AC439551A737C73168CC5BF9 /* TreeNodeKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = A60709210745C60D64F82D53 /* TreeNodeKind.swift */; }; AC47DF43EFF42358F033C87C /* WorkspaceBackAndForthCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = EED7EE20910D7BE4D0150CED /* WorkspaceBackAndForthCommand.swift */; }; AE76A183D0454E4C8ADCE380 /* SequenceEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE5DCAEC5EE619CE33859E7 /* SequenceEx.swift */; }; B0D0C37BAE7E7F0D0FF1E9FC /* GlobalObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C2E5977331398421A4FC168 /* GlobalObserver.swift */; }; @@ -118,6 +119,7 @@ 99853C505D93E41F6531C324 /* CloseAllWindowsButCurrentCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseAllWindowsButCurrentCommand.swift; sourceTree = ""; }; 9D31BF26EAFA96F675D2C14B /* accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = accessibility.swift; sourceTree = ""; }; 9F6B8A501483ACBB62560101 /* TreeNodeEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeNodeEx.swift; sourceTree = ""; }; + A60709210745C60D64F82D53 /* TreeNodeKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeNodeKind.swift; sourceTree = ""; }; A9EDFD4A9F45182CA6E0BD7B /* OptionalEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalEx.swift; sourceTree = ""; }; AAE5DCAEC5EE619CE33859E7 /* SequenceEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceEx.swift; sourceTree = ""; }; AD1645D9939F3F896EF21393 /* TreeNodeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeNodeTest.swift; sourceTree = ""; }; @@ -184,6 +186,7 @@ C848D6E57FDF22AAF0FB45E6 /* TilingContainer.swift */, 3A262B442A94C1964509B691 /* TreeNode.swift */, 9F6B8A501483ACBB62560101 /* TreeNodeEx.swift */, + A60709210745C60D64F82D53 /* TreeNodeKind.swift */, 6352ADEE6625D9703CFCA99A /* Window.swift */, C3F068BCC50ED846DCBFDE57 /* Workspace.swift */, ); @@ -445,6 +448,7 @@ 56E72B24303F5F337B31B776 /* TrayMenuModel.swift in Sources */, F892B5DCB4F731B3E173FF4C /* TreeNode.swift in Sources */, 03D8ABD4597EDB00D79807E1 /* TreeNodeEx.swift in Sources */, + AC439551A737C73168CC5BF9 /* TreeNodeKind.swift in Sources */, 2CC46A952773F148AC398144 /* Window.swift in Sources */, BF16873111EEDE60A8AACD6B /* Workspace.swift in Sources */, AC47DF43EFF42358F033C87C /* WorkspaceBackAndForthCommand.swift in Sources */, diff --git a/src/command/MoveContainerToWorkspaceCommand.swift b/src/command/MoveContainerToWorkspaceCommand.swift index 26d1a5bb..138137fd 100644 --- a/src/command/MoveContainerToWorkspaceCommand.swift +++ b/src/command/MoveContainerToWorkspaceCommand.swift @@ -7,14 +7,7 @@ struct MoveContainerToWorkspaceCommand: Command { let targetWorkspace = Workspace.get(byName: targetWorkspaceName) let targetContainer = focused.isFloating ? targetWorkspace : targetWorkspace.rootTilingContainer focused.unbindFromParent() - let weight: CGFloat - if let targetContainer = targetContainer as? TilingContainer { - weight = targetContainer.children.sumOf { $0.getWeight(targetContainer.orientation) } - .div(targetContainer.children.count) ?? 1 - } else { - weight = 1 - } - focused.bindTo(parent: targetContainer, adaptiveWeight: weight) // todo different monitor + focused.bindTo(parent: targetContainer, adaptiveWeight: EVEN_ADAPTIVE_WEIGHT) // todo different monitor WorkspaceCommand(workspaceName: preserveWorkspace.name).runWithoutRefresh() } diff --git a/src/command/MoveThroughCommand.swift b/src/command/MoveThroughCommand.swift index 0ae6d594..8eaaf283 100644 --- a/src/command/MoveThroughCommand.swift +++ b/src/command/MoveThroughCommand.swift @@ -8,25 +8,30 @@ struct MoveThroughCommand: Command { let indexOfCurrent = parent.children.firstIndex(of: currentWindow) ?? errorT("Can't find child") let indexOfTarget = direction.isPositive ? indexOfCurrent + 1 : indexOfCurrent - 1 if parent.orientation == direction.orientation && parent.children.indices.contains(indexOfTarget) { - if let targetContainer = parent.children[indexOfTarget] as? TilingContainer { // "move in" + if let toplevelSiblingTargetContainer = parent.children[indexOfTarget] as? TilingContainer { // "move into container" let mruIndexMap = currentWindow.workspace.mruWindows.mruIndexMap - let recursive = targetContainer.allLeafWindowsRecursive - let reversed = recursive.minBy { mruIndexMap[$0] ?? Int.max }! + let preferredPath: [TreeNode] = toplevelSiblingTargetContainer.allLeafWindowsRecursive + .minBy { mruIndexMap[$0] ?? Int.max }! .parentsWithSelf .reversed() - let preferredPath: [TreeNode] = Array(Array(Array(reversed).drop(while: { $0 != targetContainer })).dropFirst()) - let pizdets = targetContainer.findNodeOrientRecursive(direction.orientation, preferredPath) - if pizdets is TilingContainer { + .drop(while: { $0 != toplevelSiblingTargetContainer }) + .dropFirst() + .toArray() + let target = toplevelSiblingTargetContainer.findNodeOrientRecursive(direction.orientation, preferredPath) + if target is TilingContainer { currentWindow.unbindFromParent() - currentWindow.bindTo(parent: pizdets, adaptiveWeight: 1, index: 0) // todo adaptiveWeight - } else if pizdets is Window { + currentWindow.bindTo(parent: target, adaptiveWeight: EVEN_ADAPTIVE_WEIGHT, index: 0) // todo adaptiveWeight + } else if target is Window { currentWindow.unbindFromParent() - currentWindow.bindTo(parent: (pizdets.parent as! TilingContainer), adaptiveWeight: 1, - index: pizdets.parent!.children.firstIndex(of: pizdets)! + 1) // todo mess + currentWindow.bindTo( + parent: (target.parent as! TilingContainer), + adaptiveWeight: EVEN_ADAPTIVE_WEIGHT, + index: target.parent!.children.firstIndex(of: target)! + 1 + ) // todo mess } else { error("Impossible") } - } else if parent.children[indexOfTarget] is Window { + } else if parent.children[indexOfTarget] is Window { // "swap windows" let prevBinding = currentWindow.unbindFromParent() currentWindow.bindTo(parent: parent, adaptiveWeight: prevBinding.adaptiveWeight, index: indexOfTarget) } else { @@ -43,9 +48,10 @@ struct MoveThroughCommand: Command { private extension TreeNode { func findNodeOrientRecursive(_ orientation: Orientation, _ preferredPath: [TreeNode]) -> TreeNode { - if let window = self as? Window { - return window - } else if let container = self as? TilingContainer { + switch kind { + case .window: + return self + case .tilingContainer(let container): if container.orientation == orientation { return container } else { @@ -53,8 +59,8 @@ private extension TreeNode { return preferredPath.first! .findNodeOrientRecursive(orientation, Array(preferredPath.dropFirst())) } - } else { + case .workspace: error("Impossible") } } -} \ No newline at end of file +} diff --git a/src/tree/MacWindow.swift b/src/tree/MacWindow.swift index 6507279c..44560c9f 100644 --- a/src/tree/MacWindow.swift +++ b/src/tree/MacWindow.swift @@ -139,7 +139,7 @@ final class MacWindow: Window { return prevUnhiddenEmulationPositionRelativeToWorkspaceAssignedRect != nil } - func setSize(_ size: CGSize) { + override func setSize(_ size: CGSize) { previousSize = getSize() axWindow.set(Ax.sizeAttr, size) } @@ -148,7 +148,7 @@ final class MacWindow: Window { axWindow.get(Ax.sizeAttr) } - func setTopLeftCorner(_ point: CGPoint) { + override func setTopLeftCorner(_ point: CGPoint) { axWindow.set(Ax.topLeftCornerAttr, point) } @@ -176,3 +176,4 @@ func shouldFloat(_ axWindow: AXUIElement) -> Bool { // todo } let FLOATING_ADAPTIVE_WEIGHT = CGFloat(-1) +let EVEN_ADAPTIVE_WEIGHT = CGFloat(-2) diff --git a/src/tree/TreeNode.swift b/src/tree/TreeNode.swift index 72c41faf..3a2c7cac 100644 --- a/src/tree/TreeNode.swift +++ b/src/tree/TreeNode.swift @@ -45,12 +45,12 @@ class TreeNode: Equatable { @discardableResult func bindTo(parent newParent: TreeNode, adaptiveWeight: CGFloat, index: Int = -1) -> PreviousBindingData? { - if newParent is Window { - error("Windows can't have children") - } if _parent === newParent { error("Binding to the same parent doesn't make sense") } + if newParent is Window { + error("Windows can't have children") + } let result = unbindIfPossible() if newParent === NilTreeNode.instance { @@ -58,7 +58,20 @@ class TreeNode: Equatable { } newParent._children.insert(self, at: index == -1 ? newParent._children.count : index) _parent = newParent - self.adaptiveWeight = adaptiveWeight + if adaptiveWeight == EVEN_ADAPTIVE_WEIGHT { + switch newParent.kind { + case .tilingContainer(let container): + self.adaptiveWeight = container.children.sumOf { $0.getWeight(container.orientation) } + .div(container.children.count) + ?? 1 + case .workspace: + self.adaptiveWeight = FLOATING_ADAPTIVE_WEIGHT + case .window: + error("Windows can't have children") + } + } else { + self.adaptiveWeight = adaptiveWeight + } if let window = self as? Window { let newParentWorkspace = newParent.workspace newParentWorkspace.mruWindows.pushOrRaise(window) diff --git a/src/tree/TreeNodeEx.swift b/src/tree/TreeNodeEx.swift index 44e6d1cc..4a6a0910 100644 --- a/src/tree/TreeNodeEx.swift +++ b/src/tree/TreeNodeEx.swift @@ -21,19 +21,18 @@ extension TreeNode { } func allLeafWindowsRecursive(snappedTo direction: CardinalDirection) -> [Window] { - if let workspace = self as? Workspace { + switch kind { + case .workspace(let workspace): return workspace.rootTilingContainer.allLeafWindowsRecursive(snappedTo: direction) - } else if let window = self as? Window { + case .window(let window): return [window] - } else if let container = self as? TilingContainer { + case .tilingContainer(let container): if direction.orientation == container.orientation { return (direction.isPositive ? container.children.last : container.children.first)? .allLeafWindowsRecursive(snappedTo: direction) ?? [] } else { return children.flatMap { $0.allLeafWindowsRecursive(snappedTo: direction) } } - } else { - error("Not supported TreeNode type: \(Self.self)") } } @@ -68,12 +67,13 @@ extension TreeNode { /// Containers' weights must be normalized before calling this function func layoutRecursive(_ _point: CGPoint, width: CGFloat, height: CGFloat) { - if let workspace = self as? Workspace { + switch kind { + case .workspace(let workspace): workspace.rootTilingContainer.layoutRecursive(_point, width: width, height: height) - } else if let window = self as? MacWindow { + case .window(let window): window.setTopLeftCorner(_point) window.setSize(CGSize(width: width, height: height)) - } else if let container = self as? TilingContainer { + case .tilingContainer(let container): var point = _point for child in container.children { switch container.layout { @@ -89,8 +89,18 @@ extension TreeNode { } } } + } + } + + var kind: TreeNodeKind { + if let window = self as? Window { + return .window(window) + } else if let workspace = self as? Workspace { + return .workspace(workspace) + } else if let tilingContainer = self as? TilingContainer { + return .tilingContainer(tilingContainer) } else { - error("Not supported TreeNode type: \(Self.self)") + error("Unknown tree") } } } diff --git a/src/tree/TreeNodeKind.swift b/src/tree/TreeNodeKind.swift new file mode 100644 index 00000000..80f71ad1 --- /dev/null +++ b/src/tree/TreeNodeKind.swift @@ -0,0 +1,5 @@ +enum TreeNodeKind { + case window(Window) + case tilingContainer(TilingContainer) + case workspace(Workspace) +} diff --git a/src/tree/Window.swift b/src/tree/Window.swift index 9838677b..6df709f3 100644 --- a/src/tree/Window.swift +++ b/src/tree/Window.swift @@ -11,6 +11,10 @@ class Window: TreeNode, Hashable { } var title: String? { error("Not implemented") } + + func setSize(_ size: CGSize) { error("Not implemented") } + + func setTopLeftCorner(_ point: CGPoint) { error("Not implemented") } } extension Window { diff --git a/src/util/util.swift b/src/util/util.swift index 3f1825a9..d5508aeb 100644 --- a/src/util/util.swift +++ b/src/util/util.swift @@ -34,6 +34,10 @@ extension Double { var squared: Double { self * self } } +extension Slice { + func toArray() -> [Base.Element] { Array(self) } +} + func -(a: CGPoint, b: CGPoint) -> CGPoint { CGPoint(x: a.x - b.x, y: a.y - b.y) } diff --git a/test/command/MoveThroughCommandTest.swift b/test/command/MoveThroughCommandTest.swift index 9249e060..16c70948 100644 --- a/test/command/MoveThroughCommandTest.swift +++ b/test/command/MoveThroughCommandTest.swift @@ -61,9 +61,10 @@ final class MoveThroughCommandTest: XCTestCase { extension TreeNode { var layoutDescription: LayoutDescription { - if let window = self as? Window { + switch kind { + case .window(let window): return .window(window.windowId) - } else if let container = self as? TilingContainer { + case .tilingContainer(let container): switch container.layout { case .List: switch container.orientation { @@ -80,10 +81,8 @@ extension TreeNode { return .v_accordion(container.children.map { $0.layoutDescription }) } } - } else if let workspace = self as? Workspace { + case .workspace: return .workspace(workspace.children.map { $0.layoutDescription }) - } else { - error("Unknown type: \(Self.self)") } } }