Skip to main content

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_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            // 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                && 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 the focused window is the main or the top of the deck, we don't do
597                    // anything.
598                    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    // 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                && 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        // Fullscreen(3) > maximized(2) > normal(1) + normal(4)
1079        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        // no screen creation here
1128        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        // no tag creation here but one tag per screen is created
1148        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        // we only have one tag per screen created automatically
1157        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        // test tag history
1226        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}