Skip to content

Commit 44b9119

Browse files
committed
--cycle-accordions flag
1 parent 770ff2e commit 44b9119

File tree

5 files changed

+193
-56
lines changed

5 files changed

+193
-56
lines changed

packages/wm-common/src/app_command.rs

+5
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub enum InvokeCommand {
159159
AdjustBorders(InvokeAdjustBordersCommand),
160160
Close,
161161
Focus(InvokeFocusCommand),
162+
FocusAccordion(InvokeFocusCommand),
162163
Ignore,
163164
Move(InvokeMoveCommand),
164165
MoveWorkspace {
@@ -327,6 +328,10 @@ pub struct InvokeFocusCommand {
327328

328329
#[clap(long)]
329330
pub recent_workspace: bool,
331+
332+
// #[clap(long, default_value_t = false)]
333+
#[clap(long, default_value_t = true)]
334+
pub cycle_accordions: bool,
330335
}
331336

332337
#[derive(Args, Clone, Debug, PartialEq, Serialize)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use wm_common::{Direction, TilingDirection};
2+
3+
use super::set_focused_descendant;
4+
use crate::{
5+
models::Container,
6+
traits::{CommonGetters, TilingDirectionGetters},
7+
wm_state::WmState,
8+
};
9+
10+
pub fn focus_in_accordion(
11+
origin_container: &Container,
12+
direction: &Direction,
13+
state: &mut WmState,
14+
) -> anyhow::Result<()> {
15+
let origin_or_ancestor = origin_container.clone();
16+
17+
// Check if we're in an accordion container
18+
if let Some(parent) = origin_or_ancestor.parent() {
19+
if let Ok(direction_parent) = parent.as_direction_container() {
20+
if matches!(
21+
direction_parent.tiling_direction(),
22+
TilingDirection::HorizontalAccordion
23+
| TilingDirection::VerticalAccordion
24+
) {
25+
// For horizontal accordion, only respond to Up/Down
26+
// For vertical accordion, only respond to Left/Right
27+
let is_valid_direction = match direction_parent.tiling_direction()
28+
{
29+
TilingDirection::HorizontalAccordion => {
30+
matches!(direction, Direction::Up | Direction::Down)
31+
}
32+
TilingDirection::VerticalAccordion => {
33+
matches!(direction, Direction::Left | Direction::Right)
34+
}
35+
_ => false,
36+
};
37+
38+
if is_valid_direction {
39+
// Get the next/prev sibling depending on the direction
40+
let focus_target = match direction {
41+
Direction::Up | Direction::Left => origin_or_ancestor
42+
.prev_siblings()
43+
.find_map(|c| c.as_tiling_container().ok()),
44+
_ => origin_or_ancestor
45+
.next_siblings()
46+
.find_map(|c| c.as_tiling_container().ok()),
47+
};
48+
49+
if let Some(target) = focus_target {
50+
// Set focus to the target container
51+
set_focused_descendant(&target.into(), None);
52+
state.pending_sync.queue_focus_change().queue_cursor_jump();
53+
return Ok(());
54+
}
55+
}
56+
}
57+
}
58+
}
59+
60+
// If not in an accordion or no target found, do nothing
61+
Ok(())
62+
}

packages/wm/src/commands/container/focus_in_direction.rs

+76-54
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,55 @@ pub fn focus_in_direction(
1212
origin_container: &Container,
1313
direction: &Direction,
1414
state: &mut WmState,
15+
cycle_accordions: bool, // New parameter
1516
) -> anyhow::Result<()> {
17+
// First, check if we're in an accordion and shouldn't cycle
18+
if !cycle_accordions {
19+
let is_in_accordion = if let Some(parent) = origin_container.parent() {
20+
if let Ok(direction_parent) = parent.as_direction_container() {
21+
matches!(
22+
direction_parent.tiling_direction(),
23+
TilingDirection::HorizontalAccordion
24+
| TilingDirection::VerticalAccordion
25+
)
26+
} else {
27+
false
28+
}
29+
} else {
30+
false
31+
};
32+
33+
// If in accordion but not cycling, treat the whole accordion as one
34+
// unit
35+
if is_in_accordion {
36+
// Find the accordion container
37+
let accordion_parent = origin_container
38+
.parent()
39+
.and_then(|p| p.as_direction_container().ok())
40+
.context("No direction container")?;
41+
42+
// We'll convert it to container once and use that
43+
let accordion_container = accordion_parent.clone().into();
44+
45+
// Try to move from the accordion to the next container
46+
// Use the accordion parent as the origin instead of the window
47+
let workspace_target =
48+
workspace_focus_target(&accordion_container, direction, state)
49+
.ok()
50+
.flatten();
51+
let tiling_target =
52+
tiling_focus_target(&accordion_container, direction)?;
53+
let focus_target = tiling_target.or(workspace_target);
54+
55+
if let Some(focus_target) = focus_target {
56+
set_focused_descendant(&focus_target, None);
57+
state.pending_sync.queue_focus_change().queue_cursor_jump();
58+
}
59+
60+
return Ok(());
61+
}
62+
}
63+
1664
let focus_target = match origin_container {
1765
Container::TilingWindow(_) => {
1866
// If a suitable focus target isn't found in the current workspace,
@@ -85,67 +133,41 @@ fn tiling_focus_target(
85133
let tiling_direction = TilingDirection::from_direction(direction);
86134
let mut origin_or_ancestor = origin_container.clone();
87135

88-
// First check if we're in an accordion container
89-
if let Some(parent) = origin_or_ancestor.parent() {
136+
// Check if origin is in an accordion and if direction is valid for that
137+
// accordion
138+
let in_accordion = if let Some(parent) = origin_or_ancestor.parent() {
90139
if let Ok(direction_parent) = parent.as_direction_container() {
91140
match direction_parent.tiling_direction() {
92141
TilingDirection::HorizontalAccordion => {
93-
match direction {
94-
Direction::Up => {
95-
// Try to focus previous sibling in accordion
96-
if let Some(prev) = origin_or_ancestor
97-
.prev_siblings()
98-
.find_map(|c| c.as_tiling_container().ok())
99-
{
100-
return Ok(Some(prev.into()));
101-
}
102-
// If no previous sibling, allow falling through to normal
103-
// navigation
104-
}
105-
Direction::Down => {
106-
// Try to focus next sibling in accordion
107-
if let Some(next) = origin_or_ancestor
108-
.next_siblings()
109-
.find_map(|c| c.as_tiling_container().ok())
110-
{
111-
return Ok(Some(next.into()));
112-
}
113-
// If no next sibling, allow falling through to normal
114-
// navigation
115-
}
116-
// For Left/Right, immediately pass through to normal
117-
// navigation
118-
_ => {}
119-
}
142+
matches!(direction, Direction::Up | Direction::Down)
120143
}
121144
TilingDirection::VerticalAccordion => {
122-
match direction {
123-
Direction::Left => {
124-
if let Some(prev) = origin_or_ancestor
125-
.prev_siblings()
126-
.find_map(|c| c.as_tiling_container().ok())
127-
{
128-
return Ok(Some(prev.into()));
129-
}
130-
// If no previous sibling, allow falling through to normal
131-
// navigation
132-
}
133-
Direction::Right => {
134-
if let Some(next) = origin_or_ancestor
135-
.next_siblings()
136-
.find_map(|c| c.as_tiling_container().ok())
137-
{
138-
return Ok(Some(next.into()));
139-
}
140-
// If no next sibling, allow falling through to normal
141-
// navigation
142-
}
143-
// For Up/Down, immediately pass through to normal navigation
144-
_ => {}
145-
}
145+
matches!(direction, Direction::Left | Direction::Right)
146146
}
147-
_ => {}
147+
_ => false,
148148
}
149+
} else {
150+
false
151+
}
152+
} else {
153+
false
154+
};
155+
156+
// If we're in an accordion and moving in its main direction,
157+
// handle accordion navigation directly
158+
if in_accordion {
159+
// Get the next/prev sibling depending on the direction
160+
let focus_target = match direction {
161+
Direction::Up | Direction::Left => origin_or_ancestor
162+
.prev_siblings()
163+
.find_map(|c| c.as_tiling_container().ok()),
164+
_ => origin_or_ancestor
165+
.next_siblings()
166+
.find_map(|c| c.as_tiling_container().ok()),
167+
};
168+
169+
if let Some(target) = focus_target {
170+
return Ok(Some(target.into()));
149171
}
150172
}
151173

packages/wm/src/commands/container/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod attach_container;
22
mod detach_container;
33
mod flatten_child_split_containers;
44
mod flatten_split_container;
5+
mod focus_in_accordion;
56
mod focus_in_direction;
67
mod move_container_within_tree;
78
mod replace_container;
@@ -14,6 +15,7 @@ pub use attach_container::*;
1415
pub use detach_container::*;
1516
pub use flatten_child_split_containers::*;
1617
pub use flatten_split_container::*;
18+
pub use focus_in_accordion::*;
1719
pub use focus_in_direction::*;
1820
pub use move_container_within_tree::*;
1921
pub use replace_container::*;

packages/wm/src/wm.rs

+48-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use wm_platform::PlatformEvent;
1111
use crate::{
1212
commands::{
1313
container::{
14-
focus_in_direction, set_tiling_direction, toggle_tiling_direction,
14+
focus_in_accordion, focus_in_direction, set_tiling_direction,
15+
toggle_tiling_direction,
1516
},
1617
general::{
1718
cycle_focus, disable_binding_mode, enable_binding_mode,
@@ -235,9 +236,54 @@ impl WindowManager {
235236
}
236237
InvokeCommand::Focus(args) => {
237238
if let Some(direction) = &args.direction {
238-
focus_in_direction(&subject_container, direction, state)?;
239+
focus_in_direction(
240+
&subject_container,
241+
direction,
242+
state,
243+
args.cycle_accordions,
244+
)?;
245+
}
246+
247+
if let Some(name) = &args.workspace {
248+
focus_workspace(
249+
WorkspaceTarget::Name(name.to_string()),
250+
state,
251+
config,
252+
)?;
253+
}
254+
255+
if let Some(monitor_index) = &args.monitor {
256+
focus_monitor(*monitor_index, state, config)?;
257+
}
258+
259+
if args.next_active_workspace {
260+
focus_workspace(WorkspaceTarget::NextActive, state, config)?;
261+
}
262+
263+
if args.prev_active_workspace {
264+
focus_workspace(WorkspaceTarget::PreviousActive, state, config)?;
265+
}
266+
267+
if args.next_workspace {
268+
focus_workspace(WorkspaceTarget::Next, state, config)?;
269+
}
270+
271+
if args.prev_workspace {
272+
focus_workspace(WorkspaceTarget::Previous, state, config)?;
273+
}
274+
275+
if args.recent_workspace {
276+
focus_workspace(WorkspaceTarget::Recent, state, config)?;
277+
}
278+
279+
Ok(())
280+
}
281+
InvokeCommand::FocusAccordion(args) => {
282+
if let Some(direction) = &args.direction {
283+
focus_in_accordion(&subject_container, direction, state)?;
239284
}
240285

286+
// TODO - temp C+P
241287
if let Some(name) = &args.workspace {
242288
focus_workspace(
243289
WorkspaceTarget::Name(name.to_string()),

0 commit comments

Comments
 (0)