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_2021 $(, $arg:expr_2021 )* $(,)? )) => {{
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 && let Some(&Some(h)) = focused_window
586 {
587 let mut tags_windows = state
588 .windows
589 .iter()
590 .filter(|w| w.has_tag(&tag_id) && w.is_managed() && !w.floating());
591
592 let mw = tags_windows.next();
593 let tdw = tags_windows.next();
594
595 if let (Some(mw), Some(tdw)) = (mw, tdw) {
596 if mw.handle != h && tdw.handle != h {
599 to_focus = Some(tdw);
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 && let Some(window) = manager
974 .state
975 .windows
976 .iter_mut()
977 .find(|w| w.handle == window_handle)
978 && (window_state == WindowState::Fullscreen
979 || window_state == WindowState::Maximized)
980 {
981 if toggle_to {
982 window.states = vec![window_state];
983 } else {
984 window.states.retain(|s| s != &window_state);
985 }
986 }
987 }
988 }
989
990 #[test]
991 fn toggle_fullscreen() {
992 let mut manager = Manager::new_test(vec!["1".to_string()]);
993 manager.screen_create_handler(Screen::default());
994
995 for i in 1..=3 {
996 manager.window_created_handler(
997 Window::new(WindowHandle::<MockHandle>(i), None, None),
998 -1,
999 -1,
1000 );
1001 }
1002
1003 assert!(
1004 !manager
1005 .state
1006 .windows
1007 .iter()
1008 .any(|w| w.states.contains(&WindowState::Fullscreen))
1009 );
1010
1011 manager.command_handler(&Command::ToggleFullScreen);
1012
1013 mock_update(&mut manager);
1014 assert!(
1015 manager
1016 .state
1017 .windows
1018 .iter()
1019 .any(|w| w.states.contains(&WindowState::Fullscreen))
1020 );
1021 }
1022
1023 #[test]
1024 fn toggle_maximized() {
1025 let mut manager = Manager::new_test(vec!["1".to_string()]);
1026 manager.screen_create_handler(Screen::default());
1027
1028 for i in 1..=3 {
1029 manager.window_created_handler(
1030 Window::new(WindowHandle::<MockHandle>(i), None, None),
1031 -1,
1032 -1,
1033 );
1034 }
1035
1036 assert!(
1037 !manager
1038 .state
1039 .windows
1040 .iter()
1041 .any(|w| w.states.contains(&WindowState::Maximized))
1042 );
1043
1044 manager.command_handler(&Command::ToggleMaximized);
1045
1046 mock_update(&mut manager);
1047 assert!(
1048 manager
1049 .state
1050 .windows
1051 .iter()
1052 .any(|w| w.states.contains(&WindowState::Maximized))
1053 );
1054 }
1055
1056 #[test]
1057 fn fullscreen_window_sorting() {
1058 let mut manager = Manager::new_test(vec!["1".to_string()]);
1059 manager.screen_create_handler(Screen::default());
1060
1061 for i in 1..=4 {
1062 manager.window_created_handler(
1063 Window::new(WindowHandle::<MockHandle>(i), None, None),
1064 -1,
1065 -1,
1066 );
1067 }
1068
1069 manager.state.focus_window(&WindowHandle::<MockHandle>(2));
1070 manager.command_handler(&Command::ToggleMaximized);
1071
1072 manager.state.focus_window(&WindowHandle::<MockHandle>(3));
1073 manager.command_handler(&Command::ToggleFullScreen);
1074
1075 mock_update(&mut manager);
1076 manager.state.sort_windows();
1077
1078 let expected_order = vec![
1080 WindowHandle::<MockHandle>(3),
1081 WindowHandle::<MockHandle>(2),
1082 WindowHandle::<MockHandle>(1),
1083 WindowHandle::<MockHandle>(4),
1084 ];
1085
1086 match manager.state.actions.front().unwrap() {
1087 DisplayAction::SetWindowOrder(order) => assert_eq!(order, &expected_order),
1088 _ => unreachable!("No other update should be left"),
1089 }
1090 }
1091
1092 #[test]
1093 fn return_to_last_tag_should_go_back_to_last_tag() {
1094 let mut manager = Manager::new_test(vec![
1095 "A15".to_string(),
1096 "B24".to_string(),
1097 "C".to_string(),
1098 "6D4".to_string(),
1099 "E39".to_string(),
1100 "F67".to_string(),
1101 ]);
1102 manager.screen_create_handler(Screen::default());
1103 manager.screen_create_handler(Screen::default());
1104
1105 assert!(manager.command_handler(&Command::GoToTag {
1106 tag: 1,
1107 swap: false
1108 }));
1109 let current_tag = manager.state.focus_manager.tag(0).unwrap();
1110 assert_eq!(current_tag, 1);
1111
1112 assert!(manager.command_handler(&Command::GoToTag {
1113 tag: 2,
1114 swap: false
1115 }));
1116 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1117 assert_eq!(current_tag, 2);
1118
1119 manager.command_handler(&Command::ReturnToLastTag);
1120 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1121 assert_eq!(current_tag, 1);
1122 }
1123
1124 #[test]
1125 fn go_to_tag_should_return_false_if_no_screen_is_created() {
1126 let mut manager = Manager::new_test(vec![]);
1127 assert!(!manager.command_handler(&Command::GoToTag {
1129 tag: 6,
1130 swap: false
1131 }));
1132 assert!(!manager.command_handler(&Command::GoToTag {
1133 tag: 2,
1134 swap: false
1135 }));
1136 assert!(!manager.command_handler(&Command::GoToTag {
1137 tag: 15,
1138 swap: false
1139 }));
1140 }
1141
1142 #[test]
1143 fn go_to_tag_should_create_at_least_one_tag_per_screen_no_more() {
1144 let mut manager = Manager::new_test(vec![]);
1145 manager.screen_create_handler(Screen::default());
1146 manager.screen_create_handler(Screen::default());
1147 assert!(manager.command_handler(&Command::GoToTag {
1149 tag: 2,
1150 swap: false
1151 }));
1152 assert!(manager.command_handler(&Command::GoToTag {
1153 tag: 1,
1154 swap: false
1155 }));
1156 assert!(!manager.command_handler(&Command::GoToTag {
1158 tag: 3,
1159 swap: false
1160 }));
1161 }
1162
1163 #[test]
1164 fn go_to_tag_should_return_false_on_invalid_input() {
1165 let mut manager = Manager::new_test(vec![]);
1166 manager.screen_create_handler(Screen::default());
1167 manager.state.tags = Tags::new();
1168 manager.state.tags.add_new("A15");
1169 manager.state.tags.add_new("B24");
1170 manager.state.tags.add_new("C");
1171 manager.state.tags.add_new("6D4");
1172 manager.state.tags.add_new("E39");
1173 manager.state.tags.add_new("F67");
1174 assert!(!manager.command_handler(&Command::GoToTag {
1175 tag: 0,
1176 swap: false
1177 }));
1178 assert!(!manager.command_handler(&Command::GoToTag {
1179 tag: 999,
1180 swap: false
1181 }));
1182 }
1183
1184 #[test]
1185 fn go_to_tag_should_go_to_tag_and_set_history() {
1186 let mut manager = Manager::new_test(vec![
1187 "A15".to_string(),
1188 "B24".to_string(),
1189 "C".to_string(),
1190 "6D4".to_string(),
1191 "E39".to_string(),
1192 "F67".to_string(),
1193 ]);
1194 manager.screen_create_handler(Screen::default());
1195 manager.screen_create_handler(Screen::default());
1196
1197 assert!(manager.command_handler(&Command::GoToTag {
1198 tag: 6,
1199 swap: false
1200 }));
1201 let current_tag = manager.state.focus_manager.tag(0).unwrap();
1202 assert_eq!(current_tag, 6);
1203
1204 assert!(manager.command_handler(&Command::GoToTag {
1205 tag: 2,
1206 swap: false
1207 }));
1208 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1209 assert_eq!(current_tag, 2);
1210
1211 assert!(manager.command_handler(&Command::GoToTag {
1212 tag: 3,
1213 swap: false
1214 }));
1215 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1216 assert_eq!(current_tag, 3);
1217
1218 assert!(manager.command_handler(&Command::GoToTag {
1219 tag: 4,
1220 swap: false
1221 }));
1222 let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
1223 assert_eq!(current_tag, 4);
1224
1225 assert_eq!(manager.state.focus_manager.tag(1).unwrap_or_default(), 3);
1227 assert_eq!(manager.state.focus_manager.tag(2).unwrap_or_default(), 2);
1228 assert_eq!(manager.state.focus_manager.tag(3).unwrap_or_default(), 6);
1229 }
1230
1231 #[test]
1232 fn focus_tag_change_should_go_to_previous_and_next_tag() {
1233 let mut manager = Manager::new_test(vec![
1234 "A15".to_string(),
1235 "B24".to_string(),
1236 "C".to_string(),
1237 "6D4".to_string(),
1238 "E39".to_string(),
1239 "F67".to_string(),
1240 ]);
1241 manager.screen_create_handler(Screen::default());
1242 let state = &mut manager.state;
1243
1244 state.focus_tag(&2);
1245 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1246
1247 focus_tag_change(state, 1);
1248 assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
1249
1250 focus_tag_change(state, -1);
1251 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1252
1253 focus_tag_change(state, 2);
1254 assert_eq!(state.focus_manager.tag(0).unwrap(), 4);
1255
1256 focus_tag_change(state, -5);
1257 assert_eq!(state.focus_manager.tag(0).unwrap(), 5);
1258
1259 focus_tag_change(state, 3);
1260 assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
1261
1262 focus_tag_change(state, 13);
1263 assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
1264 }
1265
1266 #[test]
1267 fn focus_window_top() {
1268 let mut manager = Manager::new_test(vec![]);
1269 manager.screen_create_handler(Screen::default());
1270
1271 manager.window_created_handler(
1272 Window::new(WindowHandle::<MockHandle>(1), None, None),
1273 -1,
1274 -1,
1275 );
1276 manager.window_created_handler(
1277 Window::new(WindowHandle::<MockHandle>(2), None, None),
1278 -1,
1279 -1,
1280 );
1281 manager.window_created_handler(
1282 Window::new(WindowHandle::<MockHandle>(3), None, None),
1283 -1,
1284 -1,
1285 );
1286
1287 let expected = manager.state.windows[0].clone();
1288 let initial = manager.state.windows[1].clone();
1289
1290 manager.state.focus_window(&initial.handle);
1291
1292 manager.command_handler(&Command::FocusWindowTop { swap: false });
1293 let actual = manager
1294 .state
1295 .focus_manager
1296 .window(&manager.state.windows)
1297 .unwrap()
1298 .handle;
1299 assert_eq!(expected.handle, actual);
1300
1301 manager.command_handler(&Command::FocusWindowTop { swap: false });
1302 let actual = manager
1303 .state
1304 .focus_manager
1305 .window(&manager.state.windows)
1306 .unwrap()
1307 .handle;
1308 assert_eq!(expected.handle, actual);
1309
1310 manager.command_handler(&Command::FocusWindowTop { swap: true });
1311 let actual = manager
1312 .state
1313 .focus_manager
1314 .window(&manager.state.windows)
1315 .unwrap()
1316 .handle;
1317 assert_eq!(initial.handle, actual);
1318 }
1319
1320 #[test]
1321 fn move_window_top() {
1322 let mut manager = Manager::new_test(vec![]);
1323 manager.screen_create_handler(Screen::default());
1324
1325 manager.window_created_handler(
1326 Window::new(WindowHandle::<MockHandle>(1), None, None),
1327 -1,
1328 -1,
1329 );
1330 manager.window_created_handler(
1331 Window::new(WindowHandle::<MockHandle>(2), None, None),
1332 -1,
1333 -1,
1334 );
1335 manager.window_created_handler(
1336 Window::new(WindowHandle::<MockHandle>(3), None, None),
1337 -1,
1338 -1,
1339 );
1340
1341 let expected = manager.state.windows[0].clone();
1342 let initial = manager.state.windows[1].clone();
1343
1344 manager.state.focus_window(&initial.handle);
1345
1346 manager.command_handler(&Command::MoveWindowTop { swap: false });
1347 assert_eq!(manager.state.windows[0].handle, initial.handle);
1348
1349 manager.command_handler(&Command::MoveWindowTop { swap: false });
1350 assert_eq!(manager.state.windows[0].handle, initial.handle);
1351
1352 manager.command_handler(&Command::MoveWindowTop { swap: true });
1353 assert_eq!(manager.state.windows[0].handle, expected.handle);
1354 }
1355
1356 #[test]
1357 fn move_window_to_next_or_prev_tag_should_be_able_to_cycle() {
1358 let mut manager = Manager::new_test(vec![
1359 "AO".to_string(),
1360 "EU".to_string(),
1361 "ID".to_string(),
1362 "HT".to_string(),
1363 "NS".to_string(),
1364 ]);
1365 manager.screen_create_handler(Screen::default());
1366 manager.window_created_handler(
1367 Window::new(WindowHandle::<MockHandle>(1), None, None),
1368 -1,
1369 -1,
1370 );
1371
1372 let first_tag = manager.state.tags.get(1).unwrap().id;
1373 let third_tag = manager.state.tags.get(3).unwrap().id;
1374 let last_tag = manager.state.tags.get(5).unwrap().id;
1375
1376 assert!(manager.state.windows[0].has_tag(&first_tag));
1377
1378 manager.command_handler(&Command::MoveWindowToPreviousTag { follow: true });
1379 assert!(manager.state.windows[0].has_tag(&last_tag));
1380
1381 (0..3).for_each(|_| {
1382 manager.command_handler(&Command::MoveWindowToNextTag { follow: false });
1383 manager.command_handler(&Command::FocusNextTag {
1384 behavior: FocusDeltaBehavior::Default,
1385 });
1386 });
1387 assert!(manager.state.windows[0].has_tag(&third_tag));
1388 }
1389
1390 #[test]
1391 fn move_window_to_next_or_prev_tag_should_be_able_to_keep_window_focused() {
1392 let mut manager = Manager::new_test(vec!["AO".to_string(), "EU".to_string()]);
1393 manager.screen_create_handler(Screen::default());
1394 manager.window_created_handler(
1395 Window::new(WindowHandle::<MockHandle>(1), None, None),
1396 -1,
1397 -1,
1398 );
1399 manager.window_created_handler(
1400 Window::new(WindowHandle::<MockHandle>(2), None, None),
1401 -1,
1402 -1,
1403 );
1404 let expected_tag = manager.state.tags.get(2).unwrap().id;
1405 manager.command_handler(&Command::SendWindowToTag {
1406 window: None,
1407 tag: expected_tag,
1408 });
1409 let initial = manager.state.windows[0].clone();
1410
1411 manager.command_handler(&Command::MoveWindowToNextTag { follow: true });
1412
1413 assert_eq!(
1414 *manager.state.focus_manager.tag_history.front().unwrap(),
1415 expected_tag
1416 );
1417 assert_eq!(manager.state.windows[0].handle, initial.handle);
1418 }
1419
1420 #[test]
1421 fn after_moving_second_window_remaining_single_window_has_no_border() {
1422 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
1423 manager.screen_create_handler(Screen::default());
1424
1425 manager.window_created_handler(
1426 Window::new(WindowHandle::<MockHandle>(1), None, None),
1427 -1,
1428 -1,
1429 );
1430 manager.window_created_handler(
1431 Window::new(WindowHandle::<MockHandle>(2), None, None),
1432 -1,
1433 -1,
1434 );
1435
1436 let first_tag = manager.state.tags.get(1).unwrap().id;
1437 assert!(manager.state.windows[0].has_tag(&first_tag));
1438 assert!(manager.state.windows[0].border() > 0);
1439
1440 let second_tag = manager.state.tags.get(2).unwrap().id;
1441 assert!(manager.command_handler(&Command::SendWindowToTag {
1442 window: Some(manager.state.windows[0].handle),
1443 tag: second_tag,
1444 }));
1445
1446 assert_eq!(manager.state.windows[0].border(), 0);
1447 }
1448
1449 #[test]
1450 fn after_moving_single_window_to_another_single_window_both_have_borders() {
1451 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
1452 manager.screen_create_handler(Screen::default());
1453
1454 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1455 first_window.tag(&1);
1456 manager.window_created_handler(first_window, -1, -1);
1457
1458 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1459 second_window.tag(&2);
1460 manager.window_created_handler(second_window, -1, -1);
1461
1462 let second_tag = manager.state.tags.get(2).unwrap().id;
1463 assert!(manager.command_handler(&Command::SendWindowToTag {
1464 window: Some(manager.state.windows[0].handle),
1465 tag: second_tag,
1466 }));
1467
1468 assert_eq!(manager.state.windows[0].border(), 1);
1469 assert_eq!(manager.state.windows[1].border(), 1);
1470 }
1471
1472 #[test]
1473
1474 fn goto_next_empty_tag_while_in_used_tag() {
1475 let mut manager: Manager<
1476 MockHandle,
1477 crate::config::tests::TestConfig,
1478 crate::display_servers::MockDisplayServer<MockHandle>,
1479 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1480 manager.screen_create_handler(Screen::default());
1481
1482 manager.state.focus_tag(&1);
1483
1484 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1485 first_window.tag(&1);
1486 manager.window_created_handler(first_window, -1, -1);
1487
1488 assert!(manager.command_handler(&Command::FocusNextTag {
1489 behavior: FocusDeltaBehavior::IgnoreUsed
1490 }));
1491
1492 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1493 }
1494
1495 #[test]
1496 fn goto_next_empty_tag_while_in_empty_tag() {
1497 let mut manager: Manager<
1498 MockHandle,
1499 crate::config::tests::TestConfig,
1500 crate::display_servers::MockDisplayServer<MockHandle>,
1501 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Empty".to_string()]);
1502 manager.screen_create_handler(Screen::default());
1503
1504 manager.state.focus_tag(&1);
1505
1506 assert!(manager.command_handler(&Command::FocusNextTag {
1507 behavior: FocusDeltaBehavior::IgnoreUsed
1508 }));
1509
1510 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1511 }
1512
1513 #[test]
1514 fn goto_next_empty_tag_multiple_tags() {
1515 let mut manager: Manager<
1516 MockHandle,
1517 crate::config::tests::TestConfig,
1518 crate::display_servers::MockDisplayServer<MockHandle>,
1519 > = Manager::new_test(vec![
1520 "A".to_string(),
1521 "B".to_string(),
1522 "C".to_string(),
1523 "D".to_string(),
1524 ]);
1525 manager.screen_create_handler(Screen::default());
1526
1527 manager.state.focus_tag(&1);
1528
1529 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1530 first_window.tag(&1);
1531 manager.window_created_handler(first_window, -1, -1);
1532
1533 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1534 second_window.tag(&1);
1535 manager.window_created_handler(second_window, -1, -1);
1536
1537 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1538 third_window.tag(&2);
1539 let third_window_handle = third_window.handle;
1540 manager.window_created_handler(third_window, -1, -1);
1541
1542 assert!(manager.command_handler(&Command::FocusNextTag {
1543 behavior: FocusDeltaBehavior::IgnoreUsed
1544 }));
1545
1546 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1547
1548 assert!(manager.command_handler(&Command::FocusNextTag {
1549 behavior: FocusDeltaBehavior::IgnoreUsed
1550 }));
1551
1552 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1553
1554 assert!(manager.command_handler(&Command::FocusNextTag {
1555 behavior: FocusDeltaBehavior::IgnoreUsed
1556 }));
1557
1558 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1559
1560 manager.window_destroyed_handler(&third_window_handle);
1561
1562 assert!(manager.command_handler(&Command::FocusNextTag {
1563 behavior: FocusDeltaBehavior::IgnoreUsed
1564 }));
1565 assert!(manager.command_handler(&Command::FocusNextTag {
1566 behavior: FocusDeltaBehavior::IgnoreUsed
1567 }));
1568
1569 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1570 }
1571
1572 #[test]
1573
1574 fn goto_previous_empty_tag_while_in_used_tag() {
1575 let mut manager: Manager<
1576 MockHandle,
1577 crate::config::tests::TestConfig,
1578 crate::display_servers::MockDisplayServer<MockHandle>,
1579 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1580 manager.screen_create_handler(Screen::default());
1581
1582 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1583 first_window.tag(&2);
1584 manager.window_created_handler(first_window, -1, -1);
1585
1586 manager.state.focus_tag(&2);
1587
1588 assert!(manager.command_handler(&Command::FocusPreviousTag {
1589 behavior: FocusDeltaBehavior::IgnoreUsed
1590 }));
1591
1592 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1593 }
1594
1595 #[test]
1596 fn goto_previous_empty_tag_while_in_empty_tag() {
1597 let mut manager: Manager<
1598 MockHandle,
1599 crate::config::tests::TestConfig,
1600 crate::display_servers::MockDisplayServer<MockHandle>,
1601 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Empty".to_string()]);
1602 manager.screen_create_handler(Screen::default());
1603
1604 manager.state.focus_tag(&2);
1605
1606 assert!(manager.command_handler(&Command::FocusPreviousTag {
1607 behavior: FocusDeltaBehavior::IgnoreUsed
1608 }));
1609
1610 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1611 }
1612
1613 #[test]
1614 fn goto_previous_empty_tag_multiple_tags() {
1615 let mut manager: Manager<
1616 MockHandle,
1617 crate::config::tests::TestConfig,
1618 crate::display_servers::MockDisplayServer<MockHandle>,
1619 > = Manager::new_test(vec![
1620 "A".to_string(),
1621 "B".to_string(),
1622 "C".to_string(),
1623 "D".to_string(),
1624 ]);
1625 manager.screen_create_handler(Screen::default());
1626
1627 manager.state.focus_tag(&1);
1628
1629 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1630 first_window.tag(&1);
1631 manager.window_created_handler(first_window, -1, -1);
1632
1633 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1634 second_window.tag(&1);
1635 manager.window_created_handler(second_window, -1, -1);
1636
1637 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1638 third_window.tag(&2);
1639 let third_window_handle = third_window.handle;
1640 manager.window_created_handler(third_window, -1, -1);
1641
1642 assert!(manager.command_handler(&Command::FocusPreviousTag {
1643 behavior: FocusDeltaBehavior::IgnoreUsed
1644 }));
1645
1646 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1647
1648 assert!(manager.command_handler(&Command::FocusPreviousTag {
1649 behavior: FocusDeltaBehavior::IgnoreUsed
1650 }));
1651
1652 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1653
1654 assert!(manager.command_handler(&Command::FocusPreviousTag {
1655 behavior: FocusDeltaBehavior::IgnoreUsed
1656 }));
1657
1658 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1659
1660 manager.window_destroyed_handler(&third_window_handle);
1661 assert!(manager.command_handler(&Command::FocusPreviousTag {
1662 behavior: FocusDeltaBehavior::IgnoreUsed
1663 }));
1664 assert!(manager.command_handler(&Command::FocusPreviousTag {
1665 behavior: FocusDeltaBehavior::IgnoreUsed
1666 }));
1667
1668 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1669 }
1670
1671 #[test]
1672
1673 fn goto_next_used_tag_while_in_used_tag() {
1674 let mut manager: Manager<
1675 MockHandle,
1676 crate::config::tests::TestConfig,
1677 crate::display_servers::MockDisplayServer<MockHandle>,
1678 > = Manager::new_test(vec!["Used".to_string(), "Empty".to_string()]);
1679 manager.screen_create_handler(Screen::default());
1680
1681 manager.state.focus_tag(&1);
1682
1683 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1684 first_window.tag(&1);
1685 manager.window_created_handler(first_window, -1, -1);
1686
1687 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1688 second_window.tag(&2);
1689 manager.window_created_handler(second_window, -1, -1);
1690
1691 assert!(manager.command_handler(&Command::FocusNextTag {
1692 behavior: FocusDeltaBehavior::IgnoreEmpty
1693 }));
1694
1695 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1696 }
1697
1698 #[test]
1699 fn goto_next_used_tag_while_in_empty_tag() {
1700 let mut manager: Manager<
1701 MockHandle,
1702 crate::config::tests::TestConfig,
1703 crate::display_servers::MockDisplayServer<MockHandle>,
1704 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Used".to_string()]);
1705 manager.screen_create_handler(Screen::default());
1706
1707 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1708 first_window.tag(&2);
1709 manager.window_created_handler(first_window, -1, -1);
1710
1711 manager.state.focus_tag(&1);
1712
1713 assert!(manager.command_handler(&Command::FocusNextTag {
1714 behavior: FocusDeltaBehavior::IgnoreEmpty
1715 }));
1716
1717 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1718 }
1719
1720 #[test]
1721 fn goto_next_used_tag_multiple_tags() {
1722 let mut manager: Manager<
1723 MockHandle,
1724 crate::config::tests::TestConfig,
1725 crate::display_servers::MockDisplayServer<MockHandle>,
1726 > = Manager::new_test(vec![
1727 "A".to_string(),
1728 "B".to_string(),
1729 "C".to_string(),
1730 "D".to_string(),
1731 ]);
1732 manager.screen_create_handler(Screen::default());
1733
1734 manager.state.focus_tag(&1);
1735
1736 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1737 first_window.tag(&1);
1738 manager.window_created_handler(first_window, -1, -1);
1739
1740 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1741 second_window.tag(&2);
1742 manager.window_created_handler(second_window, -1, -1);
1743
1744 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1745 third_window.tag(&4);
1746 let third_window_handle = third_window.handle;
1747 manager.window_created_handler(third_window, -1, -1);
1748
1749 assert!(manager.command_handler(&Command::FocusNextTag {
1750 behavior: FocusDeltaBehavior::IgnoreEmpty
1751 }));
1752
1753 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1754
1755 assert!(manager.command_handler(&Command::FocusNextTag {
1756 behavior: FocusDeltaBehavior::IgnoreEmpty
1757 }));
1758
1759 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1760
1761 assert!(manager.command_handler(&Command::FocusNextTag {
1762 behavior: FocusDeltaBehavior::IgnoreEmpty
1763 }));
1764
1765 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1766
1767 manager.window_destroyed_handler(&third_window_handle);
1768
1769 assert!(manager.command_handler(&Command::FocusNextTag {
1770 behavior: FocusDeltaBehavior::IgnoreEmpty
1771 }));
1772 assert!(manager.command_handler(&Command::FocusNextTag {
1773 behavior: FocusDeltaBehavior::IgnoreEmpty
1774 }));
1775
1776 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1777 }
1778
1779 #[test]
1780
1781 fn goto_previous_used_tag_while_in_used_tag() {
1782 let mut manager: Manager<
1783 MockHandle,
1784 crate::config::tests::TestConfig,
1785 crate::display_servers::MockDisplayServer<MockHandle>,
1786 > = Manager::new_test(vec!["A".to_string(), "B".to_string(), "C".to_string()]);
1787 manager.screen_create_handler(Screen::default());
1788
1789 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1790 first_window.tag(&1);
1791 manager.window_created_handler(first_window, -1, -1);
1792
1793 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1794 second_window.tag(&2);
1795 manager.window_created_handler(second_window, -1, -1);
1796
1797 manager.state.focus_tag(&2);
1798
1799 assert!(manager.command_handler(&Command::FocusPreviousTag {
1800 behavior: FocusDeltaBehavior::IgnoreEmpty
1801 }));
1802
1803 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1804 }
1805
1806 #[test]
1807 fn goto_previous_used_tag_while_in_empty_tag() {
1808 let mut manager: Manager<
1809 MockHandle,
1810 crate::config::tests::TestConfig,
1811 crate::display_servers::MockDisplayServer<MockHandle>,
1812 > = Manager::new_test(vec!["Emtpy_One".to_string(), "Used".to_string()]);
1813 manager.screen_create_handler(Screen::default());
1814
1815 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1816 first_window.tag(&2);
1817 manager.window_created_handler(first_window, -1, -1);
1818
1819 manager.state.focus_tag(&1);
1820
1821 assert!(manager.command_handler(&Command::FocusPreviousTag {
1822 behavior: FocusDeltaBehavior::IgnoreEmpty
1823 }));
1824
1825 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1826 }
1827
1828 #[test]
1829 fn goto_previous_used_tag_multiple_tags() {
1830 let mut manager: Manager<
1831 MockHandle,
1832 crate::config::tests::TestConfig,
1833 crate::display_servers::MockDisplayServer<MockHandle>,
1834 > = Manager::new_test(vec![
1835 "A".to_string(),
1836 "B".to_string(),
1837 "C".to_string(),
1838 "D".to_string(),
1839 "E".to_string(),
1840 ]);
1841 manager.screen_create_handler(Screen::default());
1842
1843 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1844 first_window.tag(&1);
1845 manager.window_created_handler(first_window, -1, -1);
1846
1847 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1848 second_window.tag(&2);
1849 manager.window_created_handler(second_window, -1, -1);
1850
1851 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
1852 third_window.tag(&4);
1853 let third_window_handle = third_window.handle;
1854 manager.window_created_handler(third_window, -1, -1);
1855
1856 manager.state.focus_tag(&4);
1857
1858 assert!(manager.command_handler(&Command::FocusPreviousTag {
1859 behavior: FocusDeltaBehavior::IgnoreEmpty
1860 }));
1861
1862 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 2);
1863
1864 assert!(manager.command_handler(&Command::FocusPreviousTag {
1865 behavior: FocusDeltaBehavior::IgnoreEmpty
1866 }));
1867
1868 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1869
1870 assert!(manager.command_handler(&Command::FocusPreviousTag {
1871 behavior: FocusDeltaBehavior::IgnoreEmpty
1872 }));
1873
1874 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 4);
1875
1876 manager.window_destroyed_handler(&third_window_handle);
1877
1878 assert!(manager.command_handler(&Command::FocusPreviousTag {
1879 behavior: FocusDeltaBehavior::IgnoreEmpty
1880 }));
1881 assert!(manager.command_handler(&Command::FocusPreviousTag {
1882 behavior: FocusDeltaBehavior::IgnoreEmpty
1883 }));
1884
1885 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 1);
1886 }
1887
1888 #[test]
1889 fn goto_previous_used_tag_wraparound() {
1890 let mut manager: Manager<
1891 MockHandle,
1892 crate::config::tests::TestConfig,
1893 crate::display_servers::MockDisplayServer<MockHandle>,
1894 > = Manager::new_test(vec!["A".to_string(), "B".to_string(), "C".to_string()]);
1895 manager.screen_create_handler(Screen::default());
1896
1897 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
1898 first_window.tag(&3);
1899 manager.window_created_handler(first_window, -1, -1);
1900
1901 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
1902 second_window.tag(&2);
1903 manager.window_created_handler(second_window, -1, -1);
1904
1905 manager.state.focus_tag(&2);
1906
1907 assert!(manager.command_handler(&Command::FocusPreviousTag {
1908 behavior: FocusDeltaBehavior::IgnoreEmpty
1909 }));
1910
1911 assert_eq!(manager.state.focus_manager.tag(0).unwrap(), 3);
1912 }
1913}