#![allow(clippy::wildcard_imports)]
mod scratchpad_handler;
pub use scratchpad_handler::{Direction, ReleaseScratchPadOption};
use super::*;
use crate::display_action::DisplayAction;
use crate::display_servers::DisplayServer;
use crate::layouts::Layout;
use crate::models::{LayoutMode, TagId, WindowState};
use crate::state::State;
use crate::utils::helpers;
use crate::utils::helpers::relative_find;
use crate::{config::Config, models::FocusBehaviour};
impl<C: Config, SERVER: DisplayServer> Manager<C, SERVER> {
pub fn command_handler(&mut self, command: &Command) -> bool {
process_internal(self, command).unwrap_or(false)
}
}
macro_rules! move_focus_common_vars {
($func:ident ($state:expr $(, $arg:expr )* $(,)? )) => {{
let handle = $state.focus_manager.window(&$state.windows)?.handle;
let tag_id = $state.focus_manager.tag(0)?;
let tag = $state.tags.get(tag_id)?;
let layout = Some(tag.layout);
let for_active_workspace =
|x: &Window| -> bool { x.tag == Some(tag_id) && x.is_managed() };
let to_reorder = helpers::vec_extract(&mut $state.windows, for_active_workspace);
$func($state, handle, layout, to_reorder, $($arg),*)
}};
}
fn process_internal<C: Config, SERVER: DisplayServer>(
manager: &mut Manager<C, SERVER>,
command: &Command,
) -> Option<bool> {
let state = &mut manager.state;
match command {
Command::ToggleScratchPad(name) => scratchpad_handler::toggle_scratchpad(manager, name),
Command::AttachScratchPad { window, scratchpad } => {
scratchpad_handler::attach_scratchpad(*window, scratchpad, manager)
}
Command::ReleaseScratchPad { window, tag } => {
scratchpad_handler::release_scratchpad(window.clone(), *tag, manager)
}
Command::NextScratchPadWindow { scratchpad } => {
scratchpad_handler::cycle_scratchpad_window(manager, scratchpad, Direction::Forward)
}
Command::PrevScratchPadWindow { scratchpad } => {
scratchpad_handler::cycle_scratchpad_window(manager, scratchpad, Direction::Backward)
}
Command::ToggleFullScreen => toggle_state(state, WindowState::Fullscreen),
Command::ToggleSticky => toggle_state(state, WindowState::Sticky),
Command::SendWindowToTag { window, tag } => move_to_tag(*window, *tag, manager),
Command::MoveWindowToNextTag { follow } => move_to_tag_relative(manager, *follow, 1),
Command::MoveWindowToPreviousTag { follow } => move_to_tag_relative(manager, *follow, -1),
Command::MoveWindowToLastWorkspace => move_to_last_workspace(state),
Command::MoveWindowToNextWorkspace => move_window_to_workspace_change(manager, 1),
Command::MoveWindowToPreviousWorkspace => move_window_to_workspace_change(manager, -1),
Command::MoveWindowUp => move_focus_common_vars!(move_window_change(state, -1)),
Command::MoveWindowDown => move_focus_common_vars!(move_window_change(state, 1)),
Command::MoveWindowTop { swap } => move_focus_common_vars!(move_window_top(state, *swap)),
Command::GoToTag { tag, swap } => goto_tag(state, *tag, *swap),
Command::ReturnToLastTag => return_to_last_tag(state),
Command::CloseWindow => close_window(state),
Command::SwapScreens => swap_tags(state),
Command::NextLayout => next_layout(state),
Command::PreviousLayout => previous_layout(state),
Command::SetLayout(layout) => set_layout(*layout, state),
Command::FloatingToTile => floating_to_tile(state),
Command::TileToFloating => tile_to_floating(state),
Command::ToggleFloating => toggle_floating(state),
Command::FocusNextTag => focus_tag_change(state, 1),
Command::FocusPreviousTag => focus_tag_change(state, -1),
Command::FocusWindow(param) => focus_window(state, param),
Command::FocusWindowUp => move_focus_common_vars!(focus_window_change(state, -1)),
Command::FocusWindowDown => move_focus_common_vars!(focus_window_change(state, 1)),
Command::FocusWindowTop { swap } => focus_window_top(state, *swap),
Command::FocusWorkspaceNext => focus_workspace_change(state, 1),
Command::FocusWorkspacePrevious => focus_workspace_change(state, -1),
Command::SoftReload => {
if let Some((handle, Some(tag))) = state
.focus_manager
.window(&state.windows)
.map(|w| (w.handle, w.tag))
{
let old_handle = state
.focus_manager
.tags_last_window
.entry(tag)
.or_insert(handle);
*old_handle = handle;
}
manager.config.save_state(&manager.state);
manager.hard_reload();
None
}
Command::HardReload => {
manager.hard_reload();
None
}
Command::RotateTag => rotate_tag(state),
Command::IncreaseMainWidth(delta) => change_main_width(state, *delta, 1),
Command::DecreaseMainWidth(delta) => change_main_width(state, *delta, -1),
Command::SetMarginMultiplier(multiplier) => set_margin_multiplier(state, *multiplier),
Command::SendWorkspaceToTag(ws_index, tag_index) => {
Some(send_workspace_to_tag(state, *ws_index, *tag_index))
}
Command::CloseAllOtherWindows => close_all_other_windows(state),
Command::Other(cmd) => Some(C::command_handler(cmd, manager)),
}
}
fn toggle_state(state: &mut State, window_state: WindowState) -> Option<bool> {
let window = state.focus_manager.window(&state.windows)?;
let handle = window.handle;
let toggle_to = !window.has_state(&window_state);
let act = DisplayAction::SetState(handle, toggle_to, window_state);
state.actions.push_back(act);
state.handle_window_focus(&handle);
match window_state {
WindowState::Fullscreen => Some(true),
_ => Some(false),
}
}
fn move_to_tag<C: Config, SERVER: DisplayServer>(
window: Option<WindowHandle>,
tag_id: TagId,
manager: &mut Manager<C, SERVER>,
) -> Option<bool> {
let tag = manager.state.tags.get(tag_id)?.clone();
let margin_multiplier = match manager.state.windows.iter().find(|w| w.has_tag(&tag.id)) {
Some(w) => w.margin_multiplier(),
None => 1.0,
};
let handle = window.or(*manager.state.focus_manager.window_history.get(0)?)?;
let handle_focus = window.is_none();
let new_handle = if handle_focus {
manager.get_next_or_previous_handle(&handle)
} else {
None
};
let window = manager
.state
.windows
.iter_mut()
.find(|w| w.handle == handle)?;
window.untag();
window.set_floating(false);
window.tag(&tag.id);
window.apply_margin_multiplier(margin_multiplier);
let act = DisplayAction::SetWindowTag(window.handle, Some(tag.id));
manager.state.actions.push_back(act);
manager.state.sort_windows();
manager
.state
.handle_single_border(manager.config.border_width());
if handle_focus {
if let Some(new_handle) = new_handle {
manager.state.focus_window(&new_handle);
} else {
let act = DisplayAction::Unfocus(Some(handle), false);
manager.state.actions.push_back(act);
manager.state.focus_manager.window_history.push_front(None);
}
}
Some(true)
}
fn move_to_tag_relative<C: Config, SERVER: DisplayServer>(
manager: &mut Manager<C, SERVER>,
follow: bool,
delta: i32,
) -> Option<bool> {
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default() - 1;
let tags_len = manager.state.tags.normal().len() as isize;
let desired_tag = (current_tag as isize + delta as isize).rem_euclid(tags_len) + 1;
let desired_tag = desired_tag as usize;
move_to_tag(None, desired_tag, manager);
if follow {
let moved_window = *manager.state.focus_manager.window_history.get(1)?;
manager.state.goto_tag_handler(desired_tag);
manager.state.handle_window_focus(&moved_window?);
}
Some(true)
}
fn move_window_to_workspace_change<C: Config, SERVER: DisplayServer>(
manager: &mut Manager<C, SERVER>,
delta: i32,
) -> Option<bool> {
let current = manager
.state
.focus_manager
.workspace(&manager.state.workspaces)?;
let workspace =
helpers::relative_find(&manager.state.workspaces, |w| w == current, delta, true)?.clone();
let tag_id = workspace.tag?;
move_to_tag(None, tag_id, manager)
}
fn goto_tag(state: &mut State, input_tag: TagId, current_tag_swap: bool) -> Option<bool> {
let current_tag = state.focus_manager.tag(0).unwrap_or_default();
let previous_tag = state.focus_manager.tag(1).unwrap_or_default();
let destination_tag = if current_tag_swap && current_tag == input_tag {
previous_tag
} else {
input_tag
};
state.goto_tag_handler(destination_tag)
}
fn return_to_last_tag(state: &mut State) -> Option<bool> {
let previous_tag = state.focus_manager.tag(1).unwrap_or_default();
state.goto_tag_handler(previous_tag)
}
fn focus_window(state: &mut State, param: &str) -> Option<bool> {
match param.parse::<usize>() {
Ok(index) if index > 0 => {
let handle = state
.windows
.iter()
.filter(|w| w.visible())
.nth(index - 1)?
.handle;
state.handle_window_focus(&handle);
None
}
Err(_) => focus_window_by_class(state, param),
Ok(_) => None,
}
}
fn focus_window_by_class(state: &mut State, window_class: &str) -> Option<bool> {
let is_target = |w: &Window| -> bool {
w.res_name
.as_ref()
.zip(w.res_class.as_ref())
.map_or(false, |(res_name, res_class)| {
window_class == res_name || window_class == res_class
})
};
let current_window = state.focus_manager.window(&state.windows)?;
let target_window = if is_target(current_window) {
let previous_window_handle = state.focus_manager.window_history.get(1);
state
.windows
.iter()
.find(|w| Some(&Some(w.handle)) == previous_window_handle)
.cloned()
} else {
state.windows.iter().find(|w| is_target(w)).cloned()
}?;
let handle = target_window.handle;
if target_window.visible() {
state.handle_window_focus(&handle);
return None;
}
let tag_id = target_window.tag?;
state.goto_tag_handler(tag_id)?;
match state
.focus_manager
.workspace(&state.workspaces)
.map(|ws| ws.layout)
{
Some(layout) if layout == Layout::Monocle || layout == Layout::MainAndDeck => {
let mut windows = helpers::vec_extract(&mut state.windows, |w| {
w.has_tag(&tag_id) && w.is_managed() && !w.floating()
});
let cycle = |wins: &mut Vec<Window>, s: &mut State| {
let window_index = wins.iter().position(|w| w.handle == handle).unwrap_or(0);
let _ = helpers::cycle_vec(wins, -(window_index as i32));
s.windows.append(wins);
};
if layout == Layout::Monocle && windows.len() > 1 {
cycle(&mut windows, state);
} else if layout == Layout::MainAndDeck && windows.len() > 2 {
let main_window = windows.remove(0);
state.windows.push(main_window);
cycle(&mut windows, state);
} else {
state.windows.append(&mut windows);
}
state.handle_window_focus(&handle);
Some(true)
}
Some(_) => {
state.handle_window_focus(&handle);
Some(true)
}
None => None,
}
}
fn focus_tag_change(state: &mut State, delta: i8) -> Option<bool> {
let current_tag = state.focus_manager.tag(0)?;
let tags = state.tags.normal();
let relative_tag_id = relative_find(tags, |tag| tag.id == current_tag, i32::from(delta), true)
.map(|tag| tag.id)?;
state.goto_tag_handler(relative_tag_id)
}
fn swap_tags(state: &mut State) -> Option<bool> {
if state.workspaces.len() >= 2 && state.focus_manager.workspace_history.len() >= 2 {
let hist_a = *state.focus_manager.workspace_history.get(0)?;
let hist_b = *state.focus_manager.workspace_history.get(1)?;
let mut temp = None;
std::mem::swap(&mut state.workspaces.get_mut(hist_a)?.tag, &mut temp);
std::mem::swap(&mut state.workspaces.get_mut(hist_b)?.tag, &mut temp);
std::mem::swap(&mut state.workspaces.get_mut(hist_a)?.tag, &mut temp);
state.update_static();
state
.layout_manager
.update_layouts(&mut state.workspaces, state.tags.all_mut());
return Some(true);
}
if state.workspaces.len() == 1 {
let last = *state.focus_manager.tag_history.get(1)?;
return state.goto_tag_handler(last);
}
None
}
fn close_window(state: &mut State) -> Option<bool> {
let window = state.focus_manager.window(&state.windows)?;
if window.is_managed() {
let act = DisplayAction::KillWindow(window.handle);
state.actions.push_back(act);
}
None
}
fn move_to_last_workspace(state: &mut State) -> Option<bool> {
if state.workspaces.len() >= 2 && state.focus_manager.workspace_history.len() >= 2 {
let index = *state.focus_manager.workspace_history.get(1)?;
let wp_tags = state.workspaces.get(index)?.tag;
let window = state.focus_manager.window_mut(&mut state.windows)?;
window.tag = wp_tags;
return Some(true);
}
None
}
fn next_layout(state: &mut State) -> Option<bool> {
let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
let layout = state.layout_manager.next_layout(workspace);
set_layout(layout, state)
}
fn previous_layout(state: &mut State) -> Option<bool> {
let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
let layout = state.layout_manager.previous_layout(workspace);
set_layout(layout, state)
}
fn set_layout(layout: Layout, state: &mut State) -> Option<bool> {
let tag_id = state.focus_manager.tag(0)?;
if state.focus_manager.behaviour != FocusBehaviour::Sloppy {
let focused_window = state.focus_manager.window_history.get(0);
let is_focused_floating = match state
.windows
.iter()
.find(|w| Some(&Some(w.handle)) == focused_window)
{
Some(w) => w.floating(),
None => false,
};
if !is_focused_floating {
let mut to_focus: Option<Window> = None;
if layout == Layout::Monocle {
to_focus = state
.windows
.iter()
.find(|w| w.has_tag(&tag_id) && w.is_managed() && !w.floating())
.cloned();
} else if layout == Layout::MainAndDeck {
let tags_windows = state
.windows
.iter()
.filter(|w| w.has_tag(&tag_id) && w.is_managed() && !w.floating())
.collect::<Vec<&Window>>();
if let (Some(mw), Some(tdw)) = (tags_windows.get(0), tags_windows.get(1)) {
if let Some(&Some(h)) = focused_window {
if mw.handle != h && tdw.handle != h {
if let Some(w) = tags_windows.get(1).copied() {
to_focus = Some(w.clone());
}
}
}
}
}
if let Some(w) = to_focus {
state.focus_window(&w.handle);
}
}
}
let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
workspace.layout = layout;
if state.layout_manager.mode == LayoutMode::Workspace {
workspace.main_width_percentage = layout.main_width();
}
let tag = state.tags.get_mut(tag_id)?;
tag.set_layout(layout, layout.main_width());
Some(true)
}
fn floating_to_tile(state: &mut State) -> Option<bool> {
let workspace = state.focus_manager.workspace(&state.workspaces)?;
let window = state.focus_manager.window_mut(&mut state.windows)?;
if window.must_float() {
return None;
}
if !window.floating() {
return None;
}
let handle = window.handle;
if window.snap_to_workspace(workspace) {
state.sort_windows();
}
state.handle_window_focus(&handle);
Some(true)
}
fn tile_to_floating(state: &mut State) -> Option<bool> {
let width = state.default_width;
let height = state.default_height;
let window = state.focus_manager.window_mut(&mut state.windows)?;
if window.must_float() {
return None;
}
if window.floating() {
return None;
}
let mut normal = window.normal;
let offset = window.container_size.unwrap_or_default();
normal.set_x(normal.x() + window.margin.left as i32);
normal.set_y(normal.y() + window.margin.top as i32);
normal.set_w(width);
normal.set_h(height);
let floating = normal - offset;
window.set_floating_offsets(Some(floating));
window.start_loc = Some(floating);
window.set_floating(true);
state.sort_windows();
Some(true)
}
fn toggle_floating(state: &mut State) -> Option<bool> {
let window = state.focus_manager.window(&state.windows)?;
if window.floating() {
floating_to_tile(state)
} else {
tile_to_floating(state)
}
}
fn move_window_change(
state: &mut State,
mut handle: WindowHandle,
layout: Option<Layout>,
mut to_reorder: Vec<Window>,
val: i32,
) -> Option<bool> {
let is_handle = |x: &Window| -> bool { x.handle == handle };
if layout == Some(Layout::Monocle) {
handle = helpers::relative_find(&to_reorder, is_handle, -val, true)?.handle;
let _ = helpers::cycle_vec(&mut to_reorder, val);
} else if layout == Some(Layout::MainAndDeck) {
if let Some(index) = to_reorder.iter().position(|x: &Window| !x.floating()) {
let mut window_group = to_reorder.split_off(index + 1);
if !to_reorder.iter().any(|w| w.handle == handle) {
handle = helpers::relative_find(&window_group, is_handle, -val, true)?.handle;
}
let _ = helpers::cycle_vec(&mut window_group, val);
to_reorder.append(&mut window_group);
}
} else {
let _ = helpers::reorder_vec(&mut to_reorder, is_handle, val);
}
state.windows.append(&mut to_reorder);
state.handle_window_focus(&handle);
Some(true)
}
fn move_window_top(
state: &mut State,
handle: WindowHandle,
_layout: Option<Layout>,
mut to_reorder: Vec<Window>,
swap: bool,
) -> Option<bool> {
let is_handle = |x: &Window| -> bool { x.handle == handle };
let list = &mut to_reorder;
let len = list.len();
let index = list.iter().position(|x| is_handle(x))?;
let item = list.get(index)?.clone();
list.remove(index);
let mut new_index: usize = match index {
0 if swap => 1,
_ => 0,
};
if new_index >= len {
new_index -= len;
}
list.insert(new_index, item);
state.windows.append(&mut to_reorder);
if index > 0 {
state.handle_window_focus(&handle);
}
Some(true)
}
fn focus_window_change(
state: &mut State,
mut handle: WindowHandle,
layout: Option<Layout>,
mut to_reorder: Vec<Window>,
val: i32,
) -> Option<bool> {
let is_handle = |x: &Window| -> bool { x.handle == handle };
if layout == Some(Layout::Monocle) {
handle = helpers::relative_find(&to_reorder, is_handle, -val, true)?.handle;
let _ = helpers::cycle_vec(&mut to_reorder, val);
} else if layout == Some(Layout::MainAndDeck) {
let len = to_reorder.len() as i32;
if len > 0 {
let index = match to_reorder.iter().position(|x: &Window| !x.floating()) {
Some(i) => {
if i as i32 == len - 1 {
i
} else {
i + 1
}
}
None => len.saturating_sub(1) as usize,
};
let window_group = &to_reorder[..=index];
handle = helpers::relative_find(window_group, is_handle, -val, true)?.handle;
}
} else if let Some(new_focused) = helpers::relative_find(&to_reorder, is_handle, val, true) {
handle = new_focused.handle;
}
state.windows.append(&mut to_reorder);
state.handle_window_focus(&handle);
Some(layout == Some(Layout::Monocle))
}
fn focus_window_top(state: &mut State, swap: bool) -> Option<bool> {
let tag = state.focus_manager.tag(0)?;
let cur = state.focus_manager.window(&state.windows).map(|w| w.handle);
let prev = state.focus_manager.tags_last_window.get(&tag).copied();
let next = state
.windows
.iter()
.find(|x| x.tag == Some(tag) && !x.floating() && x.is_managed())
.map(|w| w.handle);
match (next, cur, prev) {
(Some(next), Some(cur), Some(prev)) if next == cur && swap => {
state.handle_window_focus(&prev);
}
(Some(next), Some(cur), _) if next != cur => state.handle_window_focus(&next),
_ => {}
}
None
}
fn close_all_other_windows(state: &mut State) -> Option<bool> {
let current_window: Option<WindowHandle> =
state.focus_manager.window(&state.windows).map(|w| w.handle);
let current_workspace = state.focus_manager.workspace(&state.workspaces);
for window in &state.windows {
if window.handle.ne(¤t_window?)
&& current_workspace?.is_displaying(window)
&& window.r#type.ne(&WindowType::Normal)
{
let act = DisplayAction::KillWindow(window.handle);
state.actions.push_back(act);
}
}
Some(true)
}
fn focus_workspace_change(state: &mut State, val: i32) -> Option<bool> {
let current = state.focus_manager.workspace(&state.workspaces)?;
let workspace = helpers::relative_find(&state.workspaces, |w| w == current, val, true)?.clone();
if state.focus_manager.behaviour.is_sloppy() && state.focus_manager.sloppy_mouse_follows_focus {
let action = workspace
.tag
.as_ref()
.and_then(|tag| state.focus_manager.tags_last_window.get(tag))
.map_or_else(
|| DisplayAction::MoveMouseOverPoint(workspace.xyhw.center()),
|h| DisplayAction::MoveMouseOver(*h, true),
);
state.actions.push_back(action);
}
state.focus_workspace(&workspace);
None
}
fn rotate_tag(state: &mut State) -> Option<bool> {
let tag_id = state.focus_manager.tag(0)?;
let tag = state.tags.get_mut(tag_id)?;
tag.rotate_layout()?;
Some(true)
}
fn change_main_width(state: &mut State, delta: i8, factor: i8) -> Option<bool> {
let workspace = state.focus_manager.workspace_mut(&mut state.workspaces)?;
workspace.change_main_width(delta * factor);
let tag_id = state.focus_manager.tag(0)?;
let tag = state.tags.get_mut(tag_id)?;
tag.change_main_width(delta * factor);
Some(true)
}
fn set_margin_multiplier(state: &mut State, margin_multiplier: f32) -> Option<bool> {
let ws = state.focus_manager.workspace_mut(&mut state.workspaces)?;
ws.set_margin_multiplier(margin_multiplier);
let tag = ws.tag;
if state.windows.iter().any(|w| w.r#type == WindowType::Normal) {
let for_active_workspace =
|x: &Window| -> bool { tag == x.tag && x.r#type == WindowType::Normal };
let mut to_apply_margin_multiplier =
helpers::vec_extract(&mut state.windows, for_active_workspace);
for w in &mut to_apply_margin_multiplier {
if let Some(ws) = state.focus_manager.workspace(&state.workspaces) {
w.apply_margin_multiplier(ws.margin_multiplier());
}
}
state.windows.append(&mut to_apply_margin_multiplier);
}
Some(true)
}
fn send_workspace_to_tag(state: &mut State, ws_index: usize, tag_index: usize) -> bool {
if ws_index < state.workspaces.len() && tag_index < state.tags.len_normal() {
let workspace = &state.workspaces[ws_index].clone();
state.focus_workspace(workspace);
state.goto_tag_handler(tag_index + 1);
return true;
}
false
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Tags;
#[test]
fn return_to_last_tag_should_go_back_to_last_tag() {
let mut manager = Manager::new_test(vec![
"A15".to_string(),
"B24".to_string(),
"C".to_string(),
"6D4".to_string(),
"E39".to_string(),
"F67".to_string(),
]);
manager.screen_create_handler(Screen::default());
manager.screen_create_handler(Screen::default());
assert!(manager.command_handler(&Command::GoToTag {
tag: 1,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap();
assert_eq!(current_tag, 1);
assert!(manager.command_handler(&Command::GoToTag {
tag: 2,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
assert_eq!(current_tag, 2);
manager.command_handler(&Command::ReturnToLastTag);
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
assert_eq!(current_tag, 1);
}
#[test]
fn go_to_tag_should_return_false_if_no_screen_is_created() {
let mut manager = Manager::new_test(vec![]);
assert!(!manager.command_handler(&Command::GoToTag {
tag: 6,
swap: false
}));
assert!(!manager.command_handler(&Command::GoToTag {
tag: 2,
swap: false
}));
assert!(!manager.command_handler(&Command::GoToTag {
tag: 15,
swap: false
}));
}
#[test]
fn go_to_tag_should_create_at_least_one_tag_per_screen_no_more() {
let mut manager = Manager::new_test(vec![]);
manager.screen_create_handler(Screen::default());
manager.screen_create_handler(Screen::default());
assert!(manager.command_handler(&Command::GoToTag {
tag: 2,
swap: false
}));
assert!(manager.command_handler(&Command::GoToTag {
tag: 1,
swap: false
}));
assert!(!manager.command_handler(&Command::GoToTag {
tag: 3,
swap: false
}));
}
#[test]
fn go_to_tag_should_return_false_on_invalid_input() {
let mut manager = Manager::new_test(vec![]);
manager.screen_create_handler(Screen::default());
manager.state.tags = Tags::new();
manager.state.tags.add_new("A15", Layout::default());
manager.state.tags.add_new("B24", Layout::default());
manager.state.tags.add_new("C", Layout::default());
manager.state.tags.add_new("6D4", Layout::default());
manager.state.tags.add_new("E39", Layout::default());
manager.state.tags.add_new("F67", Layout::default());
assert!(!manager.command_handler(&Command::GoToTag {
tag: 0,
swap: false
}));
assert!(!manager.command_handler(&Command::GoToTag {
tag: 999,
swap: false
}));
}
#[test]
fn go_to_tag_should_go_to_tag_and_set_history() {
let mut manager = Manager::new_test(vec![
"A15".to_string(),
"B24".to_string(),
"C".to_string(),
"6D4".to_string(),
"E39".to_string(),
"F67".to_string(),
]);
manager.screen_create_handler(Screen::default());
manager.screen_create_handler(Screen::default());
assert!(manager.command_handler(&Command::GoToTag {
tag: 6,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap();
assert_eq!(current_tag, 6);
assert!(manager.command_handler(&Command::GoToTag {
tag: 2,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
assert_eq!(current_tag, 2);
assert!(manager.command_handler(&Command::GoToTag {
tag: 3,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
assert_eq!(current_tag, 3);
assert!(manager.command_handler(&Command::GoToTag {
tag: 4,
swap: false
}));
let current_tag = manager.state.focus_manager.tag(0).unwrap_or_default();
assert_eq!(current_tag, 4);
assert_eq!(manager.state.focus_manager.tag(1).unwrap_or_default(), 3);
assert_eq!(manager.state.focus_manager.tag(2).unwrap_or_default(), 2);
assert_eq!(manager.state.focus_manager.tag(3).unwrap_or_default(), 6);
}
#[test]
fn focus_tag_change_should_go_to_previous_and_next_tag() {
let mut manager = Manager::new_test(vec![
"A15".to_string(),
"B24".to_string(),
"C".to_string(),
"6D4".to_string(),
"E39".to_string(),
"F67".to_string(),
]);
manager.screen_create_handler(Screen::default());
let state = &mut manager.state;
state.focus_tag(&2);
assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
focus_tag_change(state, 1);
assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
focus_tag_change(state, -1);
assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
focus_tag_change(state, 2);
assert_eq!(state.focus_manager.tag(0).unwrap(), 4);
focus_tag_change(state, -5);
assert_eq!(state.focus_manager.tag(0).unwrap(), 5);
focus_tag_change(state, 3);
assert_eq!(state.focus_manager.tag(0).unwrap(), 2);
focus_tag_change(state, 13);
assert_eq!(state.focus_manager.tag(0).unwrap(), 3);
}
#[test]
fn focus_window_top() {
let mut manager = Manager::new_test(vec![]);
manager.screen_create_handler(Screen::default());
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(1), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(2), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(3), None, None),
-1,
-1,
);
let expected = manager.state.windows[0].clone();
let initial = manager.state.windows[1].clone();
manager.state.focus_window(&initial.handle);
manager.command_handler(&Command::FocusWindowTop { swap: false });
let actual = manager
.state
.focus_manager
.window(&manager.state.windows)
.unwrap()
.handle;
assert_eq!(expected.handle, actual);
manager.command_handler(&Command::FocusWindowTop { swap: false });
let actual = manager
.state
.focus_manager
.window(&manager.state.windows)
.unwrap()
.handle;
assert_eq!(expected.handle, actual);
manager.command_handler(&Command::FocusWindowTop { swap: true });
let actual = manager
.state
.focus_manager
.window(&manager.state.windows)
.unwrap()
.handle;
assert_eq!(initial.handle, actual);
}
#[test]
fn move_window_top() {
let mut manager = Manager::new_test(vec![]);
manager.screen_create_handler(Screen::default());
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(1), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(2), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(3), None, None),
-1,
-1,
);
let expected = manager.state.windows[0].clone();
let initial = manager.state.windows[1].clone();
manager.state.focus_window(&initial.handle);
manager.command_handler(&Command::MoveWindowTop { swap: false });
assert_eq!(manager.state.windows[0].handle, initial.handle);
manager.command_handler(&Command::MoveWindowTop { swap: false });
assert_eq!(manager.state.windows[0].handle, initial.handle);
manager.command_handler(&Command::MoveWindowTop { swap: true });
assert_eq!(manager.state.windows[0].handle, expected.handle);
}
#[test]
fn move_window_to_next_or_prev_tag_should_be_able_to_cycle() {
let mut manager = Manager::new_test(vec![
"AO".to_string(),
"EU".to_string(),
"ID".to_string(),
"HT".to_string(),
"NS".to_string(),
]);
manager.screen_create_handler(Screen::default());
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(1), None, None),
-1,
-1,
);
let first_tag = manager.state.tags.get(1).unwrap().id;
let third_tag = manager.state.tags.get(3).unwrap().id;
let last_tag = manager.state.tags.get(5).unwrap().id;
assert!(manager.state.windows[0].has_tag(&first_tag));
manager.command_handler(&Command::MoveWindowToPreviousTag { follow: true });
assert!(manager.state.windows[0].has_tag(&last_tag));
(0..3).for_each(|_| {
manager.command_handler(&Command::MoveWindowToNextTag { follow: false });
manager.command_handler(&Command::FocusNextTag);
});
assert!(manager.state.windows[0].has_tag(&third_tag));
}
#[test]
fn move_window_to_next_or_prev_tag_should_be_able_to_keep_window_focused() {
let mut manager = Manager::new_test(vec!["AO".to_string(), "EU".to_string()]);
manager.screen_create_handler(Screen::default());
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(1), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(2), None, None),
-1,
-1,
);
let expected_tag = manager.state.tags.get(2).unwrap().id;
manager.command_handler(&Command::SendWindowToTag {
window: None,
tag: expected_tag,
});
let initial = manager.state.windows[0].clone();
manager.command_handler(&Command::MoveWindowToNextTag { follow: true });
assert_eq!(
*manager.state.focus_manager.tag_history.get(0).unwrap(),
expected_tag
);
assert_eq!(manager.state.windows[0].handle, initial.handle);
}
#[test]
fn after_moving_second_window_remaining_single_window_has_no_border() {
let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
manager.screen_create_handler(Screen::default());
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(1), None, None),
-1,
-1,
);
manager.window_created_handler(
Window::new(WindowHandle::MockHandle(2), None, None),
-1,
-1,
);
let first_tag = manager.state.tags.get(1).unwrap().id;
assert!(manager.state.windows[0].has_tag(&first_tag));
assert!(manager.state.windows[0].border() > 0);
let second_tag = manager.state.tags.get(2).unwrap().id;
assert!(manager.command_handler(&Command::SendWindowToTag {
window: Some(manager.state.windows[0].handle),
tag: second_tag,
}));
assert_eq!(manager.state.windows[0].border(), 0);
}
#[test]
fn after_moving_single_window_to_another_single_window_both_have_borders() {
let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
manager.screen_create_handler(Screen::default());
let mut first_window = Window::new(WindowHandle::MockHandle(1), None, None);
first_window.tag(&1);
manager.window_created_handler(first_window, -1, -1);
let mut second_window = Window::new(WindowHandle::MockHandle(2), None, None);
second_window.tag(&2);
manager.window_created_handler(second_window, -1, -1);
let second_tag = manager.state.tags.get(2).unwrap().id;
assert!(manager.command_handler(&Command::SendWindowToTag {
window: Some(manager.state.windows[0].handle),
tag: second_tag,
}));
assert_eq!(manager.state.windows[0].border(), 1);
assert_eq!(manager.state.windows[1].border(), 1);
}
}