Skip to content

Commit

Permalink
FocusCommand: makeFloatingWindowsSeenAsTiling
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Oct 28, 2023
1 parent 8d416ea commit 2db32b4
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 5 deletions.
46 changes: 46 additions & 0 deletions src/command/FocusCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ struct FocusCommand: Command {
func runWithoutRefresh() {
check(Thread.current.isMainThread)
guard let currentWindow = focusedWindowOrEffectivelyFocused else { return }
let workspace = currentWindow.workspace
let floatingWindows = makeFloatingWindowsSeenAsTiling(workspace: workspace)
defer {
restoreFloatingWindows(floatingWindows: floatingWindows, workspace: workspace)
}

guard let (parent, ownIndex) = currentWindow.closestParent(hasChildrenInDirection: direction, withLayout: nil) else { return }
let windowToFocus = parent.children[ownIndex + direction.focusOffset]
.findFocusTargetRecursive(snappedTo: direction.opposite)
Expand All @@ -12,6 +18,46 @@ struct FocusCommand: Command {
}
}

private func makeFloatingWindowsSeenAsTiling(workspace: Workspace) -> [FloatingWindowData] {
let floatingWindows: [FloatingWindowData] = workspace.children
.filterIsInstance(of: Window.self)
.map { (window: Window) -> FloatingWindowData? in
guard let center = window.getCenter() else { return nil }
guard let target = center.coerceIn(rect: window.workspace.monitor.rect).findIn(tree: workspace.rootTilingContainer) else { return nil }
guard let targetCenter = target.getCenter() else { return nil }
guard let tilingParent = target.parent as? TilingContainer else { return nil }
let index = center.getProjection(tilingParent.orientation) >= targetCenter.getProjection(tilingParent.orientation)
? target.ownIndex + 1
: target.ownIndex
let data = window.unbindFromParent()
return FloatingWindowData(window: window, center: center, parent: tilingParent, adaptiveWeight: data.adaptiveWeight, index: index)
}
.filterNotNil()
.sortedBy { $0.center.getProjection($0.parent.orientation) }
.reversed()

for floating in floatingWindows { // Make floating windows be seen as tiling
floating.window.bind(to: floating.parent, adaptiveWeight: 1, index: floating.index)
}
return floatingWindows
}

private func restoreFloatingWindows(floatingWindows: [FloatingWindowData], workspace: Workspace) {
for floating in floatingWindows {
floating.window.unbindFromParent()
floating.window.bind(to: workspace, adaptiveWeight: floating.adaptiveWeight, index: INDEX_BIND_LAST)
}
}

private struct FloatingWindowData {
let window: Window
let center: CGPoint

let parent: TilingContainer
let adaptiveWeight: CGFloat
let index: Int
}

private extension TreeNode {
func findFocusTargetRecursive(snappedTo direction: CardinalDirection) -> Window? {
switch genericKind {
Expand Down
4 changes: 2 additions & 2 deletions src/mouse/moveWithMouse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private func moveWithMouseIfTheCase(_ window: Window) { // todo cover with tests
if targetWorkspace != window.workspace { // Move window to a different display
let index: Int
if let swapTarget, let parent = swapTarget.parent as? TilingContainer, let targetRect = swapTarget.lastAppliedLayoutRect {
index = mouseLocation.getCoordinate(parent.orientation) >= targetRect.center.getCoordinate(parent.orientation)
index = mouseLocation.getProjection(parent.orientation) >= targetRect.center.getProjection(parent.orientation)
? swapTarget.ownIndex + 1
: swapTarget.ownIndex
} else {
Expand Down Expand Up @@ -58,7 +58,7 @@ func swapWindows(_ window1: Window, _ window2: Window) {
}
}

private extension CGPoint {
extension CGPoint {
func findIn(tree: TilingContainer) -> Window? {
let point = self
let target: TreeNode?
Expand Down
2 changes: 1 addition & 1 deletion src/tree/Workspace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ private var emptyInvisibleWorkspaceGenerator: some IteratorProtocol<Workspace> {
.makeIterator()
}

func getOrCreateNextEmptyInvisibleWorkspace() -> Workspace {
func getOrCreateNextEmptyInvisibleWorkspace() -> Workspace { // todo make monitor oriented
var generator = emptyInvisibleWorkspaceGenerator
return generator.next() ?? errorT("Can't create empty workspace")
}
Expand Down
3 changes: 2 additions & 1 deletion src/tree/layoutRecursive.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
extension TreeNode {
func layoutRecursive(_ point: CGPoint, width: CGFloat, height: CGFloat, startup: Bool) {
var point = point
// lastAppliedLayoutRect shouldn't be indented
let rect = Rect(topLeftX: point.x, topLeftY: point.y, width: width, height: height)
if let orientation = (self as? TilingContainer)?.orientation, orientation == (parent as? TilingContainer)?.orientation {
point = orientation == .h
? point + CGPoint(x: 0, y: config.indentForNestedContainersWithTheSameOrientation)
: point + CGPoint(x: config.indentForNestedContainersWithTheSameOrientation, y: 0)
}
let rect = Rect(topLeftX: point.x, topLeftY: point.y, width: width, height: height)
switch genericKind {
case .workspace(let workspace):
lastAppliedLayoutRect = rect
Expand Down
4 changes: 4 additions & 0 deletions src/util/SequenceEx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ extension Sequence {
return result
}

func grouped<Group>(by criterion: (_ transforming: Element) -> Group) -> [Group: [Element]] {
Dictionary(grouping: self, by: criterion)
}

var withIndex: [(index: Int, value: Element)] {
var index = -1
return map {
Expand Down
16 changes: 15 additions & 1 deletion src/util/util.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,14 @@ extension CGPoint {
return list.minOrThrow()
}

func coerceIn(rect: Rect) -> CGPoint {
CGPoint(x: x.coerceIn(rect.minX...(rect.maxX - 1)), y: y.coerceIn(rect.minY...(rect.maxY - 1)))
}

func addingXOffset(_ offset: CGFloat) -> CGPoint { CGPoint(x: x + offset, y: y) }
func addingYOffset(_ offset: CGFloat) -> CGPoint { CGPoint(x: x, y: y + offset) }

func getCoordinate(_ orientation: Orientation) -> Double { orientation == .h ? x : y }
func getProjection(_ orientation: Orientation) -> Double { orientation == .h ? x : y }

var vectorLength: CGFloat { sqrt(x*x - y*y) }

Expand All @@ -158,6 +162,16 @@ extension CGFloat {
func div(_ denominator: Int) -> CGFloat? {
denominator == 0 ? nil : self / CGFloat(denominator)
}

func coerceIn(_ range: ClosedRange<CGFloat>) -> CGFloat {
if self > range.upperBound {
return range.upperBound
} else if self < range.lowerBound {
return range.lowerBound
} else {
return self
}
}
}

extension CGSize {
Expand Down

0 comments on commit 2db32b4

Please sign in to comment.