1#![allow(clippy::wildcard_imports)]
2
3mod scratchpad_handler;
4
5use leftwm_layouts::geometry::{Direction as FocusDirection, Rect};
6pub use scratchpad_handler::{Direction, ReleaseScratchPadOption};
9
10use super::*;
11use crate::command::FocusDeltaBehavior;
12use crate::display_action::DisplayAction;
13use crate::display_servers::DisplayServer;
14use crate::layouts::{self, MAIN_AND_DECK, MONOCLE};
15use crate::models::{Handle, TagId, WindowState};
16use crate::state::State;
17use crate::utils::helpers;
18use crate::utils::helpers::relative_find;
19use crate::{config::Config, models::FocusBehaviour};
20
21impl<H: Handle, C: Config, SERVER: DisplayServer<H>> Manager<H, C, SERVER> {
22 pub fn command_handler(&mut self, command: &Command<H>) -> bool {
32 process_internal(self, command).unwrap_or(false)
33 }
34}
35
36macro_rules! move_focus_common_vars {
37 ($func:ident ($state:expr $(, $arg:expr )* $(,)? )) => {{
38 let handle = $state.focus_manager.window(&$state.windows)?.handle;
39 let tag_id = $state.focus_manager.tag(0)?;
40 let ws_id = $state.focus_manager.workspace(&$state.workspaces)?.id;
41 let layout = Some($state.layout_manager.layout(ws_id, tag_id).name.to_owned());
42
43 let for_active_workspace =
44 |x: &Window<H>| -> bool { x.tag == Some(tag_id) && x.is_managed() };
45
46 let to_reorder = helpers::vec_extract(&mut $state.windows, for_active_workspace);
47 $func($state, handle, layout.as_ref(), to_reorder, $($arg),*)
48 }};
49}
50
51fn process_internal<H: Handle, C: Config, SERVER: DisplayServer<H>>(
52 manager: &mut Manager<H, C, SERVER>,
53 command: &Command<H>,
54) -> Option<bool> {
55 let state = &mut manager.state;
56 match command {
57 Command::ToggleScratchPad(name) => scratchpad_handler::toggle_scratchpad(manager, name),
58 Command::AttachScratchPad { window, scratchpad } => {
59 scratchpad_handler::attach_scratchpad(*window, scratchpad, manager)
60 }
61 Command::ReleaseScratchPad { window, tag } => {
62 scratchpad_handler::release_scratchpad(window.clone(), *tag, manager)
63 }
64
65 Command::NextScratchPadWindow { scratchpad } => {
66 scratchpad_handler::cycle_scratchpad_window(manager, scratchpad, Direction::Forward)
67 }
68 Command::PrevScratchPadWindow { scratchpad } => {
69 scratchpad_handler::cycle_scratchpad_window(manager, scratchpad, Direction::Backward)
70 }
71
72 Command::ToggleMaximized => toggle_state(state, WindowState::Maximized),
73 Command::ToggleFullScreen => toggle_state(state, WindowState::Fullscreen),
74 Command::ToggleSticky => toggle_state(state, WindowState::Sticky),
75 Command::ToggleAbove => toggle_state(state, WindowState::Above),
76
77 Command::SendWindowToTag { window, tag } => move_to_tag(*window, *tag, manager),
78 Command::MoveWindowToNextTag { follow } => move_to_tag_relative(manager, *follow, 1),
79 Command::MoveWindowToPreviousTag { follow } => move_to_tag_relative(manager, *follow, -1),
80 Command::MoveWindowToLastWorkspace => move_to_last_workspace(state),
81 Command::MoveWindowToNextWorkspace => move_window_to_workspace_change(manager, 1),
82 Command::MoveWindowToPreviousWorkspace => move_window_to_workspace_change(manager, -1),
83 Command::MoveWindowUp => move_focus_common_vars!(move_window_change(state, -1)),
84 Command::MoveWindowDown => move_focus_common_vars!(move_window_change(state, 1)),
85 Command::MoveWindowTop { swap } => move_focus_common_vars!(move_window_top(state, *swap)),
86 Command::MoveWindowAt(param) => {
87 move_focus_common_vars!(move_window_direction(state, *param))
88 }
89 Command::SwapWindowTop { swap } => move_focus_common_vars!(swap_window_top(state, *swap)),
90
91 Command::GoToTag { tag, swap } => goto_tag(state, *tag, *swap),
92 Command::ReturnToLastTag => return_to_last_tag(state),
93
94 Command::CloseWindow => close_window(state),
95 Command::SwapScreens => swap_tags(state),
96 Command::NextLayout => next_layout(state),
97 Command::PreviousLayout => previous_layout(state),
98
99 Command::SetLayout(layout) => set_layout(layout.as_str(), state),
100
101 Command::FloatingToTile => floating_to_tile(state),
102 Command::TileToFloating => tile_to_floating(state),
103 Command::ToggleFloating => toggle_floating(state),
104
105 Command::FocusNextTag { behavior } => match *behavior {
106 FocusDeltaBehavior::Default => focus_tag_change(state, 1),
107 FocusDeltaBehavior::IgnoreEmpty => focus_next_used_tag(state),
108 FocusDeltaBehavior::IgnoreUsed => focus_next_empty_tag(state),
109 },
110 Command::FocusPreviousTag { behavior } => match *behavior {
111 FocusDeltaBehavior::Default => focus_tag_change(state, -1),
112 FocusDeltaBehavior::IgnoreEmpty => focus_previous_used_tag(state),
113 FocusDeltaBehavior::IgnoreUsed => focus_previous_empty_tag(state),
114 },
115 Command::FocusWindow(param) => focus_window(state, param),
116 Command::FocusWindowUp => move_focus_common_vars!(focus_window_change(state, -1)),
117 Command::FocusWindowDown => move_focus_common_vars!(focus_window_change(state, 1)),
118 Command::FocusWindowTop { swap } => focus_window_top(state, *swap),
119 Command::FocusWindowAt(param) => focus_window_direction(state, *param),
120 Command::FocusWorkspaceNext => focus_workspace_change(state, 1),
121 Command::FocusWorkspacePrevious => focus_workspace_change(state, -1),
122
123 Command::SoftReload => {
124 if let Some((handle, Some(tag))) = state
126 .focus_manager
127 .window(&state.windows)
128 .map(|w| (w.handle, w.tag))
129 {
130 let old_handle = state
131 .focus_manager
132 .tags_last_window
133 .entry(tag)
134 .or_insert(handle);
135 *old_handle = handle;
136 }
137 manager.config.save_state(&manager.state);
138 manager.hard_reload();
139 None
140 }
141 Command::HardReload => {
142 manager.hard_reload();
143 None
144 }
145
146 Command::RotateTag => rotate_tag(state),
147
148 Command::IncreaseMainWidth(delta) | Command::IncreaseMainSize(delta) => {
149 change_main_size(state, *delta, 1)
150 }
151 Command::DecreaseMainWidth(delta) | Command::DecreaseMainSize(delta) => {
152 change_main_size(state, *delta, -1)
153 }
154 Command::IncreaseMainCount() => change_main_count(state, 1),
155 Command::DecreaseMainCount() => change_main_count(state, -1),
156 Command::SetMarginMultiplier(multiplier) => set_margin_multiplier(state, *multiplier),
157 Command::SendWorkspaceToTag(ws_index, tag_index) => {
158 Some(send_workspace_to_tag(state, *ws_index, *tag_index))
159 }
160 Command::CloseAllOtherWindows => close_all_other_windows(state),
161 Command::Other(cmd) => Some(C::command_handler(cmd, manager)),
162 }
163}
164
165fn focus_next_empty_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
166 let used_tags: Vec<usize> = state.windows.iter().filter_map(|w| w.tag).collect();
167 let unused_tags: Vec<usize> = state
168 .tags
169 .normal()
170 .iter()
171 .filter(|t| !used_tags.contains(&t.to_owned().id))
172 .map(|t| t.id)
173 .collect();
174 let next_unused_tag = match unused_tags
175 .iter()
176 .find(|t| **t > state.focus_manager.tag(0).unwrap_or_default())
177 {
178 Some(t) => t,
179 None => match unused_tags.first() {
180 Some(t) => t,
181 None => return Some(false),
182 },
183 };
184 state.goto_tag_handler(*next_unused_tag)
185}
186
187fn focus_previous_empty_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
188 let used_tags: Vec<usize> = state.windows.iter().filter_map(|w| w.tag).collect();
189 let unused_tags: Vec<usize> = state
190 .tags
191 .normal()
192 .iter()
193 .filter(|t| !used_tags.contains(&t.to_owned().id))
194 .map(|t| t.id)
195 .collect();
196 let previous_unused_tag = match unused_tags
197 .iter()
198 .rfind(|t| **t < state.focus_manager.tag(0).unwrap_or_default())
199 {
200 Some(t) => t,
201 None => match unused_tags.last() {
202 Some(t) => t,
203 None => return Some(false),
204 },
205 };
206 state.goto_tag_handler(*previous_unused_tag)
207}
208
209fn focus_next_used_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
210 let mut used_tags: Vec<usize> = state.windows.iter().filter_map(|w| w.tag).collect();
211 used_tags.sort_unstable();
212 let next_used_tag = match used_tags
213 .iter()
214 .find(|t| **t > state.focus_manager.tag(0).unwrap_or_default())
215 {
216 Some(t) => t,
217 None => match used_tags.first() {
218 Some(t) => t,
219 None => return Some(false),
220 },
221 };
222 state.goto_tag_handler(*next_used_tag)
223}
224
225fn focus_previous_used_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
226 let mut used_tags: Vec<usize> = state.windows.iter().filter_map(|w| w.tag).collect();
227 used_tags.sort_unstable();
228 used_tags.reverse();
229 let previous_used_tag = match used_tags
230 .iter()
231 .find(|t| **t < state.focus_manager.tag(0).unwrap_or_default())
232 {
233 Some(t) => t,
234 None => match used_tags.first() {
235 Some(t) => t,
236 None => return Some(false),
237 },
238 };
239 state.goto_tag_handler(*previous_used_tag)
240}
241
242fn toggle_state<H: Handle>(state: &mut State<H>, window_state: WindowState) -> Option<bool> {
243 let window = state.focus_manager.window(&state.windows)?;
244 let handle = window.handle;
245 let toggle_to = !window.states.contains(&window_state);
246 let act = DisplayAction::SetState(handle, toggle_to, window_state);
247 state.actions.push_back(act);
248 state.handle_window_focus(&handle);
249 match window_state {
250 WindowState::Fullscreen | WindowState::Maximized => Some(true),
251 _ => Some(false),
252 }
253}
254
255fn move_to_tag<H: Handle, C: Config, SERVER: DisplayServer<H>>(
256 window: Option<WindowHandle<H>>,
257 tag_id: TagId,
258 manager: &mut Manager<H, C, SERVER>,
259) -> Option<bool> {
260 let tag = manager.state.tags.get(tag_id)?.clone();
261
262 let margin_multiplier = match manager.state.windows.iter().find(|w| w.has_tag(&tag.id)) {
265 Some(w) => w.margin_multiplier(),
266 None => 1.0,
267 };
268
269 let handle = window.or(*manager.state.focus_manager.window_history.front()?)?;
270 let handle_focus = window.is_none();
272 let new_handle = if handle_focus {
274 manager.get_next_or_previous_handle(&handle)
275 } else {
276 None
277 };
278
279 let window = manager
280 .state
281 .windows
282 .iter_mut()
283 .find(|w| w.handle == handle)?;
284
285 window.untag();
286 window.set_floating(false);
287 window.tag(&tag.id);
288 window.apply_margin_multiplier(margin_multiplier);
289 let act = DisplayAction::SetWindowTag(window.handle, Some(tag.id));
290 manager.state.actions.push_back(act);
291
292 manager.state.sort_windows();
293 manager
294 .state
295 .handle_single_border(manager.config.border_width());
296 if handle_focus {
297 if let Some(new_handle) = new_handle {
298 manager.state.focus_window(&new_handle);
299 } else {
300 let act = DisplayAction::Unfocus(Some(handle), false);
301 manager.state.actions.push_back(act);
302 manager.state.focus_manager.window_history.push_front(None);
303 }
304 }
305 Some(true)
306}
307
308fn move_to_tag_relative<H: Handle, C: Config, SERVER: DisplayServer<H>>(
312 manager: &mut Manager<H, C, SERVER>,
313 follow: bool,
314 delta: i32,
315) -> Option<bool> {
316 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default() - 1;
318 let tags_len = manager.state.tags.normal().len() as isize;
321 let desired_tag = (current_tag as isize + delta as isize).rem_euclid(tags_len) + 1;
322 let desired_tag = desired_tag as usize;
323
324 move_to_tag(None, desired_tag, manager);
325 if follow {
326 let moved_window = *manager.state.focus_manager.window_history.get(1)?;
327 manager.state.goto_tag_handler(desired_tag);
328 manager.state.handle_window_focus(&moved_window?);
329 }
330 Some(true)
331}
332
333fn move_window_to_workspace_change<H: Handle, C: Config, SERVER: DisplayServer<H>>(
334 manager: &mut Manager<H, C, SERVER>,
335 delta: i32,
336) -> Option<bool> {
337 let current = manager
338 .state
339 .focus_manager
340 .workspace(&manager.state.workspaces)?;
341 let workspace =
342 helpers::relative_find(&manager.state.workspaces, |w| w == current, delta, true)?.clone();
343
344 let tag_id = workspace.tag?;
345 move_to_tag(None, tag_id, manager)
346}
347
348fn goto_tag<H: Handle>(
349 state: &mut State<H>,
350 input_tag: TagId,
351 current_tag_swap: bool,
352) -> Option<bool> {
353 let current_tag = state.focus_manager.tag(0).unwrap_or_default();
354 let previous_tag = state.focus_manager.tag(1).unwrap_or_default();
355 let destination_tag = if current_tag_swap && current_tag == input_tag {
356 previous_tag
357 } else {
358 input_tag
359 };
360 state.goto_tag_handler(destination_tag)
361}
362
363fn return_to_last_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
364 let previous_tag = state.focus_manager.tag(1).unwrap_or_default();
365 state.goto_tag_handler(previous_tag)
366}
367
368fn focus_window<H: Handle>(state: &mut State<H>, param: &str) -> Option<bool> {
369 match param.parse::<usize>() {
370 Ok(index) if index > 0 => {
371 let handle = state
373 .windows
374 .iter()
375 .filter(|w| w.visible())
376 .nth(index - 1)?
377 .handle;
378
379 state.handle_window_focus(&handle);
380 None
381 }
382 Err(_) => focus_window_by_class(state, param),
383 Ok(_) => None,
384 }
385}
386
387fn focus_window_direction<H: Handle>(state: &mut State<H>, dir: FocusDirection) -> Option<bool> {
389 let workspace = state.focus_manager.workspace(&state.workspaces)?.rect();
390 let mut rects: Vec<Rect> = vec![];
391 let cur_window = state.focus_manager.window(&state.windows)?;
392
393 let mut cur = None;
394
395 for (i, x) in state.windows.iter().filter(|w| w.visible()).enumerate() {
396 if cur_window.handle.eq(&x.handle) {
397 cur = Some(i);
398 }
399 rects.push(Rect::new(
400 x.x() - workspace.x,
401 x.y() - workspace.y,
402 x.width() as u32,
403 x.height() as u32,
404 ));
405 }
406
407 let next_window = FocusDirection::find_neighbor(&rects, cur?, dir, &workspace);
408
409 match next_window {
410 Some(next) => {
411 let handle = state
413 .windows
414 .iter()
415 .filter(|w| w.visible())
416 .nth(next)?
417 .handle;
418 state.handle_window_focus(&handle);
419 Some(true)
420 }
421 None => None,
422 }
423}
424
425fn focus_window_by_class<H: Handle>(state: &mut State<H>, window_class: &str) -> Option<bool> {
426 let is_target = |w: &Window<H>| -> bool {
427 w.res_name
428 .as_ref()
429 .zip(w.res_class.as_ref())
430 .is_some_and(|(res_name, res_class)| {
431 window_class == res_name || window_class == res_class
432 })
433 };
434
435 let current_window = state.focus_manager.window(&state.windows)?;
436 let target_window = if is_target(current_window) {
437 let previous_window_handle = state.focus_manager.window_history.get(1);
438 state
439 .windows
440 .iter()
441 .find(|w| Some(&Some(w.handle)) == previous_window_handle)
442 .cloned()
443 } else {
444 state.windows.iter().find(|w| is_target(w)).cloned()
445 }?;
446
447 let handle = target_window.handle;
448
449 if target_window.visible() {
450 state.handle_window_focus(&handle);
451 return None;
452 }
453
454 let tag_id = target_window.tag?;
455 state.goto_tag_handler(tag_id)?;
456
457 match state
458 .focus_manager
459 .workspace(&state.workspaces)
460 .map(|ws| state.layout_manager.layout(ws.id, tag_id))
461 {
462 Some(layout) if layout.is_monocle() || layout.is_main_and_deck() => {
463 let mut windows = helpers::vec_extract(&mut state.windows, |w| {
464 w.has_tag(&tag_id) && w.is_managed() && !w.floating()
465 });
466
467 let cycle = |wins: &mut Vec<Window<H>>, s: &mut State<H>| {
468 let window_index = wins.iter().position(|w| w.handle == handle).unwrap_or(0);
469 _ = helpers::cycle_vec(wins, -(window_index as i32));
470 s.windows.append(wins);
471 };
472
473 if layout.is_monocle() && windows.len() > 1 {
474 cycle(&mut windows, state);
475 } else if layout.is_main_and_deck() && windows.len() > 2 {
476 let main_window = windows.remove(0);
477 state.windows.push(main_window);
478 cycle(&mut windows, state);
479 } else {
480 state.windows.append(&mut windows);
481 }
482
483 state.handle_window_focus(&handle);
484 Some(true)
485 }
486 Some(_) => {
487 state.handle_window_focus(&handle);
488 Some(true)
489 }
490 None => None,
491 }
492}
493
494fn focus_tag_change<H: Handle>(state: &mut State<H>, delta: i8) -> Option<bool> {
497 let current_tag = state.focus_manager.tag(0)?;
498 let tags = state.tags.normal();
499 let relative_tag_id = relative_find(tags, |tag| tag.id == current_tag, i32::from(delta), true)
500 .map(|tag| tag.id)?;
501 state.goto_tag_handler(relative_tag_id)
502}
503
504fn swap_tags<H: Handle>(state: &mut State<H>) -> Option<bool> {
505 if state.workspaces.len() >= 2 && state.focus_manager.workspace_history.len() >= 2 {
506 let hist_a = *state.focus_manager.workspace_history.front()?;
507 let hist_b = *state.focus_manager.workspace_history.get(1)?;
508 let mut temp = None;
510 std::mem::swap(&mut state.workspaces.get_mut(hist_a)?.tag, &mut temp);
511 std::mem::swap(&mut state.workspaces.get_mut(hist_b)?.tag, &mut temp);
512 std::mem::swap(&mut state.workspaces.get_mut(hist_a)?.tag, &mut temp);
513 state.update_static();
515 return Some(true);
516 }
517 if state.workspaces.len() == 1 {
518 let last = *state.focus_manager.tag_history.get(1)?;
519 return state.goto_tag_handler(last);
520 }
521 None
522}
523
524fn close_window<H: Handle>(state: &mut State<H>) -> Option<bool> {
526 let window = state.focus_manager.window(&state.windows)?;
527 if window.is_managed() {
528 let act = DisplayAction::KillWindow(window.handle);
529 state.actions.push_back(act);
530 }
531 None
532}
533
534fn move_to_last_workspace<H: Handle>(state: &mut State<H>) -> Option<bool> {
535 if state.workspaces.len() >= 2 && state.focus_manager.workspace_history.len() >= 2 {
536 let index = *state.focus_manager.workspace_history.get(1)?;
537 let wp_tags = state.workspaces.get(index)?.tag;
538 let window = state.focus_manager.window_mut(&mut state.windows)?;
539 window.tag = wp_tags;
540 return Some(true);
541 }
542 None
543}
544
545fn next_layout<H: Handle>(state: &mut State<H>) -> Option<bool> {
546 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
547 state
548 .layout_manager
549 .cycle_next_layout(workspace.id, workspace.tag.unwrap_or(1));
550 Some(true)
551}
552
553fn previous_layout<H: Handle>(state: &mut State<H>) -> Option<bool> {
554 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
555 state
556 .layout_manager
557 .cycle_previous_layout(workspace.id, workspace.tag.unwrap_or(1));
558 Some(true)
559}
560
561fn set_layout<H: Handle>(layout: &str, state: &mut State<H>) -> Option<bool> {
562 let tag_id = state.focus_manager.tag(0)?;
563 if state.focus_manager.behaviour != FocusBehaviour::Sloppy {
566 let focused_window = state.focus_manager.window_history.front();
568 let is_focused_floating = match state
569 .windows
570 .iter()
571 .find(|w| Some(&Some(w.handle)) == focused_window)
572 {
573 Some(w) => w.floating(),
574 None => false,
575 };
576 if !is_focused_floating {
577 let mut to_focus = None;
578
579 if layout == layouts::MONOCLE {
580 to_focus = state
581 .windows
582 .iter()
583 .find(|w| w.has_tag(&tag_id) && w.is_managed() && !w.floating());
584 } else if layout == layouts::MAIN_AND_DECK {
585 if let Some(&Some(h)) = focused_window {
586 let mut tags_windows = state
587 .windows
588 .iter()
589 .filter(|w| w.has_tag(&tag_id) && w.is_managed() && !w.floating());
590
591 let mw = tags_windows.next();
592 let tdw = tags_windows.next();
593
594 if let (Some(mw), Some(tdw)) = (mw, tdw) {
595 if mw.handle != h && tdw.handle != h {
598 to_focus = Some(tdw);
599 }
600 }
601 }
602 }
603
604 if let Some(handle) = to_focus.map(|w| w.handle) {
605 state.focus_window(&handle);
606 }
607 }
608 }
609 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
610 state
611 .layout_manager
612 .set_layout(workspace.id, tag_id, layout);
613 Some(true)
614}
615
616fn floating_to_tile<H: Handle>(state: &mut State<H>) -> Option<bool> {
617 let workspace = state.focus_manager.workspace(&state.workspaces)?;
618 let window = state.focus_manager.window_mut(&mut state.windows)?;
619 if window.must_float() {
620 return None;
621 }
622 if !window.floating() {
625 return None;
626 }
627 let handle = window.handle;
628 if window.snap_to_workspace(workspace) {
629 state.sort_windows();
630 }
631 state.handle_window_focus(&handle);
632 Some(true)
633}
634
635fn tile_to_floating<H: Handle>(state: &mut State<H>) -> Option<bool> {
636 let width = state.default_width;
637 let height = state.default_height;
638 let window = state.focus_manager.window_mut(&mut state.windows)?;
639
640 if window.floating() {
641 return None;
642 }
643
644 let mut normal = window.normal;
645 let offset = window.container_size.unwrap_or_default();
646
647 normal.set_x(normal.x() + window.margin.left as i32);
648 normal.set_y(normal.y() + window.margin.top as i32);
649 normal.set_w(width);
650 normal.set_h(height);
651 let floating = normal - offset;
652
653 window.set_floating_offsets(Some(floating));
654 window.start_loc = Some(floating);
655 window.set_floating(true);
656
657 let handle = window.handle;
658 state.move_to_top(&handle);
659
660 Some(true)
661}
662
663fn toggle_floating<H: Handle>(state: &mut State<H>) -> Option<bool> {
664 let window = state.focus_manager.window(&state.windows)?;
665 if window.floating() {
666 floating_to_tile(state)
667 } else {
668 tile_to_floating(state)
669 }
670}
671
672fn move_window_change<H: Handle>(
673 state: &mut State<H>,
674 mut handle: WindowHandle<H>,
675 layout: Option<&String>,
676 mut to_reorder: Vec<Window<H>>,
677 val: i32,
678) -> Option<bool> {
679 let is_handle = |x: &Window<H>| -> bool { x.handle == handle };
680 if layout == Some(MONOCLE.to_string()).as_ref() {
681 handle = helpers::relative_find(&to_reorder, is_handle, -val, true)?.handle;
682 let _ = helpers::cycle_vec(&mut to_reorder, val);
683 } else if layout == Some(MAIN_AND_DECK.to_string()).as_ref() {
684 if let Some(index) = to_reorder.iter().position(|x: &Window<H>| !x.floating()) {
685 let mut window_group = to_reorder.split_off(index + 1);
686 if !to_reorder.iter().any(|w| w.handle == handle) {
687 handle = helpers::relative_find(&window_group, is_handle, -val, true)?.handle;
688 }
689 _ = helpers::cycle_vec(&mut window_group, val);
690 to_reorder.append(&mut window_group);
691 }
692 } else {
693 _ = helpers::reorder_vec(&mut to_reorder, is_handle, val);
694 }
695 state.windows.append(&mut to_reorder);
696 state.handle_window_focus(&handle);
697 Some(true)
698}
699
700fn move_window_top<H: Handle>(
702 state: &mut State<H>,
703 handle: WindowHandle<H>,
704 _layout: Option<&String>,
705 mut to_reorder: Vec<Window<H>>,
706 swap: bool,
707) -> Option<bool> {
708 let is_handle = |x: &Window<H>| -> bool { x.handle == handle };
711 let list = &mut to_reorder;
712 let len = list.len();
713 let index = list.iter().position(is_handle)?;
714 let item = list.get(index)?.clone();
715 list.remove(index);
716 dbg!(swap);
717 let mut new_index: usize = match index {
718 0 if swap => 1,
719 _ => 0,
720 };
721 if new_index >= len {
722 new_index -= len;
723 }
724 list.insert(new_index, item);
725
726 state.windows.append(&mut to_reorder);
727 if index > 0 {
729 state.handle_window_focus(&handle);
730 }
731 Some(true)
732}
733
734fn move_window_direction<H: Handle>(
735 state: &mut State<H>,
736 mut handle: WindowHandle<H>,
737 _layout: Option<&String>,
738 mut to_reorder: Vec<Window<H>>,
739 dir: FocusDirection,
740) -> Option<bool> {
741 let workspace = state.focus_manager.workspace(&state.workspaces)?.rect();
742 let mut rects: Vec<Rect> = vec![];
743 let mut cur = None;
744
745 for (i, x) in to_reorder.iter().filter(|w| w.visible()).enumerate() {
746 if handle.eq(&x.handle) {
747 cur = Some(i);
748 }
749 rects.push(Rect::new(
750 x.x() - workspace.x,
751 x.y() - workspace.y,
752 x.width() as u32,
753 x.height() as u32,
754 ));
755 }
756
757 let list = &mut to_reorder;
758 if let Some(next) = FocusDirection::find_neighbor(&rects, cur?, dir, &workspace) {
759 list.swap(cur?, next);
760 handle = list.get(next)?.handle;
761 }
762
763 state.windows.append(&mut to_reorder);
764 state.handle_window_focus(&handle);
765 Some(true)
766}
767
768fn swap_window_top<H: Handle>(
769 state: &mut State<H>,
770 handle: WindowHandle<H>,
771 _layout: Option<&String>,
772 mut to_reorder: Vec<Window<H>>,
773 swap: bool,
774) -> Option<bool> {
775 let is_handle = |x: &Window<H>| -> bool { x.handle == handle };
778 let list = &mut to_reorder;
779 let len = list.len();
780 let index = list.iter().position(is_handle)?;
781
782 let mut new_index: usize = match index {
783 0 if swap => 1,
784 _ => 0,
785 };
786
787 if new_index >= len {
788 new_index -= len;
789 }
790 list.swap(index, new_index);
791
792 state.windows.append(&mut to_reorder);
793 if index > 0 {
795 state.handle_window_focus(&handle);
796 }
797 Some(true)
798}
799
800fn focus_window_change<H: Handle>(
801 state: &mut State<H>,
802 mut handle: WindowHandle<H>,
803 layout: Option<&String>,
804 mut to_reorder: Vec<Window<H>>,
805 val: i32,
806) -> Option<bool> {
807 let is_handle = |x: &Window<H>| -> bool { x.handle == handle };
808 if layout == Some(layouts::MONOCLE.to_string()).as_ref() {
809 handle = helpers::relative_find(&to_reorder, is_handle, -val, true)?.handle;
813 let _ = helpers::cycle_vec(&mut to_reorder, val);
814 } else if layout == Some(layouts::MAIN_AND_DECK.to_string()).as_ref() {
815 let len = to_reorder.len() as i32;
816 if len > 0 {
817 let index = match to_reorder.iter().position(|x: &Window<H>| !x.floating()) {
818 Some(i) => {
819 if i as i32 == len - 1 {
820 i
821 } else {
822 i + 1
823 }
824 }
825 None => len.saturating_sub(1) as usize,
826 };
827 let window_group = &to_reorder[..=index];
828 handle = helpers::relative_find(window_group, is_handle, -val, true)?.handle;
829 }
830 } else if let Some(new_focused) = helpers::relative_find(&to_reorder, is_handle, val, true) {
831 handle = new_focused.handle;
832 }
833 state.windows.append(&mut to_reorder);
834 state.handle_window_focus(&handle);
835 Some(layout == Some(layouts::MONOCLE.to_string()).as_ref())
836}
837
838fn focus_window_top<H: Handle>(state: &mut State<H>, swap: bool) -> Option<bool> {
839 let tag = state.focus_manager.tag(0)?;
840 let cur = state.focus_manager.window(&state.windows).map(|w| w.handle);
841 let prev = state.focus_manager.tags_last_window.get(&tag).copied();
842 let next = state
843 .windows
844 .iter()
845 .find(|x| x.tag == Some(tag) && !x.floating() && x.is_managed())
846 .map(|w| w.handle);
847
848 match (next, cur, prev) {
849 (Some(next), Some(cur), Some(prev)) if next == cur && swap => {
850 state.handle_window_focus(&prev);
851 }
852 (Some(next), Some(cur), _) if next != cur => state.handle_window_focus(&next),
853 _ => {}
854 }
855 None
856}
857
858fn close_all_other_windows<H: Handle>(state: &mut State<H>) -> Option<bool> {
859 let current_window: Option<WindowHandle<H>> =
860 state.focus_manager.window(&state.windows).map(|w| w.handle);
861 let current_workspace = state.focus_manager.workspace(&state.workspaces);
862
863 for window in &state.windows {
864 if window.handle.ne(¤t_window?)
865 && current_workspace?.is_displaying(window)
866 && window.r#type.ne(&WindowType::Normal)
867 {
868 let act = DisplayAction::KillWindow(window.handle);
869 state.actions.push_back(act);
870 }
871 }
872 Some(true)
873}
874
875fn focus_workspace_change<H: Handle>(state: &mut State<H>, val: i32) -> Option<bool> {
876 let current = state.focus_manager.workspace(&state.workspaces)?;
877 let workspace = helpers::relative_find(&state.workspaces, |w| w == current, val, true)?.clone();
878
879 if state.focus_manager.behaviour.is_sloppy() && state.focus_manager.sloppy_mouse_follows_focus {
880 let action = workspace
881 .tag
882 .as_ref()
883 .and_then(|tag| state.focus_manager.tags_last_window.get(tag))
884 .map_or_else(
885 || DisplayAction::MoveMouseOverPoint(workspace.xyhw.center()),
886 |h| DisplayAction::MoveMouseOver(*h, true),
887 );
888 state.actions.push_back(action);
889 }
890 state.focus_workspace(&workspace);
891 None
892}
893
894fn rotate_tag<H: Handle>(state: &mut State<H>) -> Option<bool> {
895 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
896 let workspace_id = workspace.id;
897 let tag_id = state.focus_manager.tag(0)?;
898 let def = state.layout_manager.layout_mut(workspace_id, tag_id);
899 def.rotate(true);
900 Some(true)
901}
902
903fn change_main_size<H: Handle>(state: &mut State<H>, delta: i32, factor: i8) -> Option<bool> {
904 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
905 let workspace_id = workspace.id;
906 let tag_id = state.focus_manager.tag(0)?;
907 let def = state.layout_manager.layout_mut(workspace_id, tag_id);
908 def.change_main_size(delta * i32::from(factor), workspace.width());
909 Some(true)
910}
911
912fn change_main_count<H: Handle>(state: &mut State<H>, factor: i8) -> Option<bool> {
913 let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
914 let workspace_id = workspace.id;
915 let tag_id = state.focus_manager.tag(0)?;
916 let def = state.layout_manager.layout_mut(workspace_id, tag_id);
917 match factor {
918 1 => def.increase_main_window_count(),
919 -1 => def.decrease_main_window_count(),
920 _ => (),
921 }
922 Some(true)
923}
924
925fn set_margin_multiplier<H: Handle>(state: &mut State<H>, margin_multiplier: f32) -> Option<bool> {
926 let ws = state.focus_manager.workspace_mut(&mut state.workspaces)?;
927 ws.set_margin_multiplier(margin_multiplier);
928 let tag = ws.tag;
929 if state.windows.iter().any(|w| w.r#type == WindowType::Normal) {
930 let for_active_workspace =
931 |x: &Window<H>| -> bool { tag == x.tag && x.r#type == WindowType::Normal };
932 let mut to_apply_margin_multiplier =
933 helpers::vec_extract(&mut state.windows, for_active_workspace);
934 for w in &mut to_apply_margin_multiplier {
935 if let Some(ws) = state.focus_manager.workspace(&state.workspaces) {
936 w.apply_margin_multiplier(ws.margin_multiplier());
937 }
938 }
939 state.windows.append(&mut to_apply_margin_multiplier);
940 }
941 Some(true)
942}
943
944fn send_workspace_to_tag<H: Handle>(
945 state: &mut State<H>,
946 ws_index: usize,
947 tag_index: usize,
948) -> bool {
949 if ws_index < state.workspaces.len() && tag_index < state.tags.len_normal() {
951 let workspace = &state.workspaces[ws_index].clone();
952 state.focus_workspace(workspace);
953 state.goto_tag_handler(tag_index + 1);
954 return true;
955 }
956 false
957}
958
959#[cfg(test)]
960mod tests {
961 use super::*;
962 use crate::models::{MockHandle, Tags};
963
964 fn mock_update(
965 manager: &mut Manager<
966 MockHandle,
967 crate::config::tests::TestConfig,
968 crate::display_servers::MockDisplayServer<MockHandle>,
969 >,
970 ) {
971 while let Some(act) = manager.state.actions.pop_front() {
972 if let DisplayAction::SetState(window_handle, toggle_to, window_state) = act {
973 if let Some(window) = manager
974 .state
975 .windows
976 .iter_mut()
977 .find(|w| w.handle == window_handle)
978 {
979 if window_state == WindowState::Fullscreen
980 || window_state == WindowState::Maximized
981 {
982 if toggle_to {
983 window.states = vec![window_state];
984 } else {
985 window.states.retain(|s| s != &window_state);
986 }
987 }
988 }
989 }
990 }
991 }
992
993 #[test]
994 fn toggle_fullscreen() {
995 let mut manager = Manager::new_test(vec!["1".to_string()]);
996 manager.screen_create_handler(Screen::default());
997
998 for i in 1..=3 {
999 manager.window_created_handler(
1000 Window::new(WindowHandle::<MockHandle>(i), None, None),
1001 -1,
1002 -1,
1003 );
1004 }
1005
1006 assert!(!manager
1007 .state
1008 .windows
1009 .iter()
1010 .any(|w| w.states.contains(&WindowState::Fullscreen)));
1011
1012 manager.command_handler(&Command::ToggleFullScreen);
1013
1014 mock_update(&mut manager);
1015 assert!(manager
1016 .state
1017 .windows
1018 .iter()
1019 .any(|w| w.states.contains(&WindowState::Fullscreen)));
1020 }
1021
1022 #[test]
1023 fn toggle_maximized() {
1024 let mut manager = Manager::new_test(vec!["1".to_string()]);
1025 manager.screen_create_handler(Screen::default());
1026
1027 for i in 1..=3 {
1028 manager.window_created_handler(
1029 Window::new(WindowHandle::<MockHandle>(i), None, None),
1030 -1,
1031 -1,
1032 );
1033 }
1034
1035 assert!(!manager
1036 .state
1037 .windows
1038 .iter()
1039 .any(|w| w.states.contains(&WindowState::Maximized)));
1040
1041 manager.command_handler(&Command::ToggleMaximized);
1042
1043 mock_update(&mut manager);
1044 assert!(manager
1045 .state
1046 .windows
1047 .iter()
1048 .any(|w| w.states.contains(&WindowState::Maximized)));
1049 }
1050
1051 #[test]
1052 fn fullscreen_window_sorting() {
1053 let mut manager = Manager::new_test(vec!["1".to_string()]);
1054 manager.screen_create_handler(Screen::default());
1055
1056 for i in 1..=4 {
1057 manager.window_created_handler(
1058 Window::new(WindowHandle::<MockHandle>(i), None, None),
1059 -1,
1060 -1,
1061 );
1062 }
1063
1064 manager.state.focus_window(&WindowHandle::<MockHandle>(2));
1065 manager.command_handler(&Command::ToggleMaximized);
1066
1067 manager.state.focus_window(&WindowHandle::<MockHandle>(3));
1068 manager.command_handler(&Command::ToggleFullScreen);
1069
1070 mock_update(&mut manager);
1071 manager.state.sort_windows();
1072
1073 let expected_order = vec![
1075 WindowHandle::<MockHandle>(3),
1076 WindowHandle::<MockHandle>(2),
1077 WindowHandle::<MockHandle>(1),
1078 WindowHandle::<MockHandle>(4),
1079 ];
1080
1081 match manager.state.actions.front().unwrap() {
1082 DisplayAction::SetWindowOrder(order) => assert_eq!(order, &expected_order),
1083 _ => unreachable!("No other update should be left"),
1084 }
1085 }
1086
1087 #[test]
1088 fn return_to_last_tag_should_go_back_to_last_tag() {
1089 let mut manager = Manager::new_test(vec![
1090 "A15".to_string(),
1091 "B24".to_string(),
1092 "C".to_string(),
1093 "6D4".to_string(),
1094 "E39".to_string(),
1095 "F67".to_string(),
1096 ]);
1097 manager.screen_create_handler(Screen::default());
1098 manager.screen_create_handler(Screen::default());
1099
1100 assert!(manager.command_handler(&Command::GoToTag {
1101 tag: 1,
1102 swap: false
1103 }));
1104 let current_tag = manager.state.focus_manager.tag(0).unwrap();
1105 assert_eq!(current_tag, 1);
1106
1107 assert!(manager.command_handler(&Command::GoToTag {
1108 tag: 2,
1109 swap: false
1110 }));
1111 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1112 assert_eq!(current_tag, 2);
1113
1114 manager.command_handler(&Command::ReturnToLastTag);
1115 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1116 assert_eq!(current_tag, 1);
1117 }
1118
1119 #[test]
1120 fn go_to_tag_should_return_false_if_no_screen_is_created() {
1121 let mut manager = Manager::new_test(vec![]);
1122 assert!(!manager.command_handler(&Command::GoToTag {
1124 tag: 6,
1125 swap: false
1126 }));
1127 assert!(!manager.command_handler(&Command::GoToTag {
1128 tag: 2,
1129 swap: false
1130 }));
1131 assert!(!manager.command_handler(&Command::GoToTag {
1132 tag: 15,
1133 swap: false
1134 }));
1135 }
1136
1137 #[test]
1138 fn go_to_tag_should_create_at_least_one_tag_per_screen_no_more() {
1139 let mut manager = Manager::new_test(vec![]);
1140 manager.screen_create_handler(Screen::default());
1141 manager.screen_create_handler(Screen::default());
1142 assert!(manager.command_handler(&Command::GoToTag {
1144 tag: 2,
1145 swap: false
1146 }));
1147 assert!(manager.command_handler(&Command::GoToTag {
1148 tag: 1,
1149 swap: false
1150 }));
1151 assert!(!manager.command_handler(&Command::GoToTag {
1153 tag: 3,
1154 swap: false
1155 }));
1156 }
1157
1158 #[test]
1159 fn go_to_tag_should_return_false_on_invalid_input() {
1160 let mut manager = Manager::new_test(vec![]);
1161 manager.screen_create_handler(Screen::default());
1162 manager.state.tags = Tags::new();
1163 manager.state.tags.add_new("A15");
1164 manager.state.tags.add_new("B24");
1165 manager.state.tags.add_new("C");
1166 manager.state.tags.add_new("6D4");
1167 manager.state.tags.add_new("E39");
1168 manager.state.tags.add_new("F67");
1169 assert!(!manager.command_handler(&Command::GoToTag {
1170 tag: 0,
1171 swap: false
1172 }));
1173 assert!(!manager.command_handler(&Command::GoToTag {
1174 tag: 999,
1175 swap: false
1176 }));
1177 }
1178
1179 #[test]
1180 fn go_to_tag_should_go_to_tag_and_set_history() {
1181 let mut manager = Manager::new_test(vec![
1182 "A15".to_string(),
1183 "B24".to_string(),
1184 "C".to_string(),
1185 "6D4".to_string(),
1186 "E39".to_string(),
1187 "F67".to_string(),
1188 ]);
1189 manager.screen_create_handler(Screen::default());
1190 manager.screen_create_handler(Screen::default());
1191
1192 assert!(manager.command_handler(&Command::GoToTag {
1193 tag: 6,
1194 swap: false
1195 }));
1196 let current_tag = manager.state.focus_manager.tag(0).unwrap();
1197 assert_eq!(current_tag, 6);
1198
1199 assert!(manager.command_handler(&Command::GoToTag {
1200 tag: 2,
1201 swap: false
1202 }));
1203 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1204 assert_eq!(current_tag, 2);
1205
1206 assert!(manager.command_handler(&Command::GoToTag {
1207 tag: 3,
1208 swap: false
1209 }));
1210 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1211 assert_eq!(current_tag, 3);
1212
1213 assert!(manager.command_handler(&Command::GoToTag {
1214 tag: 4,
1215 swap: false
1216 }));
1217 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1218 assert_eq!(current_tag, 4);
1219
1220 assert_eq!(manager.state.focus_manager.tag(1).unwrap_or_default(), 3);
1222 assert_eq!(manager.state.focus_manager.tag(2).unwrap_or_default(), 2);
1223 assert_eq!(manager.state.focus_manager.tag(3).unwrap_or_default(), 6);
1224 }
1225
1226 #[test]
1227 fn focus_tag_change_should_go_to_previous_and_next_tag() {
1228 let mut manager = Manager::new_test(vec![
1229 "A15".to_string(),
1230 "B24".to_string(),
1231 "C".to_string(),
1232 "6D4".to_string(),
1233 "E39".to_string(),
1234 "F67".to_string(),
1235 ]);
1236 manager.screen_create_handler(Screen::default());
1237 let state = &mut manager.state;
1238
1239 state.focus_tag(&2);
1240 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1241
1242 focus_tag_change(state, 1);
1243 assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
1244
1245 focus_tag_change(state, -1);
1246 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1247
1248 focus_tag_change(state, 2);
1249 assert_eq!(state.focus_manager.tag(0).unwrap(), 4);
1250
1251 focus_tag_change(state, -5);
1252 assert_eq!(state.focus_manager.tag(0).unwrap(), 5);
1253
1254 focus_tag_change(state, 3);
1255 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1256
1257 focus_tag_change(state, 13);
1258 assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
1259 }
1260
1261 #[test]
1262 fn focus_window_top() {
1263 let mut manager = Manager::new_test(vec![]);
1264 manager.screen_create_handler(Screen::default());
1265
1266 manager.window_created_handler(
1267 Window::new(WindowHandle::<MockHandle>(1), None, None),
1268 -1,
1269 -1,
1270 );
1271 manager.window_created_handler(
1272 Window::new(WindowHandle::<MockHandle>(2), None, None),
1273 -1,
1274 -1,
1275 );
1276 manager.window_created_handler(
1277 Window::new(WindowHandle::<MockHandle>(3), None, None),
1278 -1,
1279 -1,
1280 );
1281
1282 let expected = manager.state.windows[0].clone();
1283 let initial = manager.state.windows[1].clone();
1284
1285 manager.state.focus_window(&initial.handle);
1286
1287 manager.command_handler(&Command::FocusWindowTop { swap: false });
1288 let actual = manager
1289 .state
1290 .focus_manager
1291 .window(&manager.state.windows)
1292 .unwrap()
1293 .handle;
1294 assert_eq!(expected.handle, actual);
1295
1296 manager.command_handler(&Command::FocusWindowTop { swap: false });
1297 let actual = manager
1298 .state
1299 .focus_manager
1300 .window(&manager.state.windows)
1301 .unwrap()
1302 .handle;
1303 assert_eq!(expected.handle, actual);
1304
1305 manager.command_handler(&Command::FocusWindowTop { swap: true });
1306 let actual = manager
1307 .state
1308 .focus_manager
1309 .window(&manager.state.windows)
1310 .unwrap()
1311 .handle;
1312 assert_eq!(initial.handle, actual);
1313 }
1314
1315 #[test]
1316 fn move_window_top() {
1317 let mut manager = Manager::new_test(vec![]);
1318 manager.screen_create_handler(Screen::default());
1319
1320 manager.window_created_handler(
1321 Window::new(WindowHandle::<MockHandle>(1), None, None),
1322 -1,
1323 -1,
1324 );
1325 manager.window_created_handler(
1326 Window::new(WindowHandle::<MockHandle>(2), None, None),
1327 -1,
1328 -1,
1329 );
1330 manager.window_created_handler(
1331 Window::new(WindowHandle::<MockHandle>(3), None, None),
1332 -1,
1333 -1,
1334 );
1335
1336 let expected = manager.state.windows[0].clone();
1337 let initial = manager.state.windows[1].clone();
1338
1339 manager.state.focus_window(&initial.handle);
1340
1341 manager.command_handler(&Command::MoveWindowTop { swap: false });
1342 assert_eq!(manager.state.windows[0].handle, initial.handle);
1343
1344 manager.command_handler(&Command::MoveWindowTop { swap: false });
1345 assert_eq!(manager.state.windows[0].handle, initial.handle);
1346
1347 manager.command_handler(&Command::MoveWindowTop { swap: true });
1348 assert_eq!(manager.state.windows[0].handle, expected.handle);
1349 }
1350
1351 #[test]
1352 fn move_window_to_next_or_prev_tag_should_be_able_to_cycle() {
1353 let mut manager = Manager::new_test(vec![
1354 "AO".to_string(),
1355 "EU".to_string(),
1356 "ID".to_string(),
1357 "HT".to_string(),
1358 "NS".to_string(),
1359 ]);
1360 manager.screen_create_handler(Screen::default());
1361 manager.window_created_handler(
1362 Window::new(WindowHandle::<MockHandle>(1), None, None),
1363 -1,
1364 -1,
1365 );
1366
1367 let first_tag = manager.state.tags.get(1).unwrap().id;
1368 let third_tag = manager.state.tags.get(3).unwrap().id;
1369 let last_tag = manager.state.tags.get(5).unwrap().id;
1370
1371 assert!(manager.state.windows[0].has_tag(&first_tag));
1372
1373 manager.command_handler(&Command::MoveWindowToPreviousTag { follow: true });
1374 assert!(manager.state.windows[0].has_tag(&last_tag));
1375
1376 (0..3).for_each(|_| {
1377 manager.command_handler(&Command::MoveWindowToNextTag { follow: false });
1378 manager.command_handler(&Command::FocusNextTag {
1379 behavior: FocusDeltaBehavior::Default,
1380 });
1381 });
1382 assert!(manager.state.windows[0].has_tag(&third_tag));
1383 }
1384
1385 #[test]
1386 fn move_window_to_next_or_prev_tag_should_be_able_to_keep_window_focused() {
1387 let mut manager = Manager::new_test(vec!["AO".to_string(), "EU".to_string()]);
1388 manager.screen_create_handler(Screen::default());
1389 manager.window_created_handler(
1390 Window::new(WindowHandle::<MockHandle>(1), None, None),
1391 -1,
1392 -1,
1393 );
1394 manager.window_created_handler(
1395 Window::new(WindowHandle::<MockHandle>(2), None, None),
1396 -1,
1397 -1,
1398 );
1399 let expected_tag = manager.state.tags.get(2).unwrap().id;
1400 manager.command_handler(&Command::SendWindowToTag {
1401 window: None,
1402 tag: expected_tag,
1403 });
1404 let initial = manager.state.windows[0].clone();
1405
1406 manager.command_handler(&Command::MoveWindowToNextTag { follow: true });
1407
1408 assert_eq!(
1409 *manager.state.focus_manager.tag_history.front().unwrap(),
1410 expected_tag
1411 );
1412 assert_eq!(manager.state.windows[0].handle, initial.handle);
1413 }
1414
1415 #[test]
1416 fn after_moving_second_window_remaining_single_window_has_no_border() {
1417 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
1418 manager.screen_create_handler(Screen::default());
1419
1420 manager.window_created_handler(
1421 Window::new(WindowHandle::<MockHandle>(1), None, None),
1422 -1,
1423 -1,
1424 );
1425 manager.window_created_handler(
1426 Window::new(WindowHandle::<MockHandle>(2), None, None),
1427 -1,
1428 -1,
1429 );
1430
1431 let first_tag = manager.state.tags.get(1).unwrap().id;
1432 assert!(manager.state.windows[0].has_tag(&first_tag));
1433 assert!(manager.state.windows[0].border() > 0);
1434
1435 let second_tag = manager.state.tags.get(2).unwrap().id;
1436 assert!(manager.command_handler(&Command::SendWindowToTag {
1437 window: Some(manager.state.windows[0].handle),
1438 tag: second_tag,
1439 }));
1440
1441 assert_eq!(manager.state.windows[0].border(), 0);
1442 }
1443
1444 #[test]
1445 fn after_moving_single_window_to_another_single_window_both_have_borders() {
1446 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
1447 manager.screen_create_handler(Screen::default());
1448
1449 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1450 first_window.tag(&1);
1451 manager.window_created_handler(first_window, -1, -1);
1452
1453 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1454 second_window.tag(&2);
1455 manager.window_created_handler(second_window, -1, -1);
1456
1457 let second_tag = manager.state.tags.get(2).unwrap().id;
1458 assert!(manager.command_handler(&Command::SendWindowToTag {
1459 window: Some(manager.state.windows[0].handle),
1460 tag: second_tag,
1461 }));
1462
1463 assert_eq!(manager.state.windows[0].border(), 1);
1464 assert_eq!(manager.state.windows[1].border(), 1);
1465 }
1466
1467 #[test]
1468
1469 fn goto_next_empty_tag_while_in_used_tag() {
1470 let mut manager: Manager<
1471 MockHandle,
1472 crate::config::tests::TestConfig,
1473 crate::display_servers::MockDisplayServer<MockHandle>,
1474 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1475 manager.screen_create_handler(Screen::default());
1476
1477 manager.state.focus_tag(&1);
1478
1479 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1480 first_window.tag(&1);
1481 manager.window_created_handler(first_window, -1, -1);
1482
1483 assert!(manager.command_handler(&Command::FocusNextTag {
1484 behavior: FocusDeltaBehavior::IgnoreUsed
1485 }));
1486
1487 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1488 }
1489
1490 #[test]
1491 fn goto_next_empty_tag_while_in_empty_tag() {
1492 let mut manager: Manager<
1493 MockHandle,
1494 crate::config::tests::TestConfig,
1495 crate::display_servers::MockDisplayServer<MockHandle>,
1496 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Empty".to_string()]);
1497 manager.screen_create_handler(Screen::default());
1498
1499 manager.state.focus_tag(&1);
1500
1501 assert!(manager.command_handler(&Command::FocusNextTag {
1502 behavior: FocusDeltaBehavior::IgnoreUsed
1503 }));
1504
1505 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1506 }
1507
1508 #[test]
1509 fn goto_next_empty_tag_multiple_tags() {
1510 let mut manager: Manager<
1511 MockHandle,
1512 crate::config::tests::TestConfig,
1513 crate::display_servers::MockDisplayServer<MockHandle>,
1514 > = Manager::new_test(vec![
1515 "A".to_string(),
1516 "B".to_string(),
1517 "C".to_string(),
1518 "D".to_string(),
1519 ]);
1520 manager.screen_create_handler(Screen::default());
1521
1522 manager.state.focus_tag(&1);
1523
1524 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1525 first_window.tag(&1);
1526 manager.window_created_handler(first_window, -1, -1);
1527
1528 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1529 second_window.tag(&1);
1530 manager.window_created_handler(second_window, -1, -1);
1531
1532 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1533 third_window.tag(&2);
1534 let third_window_handle = third_window.handle;
1535 manager.window_created_handler(third_window, -1, -1);
1536
1537 assert!(manager.command_handler(&Command::FocusNextTag {
1538 behavior: FocusDeltaBehavior::IgnoreUsed
1539 }));
1540
1541 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1542
1543 assert!(manager.command_handler(&Command::FocusNextTag {
1544 behavior: FocusDeltaBehavior::IgnoreUsed
1545 }));
1546
1547 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1548
1549 assert!(manager.command_handler(&Command::FocusNextTag {
1550 behavior: FocusDeltaBehavior::IgnoreUsed
1551 }));
1552
1553 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1554
1555 manager.window_destroyed_handler(&third_window_handle);
1556
1557 assert!(manager.command_handler(&Command::FocusNextTag {
1558 behavior: FocusDeltaBehavior::IgnoreUsed
1559 }));
1560 assert!(manager.command_handler(&Command::FocusNextTag {
1561 behavior: FocusDeltaBehavior::IgnoreUsed
1562 }));
1563
1564 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1565 }
1566
1567 #[test]
1568
1569 fn goto_previous_empty_tag_while_in_used_tag() {
1570 let mut manager: Manager<
1571 MockHandle,
1572 crate::config::tests::TestConfig,
1573 crate::display_servers::MockDisplayServer<MockHandle>,
1574 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1575 manager.screen_create_handler(Screen::default());
1576
1577 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1578 first_window.tag(&2);
1579 manager.window_created_handler(first_window, -1, -1);
1580
1581 manager.state.focus_tag(&2);
1582
1583 assert!(manager.command_handler(&Command::FocusPreviousTag {
1584 behavior: FocusDeltaBehavior::IgnoreUsed
1585 }));
1586
1587 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1588 }
1589
1590 #[test]
1591 fn goto_previous_empty_tag_while_in_empty_tag() {
1592 let mut manager: Manager<
1593 MockHandle,
1594 crate::config::tests::TestConfig,
1595 crate::display_servers::MockDisplayServer<MockHandle>,
1596 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Empty".to_string()]);
1597 manager.screen_create_handler(Screen::default());
1598
1599 manager.state.focus_tag(&2);
1600
1601 assert!(manager.command_handler(&Command::FocusPreviousTag {
1602 behavior: FocusDeltaBehavior::IgnoreUsed
1603 }));
1604
1605 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1606 }
1607
1608 #[test]
1609 fn goto_previous_empty_tag_multiple_tags() {
1610 let mut manager: Manager<
1611 MockHandle,
1612 crate::config::tests::TestConfig,
1613 crate::display_servers::MockDisplayServer<MockHandle>,
1614 > = Manager::new_test(vec![
1615 "A".to_string(),
1616 "B".to_string(),
1617 "C".to_string(),
1618 "D".to_string(),
1619 ]);
1620 manager.screen_create_handler(Screen::default());
1621
1622 manager.state.focus_tag(&1);
1623
1624 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1625 first_window.tag(&1);
1626 manager.window_created_handler(first_window, -1, -1);
1627
1628 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1629 second_window.tag(&1);
1630 manager.window_created_handler(second_window, -1, -1);
1631
1632 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1633 third_window.tag(&2);
1634 let third_window_handle = third_window.handle;
1635 manager.window_created_handler(third_window, -1, -1);
1636
1637 assert!(manager.command_handler(&Command::FocusPreviousTag {
1638 behavior: FocusDeltaBehavior::IgnoreUsed
1639 }));
1640
1641 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1642
1643 assert!(manager.command_handler(&Command::FocusPreviousTag {
1644 behavior: FocusDeltaBehavior::IgnoreUsed
1645 }));
1646
1647 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1648
1649 assert!(manager.command_handler(&Command::FocusPreviousTag {
1650 behavior: FocusDeltaBehavior::IgnoreUsed
1651 }));
1652
1653 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1654
1655 manager.window_destroyed_handler(&third_window_handle);
1656 assert!(manager.command_handler(&Command::FocusPreviousTag {
1657 behavior: FocusDeltaBehavior::IgnoreUsed
1658 }));
1659 assert!(manager.command_handler(&Command::FocusPreviousTag {
1660 behavior: FocusDeltaBehavior::IgnoreUsed
1661 }));
1662
1663 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1664 }
1665
1666 #[test]
1667
1668 fn goto_next_used_tag_while_in_used_tag() {
1669 let mut manager: Manager<
1670 MockHandle,
1671 crate::config::tests::TestConfig,
1672 crate::display_servers::MockDisplayServer<MockHandle>,
1673 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1674 manager.screen_create_handler(Screen::default());
1675
1676 manager.state.focus_tag(&1);
1677
1678 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1679 first_window.tag(&1);
1680 manager.window_created_handler(first_window, -1, -1);
1681
1682 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1683 second_window.tag(&2);
1684 manager.window_created_handler(second_window, -1, -1);
1685
1686 assert!(manager.command_handler(&Command::FocusNextTag {
1687 behavior: FocusDeltaBehavior::IgnoreEmpty
1688 }));
1689
1690 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1691 }
1692
1693 #[test]
1694 fn goto_next_used_tag_while_in_empty_tag() {
1695 let mut manager: Manager<
1696 MockHandle,
1697 crate::config::tests::TestConfig,
1698 crate::display_servers::MockDisplayServer<MockHandle>,
1699 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Used".to_string()]);
1700 manager.screen_create_handler(Screen::default());
1701
1702 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1703 first_window.tag(&2);
1704 manager.window_created_handler(first_window, -1, -1);
1705
1706 manager.state.focus_tag(&1);
1707
1708 assert!(manager.command_handler(&Command::FocusNextTag {
1709 behavior: FocusDeltaBehavior::IgnoreEmpty
1710 }));
1711
1712 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1713 }
1714
1715 #[test]
1716 fn goto_next_used_tag_multiple_tags() {
1717 let mut manager: Manager<
1718 MockHandle,
1719 crate::config::tests::TestConfig,
1720 crate::display_servers::MockDisplayServer<MockHandle>,
1721 > = Manager::new_test(vec![
1722 "A".to_string(),
1723 "B".to_string(),
1724 "C".to_string(),
1725 "D".to_string(),
1726 ]);
1727 manager.screen_create_handler(Screen::default());
1728
1729 manager.state.focus_tag(&1);
1730
1731 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1732 first_window.tag(&1);
1733 manager.window_created_handler(first_window, -1, -1);
1734
1735 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1736 second_window.tag(&2);
1737 manager.window_created_handler(second_window, -1, -1);
1738
1739 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1740 third_window.tag(&4);
1741 let third_window_handle = third_window.handle;
1742 manager.window_created_handler(third_window, -1, -1);
1743
1744 assert!(manager.command_handler(&Command::FocusNextTag {
1745 behavior: FocusDeltaBehavior::IgnoreEmpty
1746 }));
1747
1748 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1749
1750 assert!(manager.command_handler(&Command::FocusNextTag {
1751 behavior: FocusDeltaBehavior::IgnoreEmpty
1752 }));
1753
1754 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1755
1756 assert!(manager.command_handler(&Command::FocusNextTag {
1757 behavior: FocusDeltaBehavior::IgnoreEmpty
1758 }));
1759
1760 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1761
1762 manager.window_destroyed_handler(&third_window_handle);
1763
1764 assert!(manager.command_handler(&Command::FocusNextTag {
1765 behavior: FocusDeltaBehavior::IgnoreEmpty
1766 }));
1767 assert!(manager.command_handler(&Command::FocusNextTag {
1768 behavior: FocusDeltaBehavior::IgnoreEmpty
1769 }));
1770
1771 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1772 }
1773
1774 #[test]
1775
1776 fn goto_previous_used_tag_while_in_used_tag() {
1777 let mut manager: Manager<
1778 MockHandle,
1779 crate::config::tests::TestConfig,
1780 crate::display_servers::MockDisplayServer<MockHandle>,
1781 > = Manager::new_test(vec!["A".to_string(), "B".to_string(), "C".to_string()]);
1782 manager.screen_create_handler(Screen::default());
1783
1784 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1785 first_window.tag(&1);
1786 manager.window_created_handler(first_window, -1, -1);
1787
1788 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1789 second_window.tag(&2);
1790 manager.window_created_handler(second_window, -1, -1);
1791
1792 manager.state.focus_tag(&2);
1793
1794 assert!(manager.command_handler(&Command::FocusPreviousTag {
1795 behavior: FocusDeltaBehavior::IgnoreEmpty
1796 }));
1797
1798 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1799 }
1800
1801 #[test]
1802 fn goto_previous_used_tag_while_in_empty_tag() {
1803 let mut manager: Manager<
1804 MockHandle,
1805 crate::config::tests::TestConfig,
1806 crate::display_servers::MockDisplayServer<MockHandle>,
1807 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Used".to_string()]);
1808 manager.screen_create_handler(Screen::default());
1809
1810 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1811 first_window.tag(&2);
1812 manager.window_created_handler(first_window, -1, -1);
1813
1814 manager.state.focus_tag(&1);
1815
1816 assert!(manager.command_handler(&Command::FocusPreviousTag {
1817 behavior: FocusDeltaBehavior::IgnoreEmpty
1818 }));
1819
1820 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1821 }
1822
1823 #[test]
1824 fn goto_previous_used_tag_multiple_tags() {
1825 let mut manager: Manager<
1826 MockHandle,
1827 crate::config::tests::TestConfig,
1828 crate::display_servers::MockDisplayServer<MockHandle>,
1829 > = Manager::new_test(vec![
1830 "A".to_string(),
1831 "B".to_string(),
1832 "C".to_string(),
1833 "D".to_string(),
1834 "E".to_string(),
1835 ]);
1836 manager.screen_create_handler(Screen::default());
1837
1838 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1839 first_window.tag(&1);
1840 manager.window_created_handler(first_window, -1, -1);
1841
1842 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1843 second_window.tag(&2);
1844 manager.window_created_handler(second_window, -1, -1);
1845
1846 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1847 third_window.tag(&4);
1848 let third_window_handle = third_window.handle;
1849 manager.window_created_handler(third_window, -1, -1);
1850
1851 manager.state.focus_tag(&4);
1852
1853 assert!(manager.command_handler(&Command::FocusPreviousTag {
1854 behavior: FocusDeltaBehavior::IgnoreEmpty
1855 }));
1856
1857 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1858
1859 assert!(manager.command_handler(&Command::FocusPreviousTag {
1860 behavior: FocusDeltaBehavior::IgnoreEmpty
1861 }));
1862
1863 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1864
1865 assert!(manager.command_handler(&Command::FocusPreviousTag {
1866 behavior: FocusDeltaBehavior::IgnoreEmpty
1867 }));
1868
1869 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1870
1871 manager.window_destroyed_handler(&third_window_handle);
1872
1873 assert!(manager.command_handler(&Command::FocusPreviousTag {
1874 behavior: FocusDeltaBehavior::IgnoreEmpty
1875 }));
1876 assert!(manager.command_handler(&Command::FocusPreviousTag {
1877 behavior: FocusDeltaBehavior::IgnoreEmpty
1878 }));
1879
1880 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1881 }
1882
1883 #[test]
1884 fn goto_previous_used_tag_wraparound() {
1885 let mut manager: Manager<
1886 MockHandle,
1887 crate::config::tests::TestConfig,
1888 crate::display_servers::MockDisplayServer<MockHandle>,
1889 > = Manager::new_test(vec!["A".to_string(), "B".to_string(), "C".to_string()]);
1890 manager.screen_create_handler(Screen::default());
1891
1892 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1893 first_window.tag(&3);
1894 manager.window_created_handler(first_window, -1, -1);
1895
1896 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1897 second_window.tag(&2);
1898 manager.window_created_handler(second_window, -1, -1);
1899
1900 manager.state.focus_tag(&2);
1901
1902 assert!(manager.command_handler(&Command::FocusPreviousTag {
1903 behavior: FocusDeltaBehavior::IgnoreEmpty
1904 }));
1905
1906 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1907 }
1908}