diff --git a/src/clients.rs b/src/clients.rs index a01f22e..e806c08 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroI32; use std::{ops::Rem, usize}; use indexmap::IndexMap; @@ -20,6 +19,7 @@ mod client { pub(crate) size: Point, pub(crate) position: Point, pub(crate) transient_for: Option, + pub(crate) fullscreen: bool, } impl Default for Client { @@ -29,6 +29,7 @@ mod client { size: (100, 100).into(), position: (0, 0).into(), transient_for: None, + fullscreen: false, } } } @@ -44,7 +45,7 @@ mod client { window, size, position, - transient_for: None, + ..Self::default() } } @@ -68,6 +69,27 @@ mod client { } } + /// toggles the clients fullscreen flag. + /// returns `true` if the client is now fullscreen. + pub fn toggle_fullscreen(&mut self) -> bool { + self.fullscreen = !self.fullscreen; + + self.is_fullscreen() + } + + pub fn set_fullscreen(&mut self, fullscreen: bool) -> bool { + if self.fullscreen == fullscreen { + false + } else { + self.fullscreen = fullscreen; + true + } + } + + pub fn is_fullscreen(&self) -> bool { + self.fullscreen + } + pub fn is_transient(&self) -> bool { self.transient_for.is_some() } @@ -408,6 +430,43 @@ impl ClientState { self.arrange_virtual_screen(); } + pub fn set_fullscreen(&mut self, key: &K, fullscreen: bool) -> bool + where + K: ClientKey, + { + self.get(key) + .into_option() + .map(|client| client.is_fullscreen() != fullscreen) + .unwrap_or(false) + .then(|| self.toggle_fullscreen(key)) + .unwrap_or(false) + } + + pub fn toggle_fullscreen(&mut self, key: &K) -> bool + where + K: ClientKey, + { + if self.inner_toggle_fullscreen(key) { + self.arrange_virtual_screen(); + true + } else { + false + } + } + + fn inner_toggle_fullscreen(&mut self, key: &K) -> bool + where + K: ClientKey, + { + self.get_mut(key) + .into_option() + .map(|client| { + client.toggle_fullscreen(); + true + }) + .unwrap_or(false) + } + /** Sets a tiled client to floating and returns true, does nothing for a floating client and returns false. If this function returns `true` you have to call `arrange_clients` after. @@ -635,49 +694,87 @@ impl ClientState { let vs = self.virtual_screens.get_mut_current(); // if aux is empty -> width : width / 2 - let (master_width, aux_width) = { - let effective_width = width - gap * 2; + let vs_width = width - gap * 2; - let master_size = if vs.aux.is_empty() { + let master_position = Point::new(0, 0); + let master_window_size = { + let factor = if vs.aux.is_empty() { 1.0 } else { self.master_size / 2.0 }; - let master_width = (effective_width as f32 * master_size) as i32; - let aux_width = effective_width - master_width; + let width = (vs_width as f32 * factor) as i32; - (master_width, aux_width) + // make sure we dont devide by 0 + // height is max height / number of clients in the stack + let height = match vs.master.len() as i32 { + 0 => 1, + n => (height - gap * 2) / n, + }; + + Size::new(width, height) }; - // make sure we dont devide by 0 - // height is max height / number of clients in the stack - let master_height = (height - gap * 2) - / match NonZeroI32::new(vs.master.len() as i32) { - Some(i) => i.get(), - None => 1, + let aux_position = Point::new(master_window_size.width, 0); + let aux_window_size = { + let width = vs_width - master_window_size.width; + + // make sure we dont devide by 0 + // height is max height / number of clients in the stack + let height = match vs.aux.len() as i32 { + 0 => 1, + n => (height - gap * 2) / n, }; - // height is max height / number of clients in the stack - let aux_height = (height - gap * 2) - / match NonZeroI32::new(vs.aux.len() as i32) { - Some(i) => i.get(), - None => 1, - }; + Size::new(width, height) + }; + + fn calculate_window_dimensions( + screen_size: Size, + stack_size: Size, + stack_position: Point, + fullscreen: bool, + nth: i32, + gap: i32, + border: i32, + ) -> (Size, Point) { + if fullscreen { + let size = Size::new( + screen_size.width - border * 2, + screen_size.height - border * 2, + ); + let pos = Point::new(0, 0); + (size, pos) + } else { + let size = Size::new( + stack_size.width - gap * 2 - border * 2, + stack_size.height - gap * 2 - border * 2, + ); + let pos = Point::new( + stack_position.x + gap * 2, + stack_position.y + stack_size.height * nth + gap * 2, + ); + (size, pos) + } + } // Master for (i, key) in vs.master.iter().enumerate() { - let size = ( - master_width - gap * 2 - self.border_size * 2, - master_height - gap * 2 - self.border_size * 2, - ); - - let position = (gap * 2, master_height * i as i32 + gap * 2); - if let Some(client) = self.clients.get_mut(key) { + let (size, position) = calculate_window_dimensions( + self.screen_size.into(), + master_window_size, + master_position, + client.is_fullscreen(), + i as i32, + gap, + self.border_size, + ); + *client = Client { size: size.into(), - position: position.into(), + position, ..*client }; } @@ -685,18 +782,20 @@ impl ClientState { // Aux for (i, key) in vs.aux.iter().enumerate() { - let size = ( - aux_width - gap * 2 - self.border_size * 2, - aux_height - gap * 2 - self.border_size * 2, - ); - - let position = - (master_width + gap * 2, aux_height * i as i32 + gap * 2); - if let Some(client) = self.clients.get_mut(key) { + let (size, position) = calculate_window_dimensions( + self.screen_size.into(), + aux_window_size, + aux_position, + client.is_fullscreen(), + i as i32, + gap, + self.border_size, + ); + *client = Client { size: size.into(), - position: position.into(), + position, ..*client }; } diff --git a/src/state.rs b/src/state.rs index 4e1bdd0..993d0fb 100644 --- a/src/state.rs +++ b/src/state.rs @@ -8,9 +8,9 @@ use crate::{ backends::{ keycodes::{MouseButton, VirtualKeyCode}, window_event::{ - ButtonEvent, ConfigureEvent, FullscreenEvent, KeyBind, KeyEvent, - KeyState, MapEvent, ModifierKey, ModifierState, MotionEvent, - MouseBind, Point, WindowEvent, + ButtonEvent, ConfigureEvent, FullscreenEvent, FullscreenState, + KeyBind, KeyEvent, KeyState, MapEvent, ModifierKey, ModifierState, + MotionEvent, MouseBind, Point, WindowEvent, }, xlib::XLib, WindowServerBackend, @@ -469,6 +469,20 @@ where state, }) => { info!("FullscreenEvent for window {}: {:?}", window, state); + + if match state { + FullscreenState::On => { + self.clients.set_fullscreen(&window, true) + } + FullscreenState::Off => { + self.clients.set_fullscreen(&window, false) + } + FullscreenState::Toggle => { + self.clients.toggle_fullscreen(&window) + } + } { + self.arrange_clients(); + } } // i dont think i actually have to handle destroy notify events. @@ -658,6 +672,12 @@ where self.clients .iter_transient() .for_each(|(_, c)| self.backend.raise_window(c.window)); + + //raise fullscreen windows + self.clients + .iter_current_screen() + .filter(|(_, c)| c.is_fullscreen()) + .for_each(|(_, c)| self.backend.raise_window(c.window)); } fn arrange_clients(&mut self) {