use crate::hooks;
use crate::layout::{side_stack, Layout, LayoutConf};
use crate::manager::WindowManager;
use std::collections::{HashMap, VecDeque};
use std::ops;
use xcb;
pub type FireAndForget = Box<dyn Fn(&mut WindowManager) -> ()>;
pub type KeyBindings = HashMap<KeyCode, FireAndForget>;
pub type ResizeAction = (WinId, Region);
pub type CodeMap = HashMap<String, u8>;
pub type WinId = u32;
#[derive(Debug, Copy, Clone)]
pub struct Point {
pub x: u32,
pub y: u32,
}
impl Point {
pub fn new(x: u32, y: u32) -> Point {
Point { x, y }
}
}
pub struct Config {
pub workspaces: &'static [&'static str],
pub fonts: &'static [&'static str],
pub floating_classes: &'static [&'static str],
pub layouts: Vec<Layout>,
pub color_scheme: ColorScheme,
pub border_px: u32,
pub gap_px: u32,
pub main_ratio_step: f32,
pub systray_spacing_px: u32,
pub show_systray: bool,
pub show_bar: bool,
pub top_bar: bool,
pub bar_height: u32,
pub respect_resize_hints: bool,
pub new_client_hooks: Vec<hooks::NewClientHook>,
pub layout_hooks: Vec<hooks::LayoutHook>,
pub workspace_change_hooks: Vec<hooks::WorkspaceChangeHook>,
pub screen_change_hooks: Vec<hooks::ScreenChangeHook>,
pub focus_hooks: Vec<hooks::FocusHook>,
}
impl Config {
pub fn default() -> Config {
Config {
workspaces: &["1", "2", "3", "4", "5", "6", "7", "8", "9"],
floating_classes: &["dmenu", "dunst"],
fonts: &["mono"],
layouts: vec![
Layout::new("[side]", LayoutConf::default(), side_stack, 1, 0.6),
Layout::floating("[----]"),
],
color_scheme: ColorScheme {
bg: 0x282828,
fg_1: 0x3c3836,
fg_2: 0xa89984,
fg_3: 0xf2e5bc,
highlight: 0xcc241d,
urgent: 0x458588,
},
border_px: 2,
gap_px: 5,
main_ratio_step: 0.05,
systray_spacing_px: 2,
show_systray: true,
show_bar: true,
top_bar: true,
bar_height: 18,
respect_resize_hints: true,
new_client_hooks: vec![],
layout_hooks: vec![],
workspace_change_hooks: vec![],
screen_change_hooks: vec![],
focus_hooks: vec![],
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Direction {
Forward,
Backward,
}
impl Direction {
pub fn reverse(&self) -> Direction {
match self {
Direction::Forward => Direction::Backward,
Direction::Backward => Direction::Forward,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum Change {
More,
Less,
}
#[derive(Debug)]
pub enum Border {
Urgent,
Focused,
Unfocused,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Region {
x: u32,
y: u32,
w: u32,
h: u32,
}
impl Region {
pub fn new(x: u32, y: u32, w: u32, h: u32) -> Region {
Region { x, y, w, h }
}
pub fn width(&self) -> u32 {
self.w
}
pub fn height(&self) -> u32 {
self.h
}
pub fn values(&self) -> (u32, u32, u32, u32) {
(self.x, self.y, self.w, self.h)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ColorScheme {
pub bg: u32,
pub fg_1: u32,
pub fg_2: u32,
pub fg_3: u32,
pub highlight: u32,
pub urgent: u32,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub struct KeyCode {
pub mask: u16,
pub code: u8,
}
impl KeyCode {
pub fn from_key_press(k: &xcb::KeyPressEvent) -> KeyCode {
KeyCode {
mask: k.state(),
code: k.detail(),
}
}
}
#[derive(Debug)]
pub(crate) struct Ring<T> {
elements: VecDeque<T>,
focused: usize,
}
impl<T> Ring<T> {
pub fn new(elements: Vec<T>) -> Ring<T> {
Ring {
elements: elements.into(),
focused: 0,
}
}
pub fn would_wrap(&self, dir: Direction) -> bool {
let wrap_back = self.focused == 0 && dir == Direction::Backward;
let wrap_forward = self.focused == self.elements.len() - 1 && dir == Direction::Forward;
wrap_back || wrap_forward
}
pub fn focused_index(&mut self) -> usize {
self.focused
}
pub fn focused(&self) -> Option<&T> {
self.elements.get(self.focused)
}
pub fn focused_mut(&mut self) -> Option<&mut T> {
self.elements.get_mut(self.focused)
}
pub fn rotate(&mut self, direction: Direction) {
if self.elements.is_empty() {
return;
}
match direction {
Direction::Forward => self.elements.rotate_right(1),
Direction::Backward => self.elements.rotate_left(1),
}
}
fn next_index(&self, direction: Direction) -> usize {
let max = self.elements.len() - 1;
match direction {
Direction::Forward => {
if self.focused == max {
0
} else {
self.focused + 1
}
}
Direction::Backward => {
if self.focused == 0 {
max
} else {
self.focused - 1
}
}
}
}
pub fn focus_nth(&mut self, n: usize) -> Option<&T> {
self.focused = n;
self.focused()
}
pub fn cycle_focus(&mut self, direction: Direction) -> Option<&T> {
self.focused = self.next_index(direction);
self.focused()
}
pub fn drag_focused(&mut self, direction: Direction) -> Option<&T> {
match (self.focused, self.next_index(direction), direction) {
(0, _, Direction::Backward) => self.rotate(direction),
(_, 0, Direction::Forward) => self.rotate(direction),
(focused, other, _) => self.elements.swap(focused, other),
}
self.cycle_focus(direction)
}
pub fn focus_by(&mut self, cond: impl Fn(&T) -> bool) -> Option<&T> {
if let Some((i, _)) = self.elements.iter().enumerate().find(|(_, e)| cond(*e)) {
self.focused = i;
Some(&self.elements[self.focused])
} else {
None
}
}
pub fn len(&self) -> usize {
self.elements.len()
}
pub fn insert(&mut self, index: usize, element: T) {
self.elements.insert(index, element);
}
pub fn remove_by(&mut self, cond: impl Fn(&T) -> bool) -> Option<T> {
if let Some((i, _)) = self.elements.iter().enumerate().find(|(_, e)| cond(*e)) {
if self.focused > 0 && self.focused == self.elements.len() - 1 {
self.focused -= 1;
}
self.elements.remove(i)
} else {
None
}
}
pub fn remove_focused(&mut self) -> Option<T> {
if self.elements.len() == 0 {
return None;
}
let c = self.elements.remove(self.focused);
if self.focused > 0 && self.focused >= self.elements.len() - 1 {
self.focused -= 1;
}
return c;
}
pub fn iter(&self) -> std::collections::vec_deque::Iter<T> {
self.elements.iter()
}
}
impl<T: Clone> Ring<T> {
#[allow(dead_code)]
pub fn as_vec(&self) -> Vec<T> {
self.iter().cloned().collect()
}
}
impl<T> ops::Index<usize> for Ring<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.elements[index]
}
}
impl<T> ops::IndexMut<usize> for Ring<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.elements[index]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rotate_holds_focus_but_permutes_order() {
let mut r = Ring::new(vec![1, 2, 3]);
r.rotate(Direction::Forward);
assert_eq!(r.as_vec(), vec![3, 1, 2]);
assert_eq!(r.focused(), Some(&3));
r.rotate(Direction::Backward);
assert_eq!(r.as_vec(), vec![1, 2, 3]);
assert_eq!(r.focused(), Some(&1));
}
#[test]
fn dragging_an_element_forward() {
let mut r = Ring::new(vec![1, 2, 3, 4]);
assert_eq!(r.focused(), Some(&1));
assert_eq!(r.drag_focused(Direction::Forward), Some(&1));
assert_eq!(r.elements, vec![2, 1, 3, 4]);
assert_eq!(r.drag_focused(Direction::Forward), Some(&1));
assert_eq!(r.elements, vec![2, 3, 1, 4]);
assert_eq!(r.drag_focused(Direction::Forward), Some(&1));
assert_eq!(r.elements, vec![2, 3, 4, 1]);
assert_eq!(r.drag_focused(Direction::Forward), Some(&1));
assert_eq!(r.elements, vec![1, 2, 3, 4]);
assert_eq!(r.focused(), Some(&1));
}
#[test]
fn dragging_an_element_backward() {
let mut r = Ring::new(vec![1, 2, 3, 4]);
assert_eq!(r.focused(), Some(&1));
assert_eq!(r.drag_focused(Direction::Backward), Some(&1));
assert_eq!(r.elements, vec![2, 3, 4, 1]);
assert_eq!(r.drag_focused(Direction::Backward), Some(&1));
assert_eq!(r.elements, vec![2, 3, 1, 4]);
assert_eq!(r.drag_focused(Direction::Backward), Some(&1));
assert_eq!(r.elements, vec![2, 1, 3, 4]);
assert_eq!(r.drag_focused(Direction::Backward), Some(&1));
assert_eq!(r.elements, vec![1, 2, 3, 4]);
assert_eq!(r.focused(), Some(&1));
}
#[test]
fn remove_focused() {
let mut r = Ring::new(vec![1, 2, 3]);
r.focused = 2;
assert_eq!(r.focused(), Some(&3));
assert_eq!(r.remove_focused(), Some(3));
assert_eq!(r.focused(), Some(&2));
assert_eq!(r.remove_focused(), Some(2));
assert_eq!(r.focused(), Some(&1));
assert_eq!(r.remove_focused(), Some(1));
assert_eq!(r.focused(), None);
assert_eq!(r.remove_focused(), None);
}
#[test]
fn remove_by() {
let mut r = Ring::new(vec![1, 2, 3, 4, 5, 6]);
r.focused = 3;
assert_eq!(r.focused(), Some(&4));
assert_eq!(r.remove_by(|e| e % 2 == 0), Some(2));
assert_eq!(r.focused(), Some(&5));
}
#[test]
fn focus_by() {
let mut r = Ring::new(vec![1, 2, 3, 4, 5, 6]);
assert_eq!(r.focus_by(|e| e % 2 == 0), Some(&2));
assert_eq!(r.focus_by(|e| e % 7 == 0), None);
}
#[test]
fn cycle_focus() {
let mut r = Ring::new(vec![1, 2, 3]);
assert_eq!(r.cycle_focus(Direction::Forward), Some(&2));
assert_eq!(r.as_vec(), vec![1, 2, 3]);
assert_eq!(r.cycle_focus(Direction::Backward), Some(&1));
assert_eq!(r.as_vec(), vec![1, 2, 3]);
}
}