leftwm_core/handlers/
command_handler.rs

1#![allow(clippy::wildcard_imports)]
2
3mod scratchpad_handler;
4
5use leftwm_layouts::geometry::{Direction as FocusDirection, Rect};
6// Make public to the rest of the crate without exposing other internal
7// details of the scratchpad handling code
8pub 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    /* When adding a command
23     * please update src/utils/command_pipe and leftwm/src/command if:
24     * - a command is introduced or renamed
25     * please also update src/bin/leftwm-check if any of the following apply after your update:
26     * - a command now requires a value
27     * - a command no longer requires a value
28     * - a new command is introduced that requires a value
29     *  */
30    /// Processes a command and invokes the associated function.
31    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            // Make sure the currently focused window is saved for the tag.
125            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    // In order to apply the correct margin multiplier we want to copy this value
263    // from any window already present on the target tag
264    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    // Only handle the focus when moving the focused window.
271    let handle_focus = window.is_none();
272    // Focus the next or previous window on the workspace.
273    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
308/// Move currently focused window to tag relative to current tag
309///
310/// Conditionally allow focus to follow the window to the target tag
311fn 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    // Map indexing from 1..len to 0..(len - 1)
317    let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default() - 1;
318    // apply euclidean division reminder to the result of offseting to wrap around tags vector
319    // and add 1 to remap back to 1..len indexing
320    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            // 1-based index seems more user-friendly to me in this context
372            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
387// TODO: add comment
388fn 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            // update current focussed window
412            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
494/// Focus the adjacent tags, depending on the delta.
495/// A delta of 1 means "next tag", a delta of -1 means "previous tag".
496fn 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        // Update workspace tags
509        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        // Update dock tags and layouts.
514        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
524// TODO: closing windows breaks focus, see gh-1204
525fn 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    // When switching to Monocle or MainAndDeck layout while in Driven
564    // or ClickTo focus mode, we check if the focus is given to a visible window.
565    if state.focus_manager.behaviour != FocusBehaviour::Sloppy {
566        // if the currently focused window is floating, nothing will be done
567        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 the focused window is the main or the top of the deck, we don't do
596                        // anything.
597                        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    // Not ideal as is_floating and must_float are connected so have to check
623    // them separately
624    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
700//val and layout aren't used which is a bit awkward
701fn 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    // Moves the selected window at index 0 of the window list.
709    // If the selected window is already at index 0, it is sent to index 1.
710    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    // focus follows the window if it was not already on top of the stack
728    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    // Swaps the selected window to index 0 of the window list.
776    // If the selected window is already at index 0, it is sent to index 1.
777    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    // focus follows the window if it was not already on top of the stack
794    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        // For Monocle we want to also move windows up/down
810        // Not the best solution but results
811        // in desired behaviour
812        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(&current_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    // todo: address inconsistency of using the index instead of the id here
950    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        // Fullscreen(3) > maximized(2) > normal(1) + normal(4)
1074        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        // no screen creation here
1123        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        // no tag creation here but one tag per screen is created
1143        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        // we only have one tag per screen created automatically
1152        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        // test tag history
1221        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}