Skip to content

Commit 5f6d135

Browse files
committed
Added --floating-pixels to move command
1 parent 18545c2 commit 5f6d135

File tree

6 files changed

+46
-6
lines changed

6 files changed

+46
-6
lines changed

Sources/AppBundle/command/impl/MoveCommand.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ struct MoveCommand: Command {
55
let args: MoveCmdArgs
66
/*conforms*/ var shouldResetClosedWindowsCache = true
77

8-
func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
8+
func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
99
let direction = args.direction.val
1010
guard let target = args.resolveTargetOrReportError(env, io) else { return false }
1111
guard let currentWindow = target.windowOrNil else {
@@ -29,7 +29,8 @@ struct MoveCommand: Command {
2929
return moveOut(window: currentWindow, direction: direction, io, args, env)
3030
}
3131
case .workspace: // floating window
32-
return io.err("moving floating windows isn't yet supported") // todo
32+
let pixels = args.floatingPixels ?? calculateDefaultPixels(direction: direction, window: currentWindow)
33+
return await moveFloatingWindow(window: currentWindow, direction: direction, pixels: pixels)
3334
case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
3435
return io.err(moveOutMacosUnconventionalWindow)
3536
case .macosPopupWindowsContainer:
@@ -154,6 +155,23 @@ private let moveOutMacosUnconventionalWindow = "moving macOS fullscreen, minimiz
154155
return true
155156
}
156157

158+
@MainActor private func calculateDefaultPixels(direction: CardinalDirection, window: Window) -> Int {
159+
guard let monitor = window.nodeMonitor else { return 50 }
160+
return direction.orientation == .h
161+
? Int(Double(monitor.width) * 0.1)
162+
: Int(Double(monitor.height) * 0.1)
163+
}
164+
165+
@MainActor private func moveFloatingWindow(window: Window, direction: CardinalDirection, pixels: Int) async -> Bool {
166+
guard let currentRect = try? await window.getAxRect() else { return false }
167+
let newTopLeft = CGPoint(
168+
x: currentRect.topLeftX + Double(direction.xOffset * pixels),
169+
y: currentRect.topLeftY + Double(direction.yOffset * pixels)
170+
)
171+
window.setAxFrame(newTopLeft, nil)
172+
return true
173+
}
174+
157175
extension TilingTreeNodeCases {
158176
@MainActor fileprivate func findDeepMoveInTargetRecursive(_ orientation: Orientation) -> TilingTreeNodeCases {
159177
return switch self {

Sources/Common/cmdArgs/impl/MoveCmdArgs.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ public struct MoveCmdArgs: CmdArgs {
88
flags: [
99
"--window-id": optionalWindowIdFlag(),
1010
"--boundaries": SubArgParser(\.rawBoundaries, upcastSubArgParserFun(parseBoundaries)),
11+
"--floating-pixels": SubArgParser(\.rawFloatingPixels, upcastSubArgParserFun(parseFloatingPixels)),
1112
"--boundaries-action": SubArgParser(\.rawBoundariesAction, upcastSubArgParserFun(parseBoundariesAction)),
1213
],
1314
posArgs: [newArgParser(\.direction, parseCardinalDirectionArg, mandatoryArgPlaceholder: CardinalDirection.unionLiteral)],
1415
)
1516

1617
public var direction: Lateinit<CardinalDirection> = .uninitialized
1718
public var rawBoundaries: Boundaries? = nil
19+
public var rawFloatingPixels: Int? = nil
1820
public var rawBoundariesAction: WhenBoundariesCrossed? = nil
1921

2022
public init(rawArgs: [String], _ direction: CardinalDirection) {
@@ -35,6 +37,7 @@ public struct MoveCmdArgs: CmdArgs {
3537
}
3638

3739
extension MoveCmdArgs {
40+
public var floatingPixels: Int? { rawFloatingPixels }
3841
public var boundaries: Boundaries { rawBoundaries ?? .workspace }
3942
public var boundariesAction: WhenBoundariesCrossed { rawBoundariesAction ?? .createImplicitContainer }
4043
}
@@ -58,3 +61,15 @@ private func parseBoundariesAction(i: SubArgParserInput) -> ParsedCliArgs<MoveCm
5861
return .fail("<action> is mandatory", advanceBy: 0)
5962
}
6063
}
64+
65+
private func parseFloatingPixels(i: SubArgParserInput) -> ParsedCliArgs<Int> {
66+
if let arg = i.nonFlagArgOrNil() {
67+
if let value = Int(arg) {
68+
return .succ(value, advanceBy: 1)
69+
} else {
70+
return .fail("'\(arg)' must be an integer", advanceBy: 1)
71+
}
72+
} else {
73+
return .fail("<pixels> is mandatory", advanceBy: 0)
74+
}
75+
}

Sources/Common/cmdHelpGenerated.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ let move_workspace_to_monitor_help_generated = """
118118
OR: move-workspace-to-monitor [-h|--help] [--workspace <workspace>] <monitor-pattern>...
119119
"""
120120
let move_help_generated = """
121-
USAGE: move [-h|--help] [--window-id <window-id>] [--boundaries <boundary>] [--boundaries-action <boundary-action>] (left|down|up|right)
121+
USAGE: move [-h|--help] [--window-id <window-id>] [--boundaries <boundary>] [--boundaries-action <boundary-action>] [--floating-pixels <pixels>] (left|down|up|right)
122122
"""
123123
let reload_config_help_generated = """
124124
USAGE: reload-config [-h|--help] [--no-gui] [--dry-run]

Sources/Common/model/CardinalDirection.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ public enum CardinalDirection: String, CaseIterable, Equatable, Sendable {
1313
}
1414
public var focusOffset: Int { isPositive ? 1 : -1 }
1515
public var insertionOffset: Int { isPositive ? 1 : 0 }
16+
public var xOffset: Int { self == .left ? -1 : (self == .right ? 1 : 0) }
17+
public var yOffset: Int { self == .down ? -1 : (self == .up ? 1 : 0) }
1618
}

docs/aerospace-move.adoc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ include::util/man-attributes.adoc[]
99
== Synopsis
1010
[verse]
1111
// tag::synopsis[]
12-
aerospace move [-h|--help] [--window-id <window-id>] [--boundaries <boundary>] [--boundaries-action <boundary-action>] (left|down|up|right)
12+
aerospace move [-h|--help] [--window-id <window-id>] [--boundaries <boundary>] [--boundaries-action <boundary-action>] [--floating-pixels <pixels>] (left|down|up|right)
1313

1414
// end::synopsis[]
1515

@@ -35,10 +35,14 @@ Defines move boundaries. +
3535
The default is: `workspace`
3636

3737
--boundaries-action <boundary-action>::
38-
Defines the behavior when requested to move across the `<boundary>`. +
38+
Define the behavior when requested to move across the `<boundary>`. +
3939
`<boundary-action>` possible values: `(stop|fail|create-implicit-container)`. +
4040
The default is: `create-implicit-container`
4141

42+
--floating-pixels <pixels>::
43+
If window is floating then move it in the specified direction by the specified number of `<pixels>`. +
44+
If not specified, the default value for `<pixels>` is 10%.
45+
4246
// =========================================================== Examples
4347
include::util/conditional-examples-header.adoc[]
4448

grammar/commands-bnf-grammar.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,12 @@ aerospace -h;
107107
<mode_id> ::= {{{ aerospace config --get mode --keys }}};
108108
<workspace> ::= {{{ aerospace list-workspaces --monitor all --empty no }}};
109109
<number> ::= {{{ true }}};
110+
<pixels> ::= {{{ true }}};
110111
<monitor_pattern> ::= {{{ true }}};
111112

112113
<list_workspaces1_flag> ::= --visible [no] | --empty [no] | --format <output_format> | --format <output_format> | --count | --json;
113114

114-
<move_command_flag> ::= --window-id <window_id> | --boundaries <boundary> | --boundaries-action <move_boundaries_action>;
115+
<move_command_flag> ::= --window-id <window_id> | --boundaries <boundary> | --boundaries-action <move_boundaries_action> | --floating-pixels <pixels>;
115116
<move_boundaries_action> ::= stop|fail|create-implicit-container;
116117

117118
<move_node_to_monitor1_flag> ::= --window-id <window_id>|--focus-follows-window|--fail-if-noop|--wrap-around;

0 commit comments

Comments
 (0)