diff options
| -rw-r--r-- | src/layout/focus_ring.rs | 108 | ||||
| -rw-r--r-- | src/layout/mod.rs | 2079 | ||||
| -rw-r--r-- | src/layout/monitor.rs | 565 | ||||
| -rw-r--r-- | src/layout/workspace.rs | 1441 |
4 files changed, 2125 insertions, 2068 deletions
diff --git a/src/layout/focus_ring.rs b/src/layout/focus_ring.rs new file mode 100644 index 00000000..9385fc6b --- /dev/null +++ b/src/layout/focus_ring.rs @@ -0,0 +1,108 @@ +use std::iter::zip; + +use arrayvec::ArrayVec; +use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; +use smithay::backend::renderer::element::Kind; +use smithay::utils::{Logical, Point, Scale, Size}; + +use crate::config::{self, Color}; + +#[derive(Debug)] +pub struct FocusRing { + buffers: [SolidColorBuffer; 4], + locations: [Point<i32, Logical>; 4], + is_off: bool, + is_border: bool, + width: i32, + active_color: Color, + inactive_color: Color, +} + +pub type FocusRingRenderElement = SolidColorRenderElement; + +impl FocusRing { + pub fn new(config: config::FocusRing) -> Self { + Self { + buffers: Default::default(), + locations: Default::default(), + is_off: config.off, + is_border: false, + width: config.width.into(), + active_color: config.active_color, + inactive_color: config.inactive_color, + } + } + + pub fn update_config(&mut self, config: config::FocusRing) { + self.is_off = config.off; + self.width = config.width.into(); + self.active_color = config.active_color; + self.inactive_color = config.inactive_color; + } + + pub fn update( + &mut self, + win_pos: Point<i32, Logical>, + win_size: Size<i32, Logical>, + is_border: bool, + ) { + if is_border { + self.buffers[0].resize((win_size.w + self.width * 2, self.width)); + self.buffers[1].resize((win_size.w + self.width * 2, self.width)); + self.buffers[2].resize((self.width, win_size.h)); + self.buffers[3].resize((self.width, win_size.h)); + + self.locations[0] = win_pos + Point::from((-self.width, -self.width)); + self.locations[1] = win_pos + Point::from((-self.width, win_size.h)); + self.locations[2] = win_pos + Point::from((-self.width, 0)); + self.locations[3] = win_pos + Point::from((win_size.w, 0)); + } else { + let size = win_size + Size::from((self.width * 2, self.width * 2)); + self.buffers[0].resize(size); + self.locations[0] = win_pos - Point::from((self.width, self.width)); + } + + self.is_border = is_border; + } + + pub fn set_active(&mut self, is_active: bool) { + let color = if is_active { + self.active_color.into() + } else { + self.inactive_color.into() + }; + + for buf in &mut self.buffers { + buf.set_color(color); + } + } + + pub fn render(&self, scale: Scale<f64>) -> impl Iterator<Item = SolidColorRenderElement> { + let mut rv = ArrayVec::<_, 4>::new(); + + if self.is_off { + return rv.into_iter(); + } + + let mut push = |buffer, location: Point<i32, Logical>| { + let elem = SolidColorRenderElement::from_buffer( + buffer, + location.to_physical_precise_round(scale), + scale, + 1., + Kind::Unspecified, + ); + rv.push(elem); + }; + + if self.is_border { + for (buf, loc) in zip(&self.buffers, self.locations) { + push(buf, loc); + } + } else { + push(&self.buffers[0], self.locations[0]); + } + + rv.into_iter() + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 8946ffd0..5d747cfe 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -29,47 +29,30 @@ //! compromise we only keep the first workspace there, and move the rest to the primary output, //! making the primary output their original output. -use std::cmp::{max, min}; -use std::iter::zip; use std::mem; use std::rc::Rc; use std::time::Duration; -use arrayvec::ArrayVec; -use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement}; -use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement; -use smithay::backend::renderer::element::utils::{ - CropRenderElement, Relocate, RelocateRenderElement, -}; -use smithay::backend::renderer::element::{AsRenderElements, Kind}; -use smithay::backend::renderer::gles::GlesRenderer; -use smithay::backend::renderer::ImportAll; use smithay::desktop::space::SpaceElement; -use smithay::desktop::{layer_map_for_output, Window}; +use smithay::desktop::Window; use smithay::output::Output; use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1; use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel; use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; -use smithay::render_elements; -use smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform}; +use smithay::utils::{Logical, Point, Size, Transform}; use smithay::wayland::compositor::{send_surface_state, with_states}; use smithay::wayland::shell::xdg::SurfaceCachedState; +pub use self::monitor::MonitorRenderElement; +use self::monitor::{Monitor, WorkspaceSwitch, WorkspaceSwitchGesture}; +use self::workspace::{compute_working_area, ColumnWidth, OutputId, Workspace}; use crate::animation::Animation; -use crate::config::{self, Color, Config, PresetWidth, SizeChange, Struts}; +use crate::config::{self, Config, SizeChange, Struts}; use crate::utils::output_size; -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct OutputId(String); - -render_elements! { - #[derive(Debug)] - pub WorkspaceRenderElement<R> where R: ImportAll; - Wayland = WaylandSurfaceRenderElement<R>, - FocusRing = SolidColorRenderElement, -} -pub type MonitorRenderElement<R> = - RelocateRenderElement<CropRenderElement<WorkspaceRenderElement<R>>>; +mod focus_ring; +mod monitor; +mod workspace; pub trait LayoutElement: SpaceElement + PartialEq + Clone { fn request_size(&self, size: Size<i32, Logical>); @@ -107,103 +90,8 @@ enum MonitorSet<W: LayoutElement> { }, } -#[derive(Debug)] -pub struct Monitor<W: LayoutElement> { - /// Output for this monitor. - output: Output, - // Must always contain at least one. - workspaces: Vec<Workspace<W>>, - /// Index of the currently active workspace. - active_workspace_idx: usize, - /// In-progress switch between workspaces. - workspace_switch: Option<WorkspaceSwitch>, - /// Configurable properties of the layout. - options: Rc<Options>, -} - -#[derive(Debug)] -enum WorkspaceSwitch { - Animation(Animation), - Gesture(WorkspaceSwitchGesture), -} - -#[derive(Debug)] -struct WorkspaceSwitchGesture { - /// Index of the workspace where the gesture was started. - center_idx: usize, - /// Current, fractional workspace index. - current_idx: f64, -} - -#[derive(Debug)] -pub struct Workspace<W: LayoutElement> { - /// The original output of this workspace. - /// - /// Most of the time this will be the workspace's current output, however, after an output - /// disconnection, it may remain pointing to the disconnected output. - original_output: OutputId, - - /// Current output of this workspace. - output: Option<Output>, - - /// Latest known view size for this workspace. - /// - /// This should be computed from the current workspace output size, or, if all outputs have - /// been disconnected, preserved until a new output is connected. - view_size: Size<i32, Logical>, - - /// Latest known working area for this workspace. - /// - /// This is similar to view size, but takes into account things like layer shell exclusive - /// zones. - working_area: Rectangle<i32, Logical>, - - /// Columns of windows on this workspace. - columns: Vec<Column<W>>, - - /// Index of the currently active column, if any. - active_column_idx: usize, - - /// Focus ring buffer and parameters. - focus_ring: FocusRing, - - /// Offset of the view computed from the active column. - /// - /// Any gaps, including left padding from work area left exclusive zone, is handled - /// with this view offset (rather than added as a constant elsewhere in the code). This allows - /// for natural handling of fullscreen windows, which must ignore work area padding. - view_offset: i32, - - /// Animation of the view offset, if one is currently ongoing. - view_offset_anim: Option<Animation>, - - /// Whether to activate the previous, rather than the next, column upon column removal. - /// - /// When a new column is created and removed with no focus changes in-between, it is more - /// natural to activate the previously-focused column. This variable tracks that. - /// - /// Since we only create-and-activate columns immediately to the right of the active column (in - /// contrast to tabs in Firefox, for example), we can track this as a bool, rather than an - /// index of the previous column to activate. - activate_prev_column_on_removal: bool, - - /// Configurable properties of the layout. - options: Rc<Options>, -} - -#[derive(Debug)] -struct FocusRing { - buffers: [SolidColorBuffer; 4], - locations: [Point<i32, Logical>; 4], - is_off: bool, - is_border: bool, - width: i32, - active_color: Color, - inactive_color: Color, -} - #[derive(Debug, PartialEq)] -struct Options { +pub struct Options { /// Padding around windows in logical pixels. gaps: i32, /// Extra padding around the working area in logical pixels. @@ -263,92 +151,6 @@ impl Options { } } -/// Width of a column. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ColumnWidth { - /// Proportion of the current view width. - Proportion(f64), - /// One of the proportion presets. - /// - /// This is separate from Proportion in order to be able to reliably cycle between preset - /// proportions. - Preset(usize), - /// Fixed width in logical pixels. - Fixed(i32), -} - -impl From<PresetWidth> for ColumnWidth { - fn from(value: PresetWidth) -> Self { - match value { - PresetWidth::Proportion(p) => Self::Proportion(p.clamp(0., 10000.)), - PresetWidth::Fixed(f) => Self::Fixed(f.clamp(1, 100000)), - } - } -} - -/// Height of a window in a column. -/// -/// Proportional height is intentionally omitted. With column widths you frequently want e.g. two -/// columns side-by-side with 50% width each, and you want them to remain this way when moving to a -/// differently sized monitor. Windows in a column, however, already auto-size to fill the available -/// height, giving you this behavior. The only reason to set a different window height, then, is -/// when you want something in the window to fit exactly, e.g. to fit 30 lines in a terminal, which -/// corresponds to the `Fixed` variant. -/// -/// This does not preclude the usual set of binds to set or resize a window proportionally. Just, -/// they are converted to, and stored as fixed height right away, so that once you resize a window -/// to fit the desired content, it can never become smaller than that when moving between monitors. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum WindowHeight { - /// Automatically computed height, evenly distributed across the column. - Auto, - /// Fixed height in logical pixels. - Fixed(i32), -} - -#[derive(Debug)] -struct Column<W: LayoutElement> { - /// Windows in this column. - /// - /// Must be non-empty. - windows: Vec<W>, - - /// Heights of the windows. - /// - /// Must have the same number of elements as `windows`. - heights: Vec<WindowHeight>, - - /// Index of the currently active window. - active_window_idx: usize, - - /// Desired width of this column. - /// - /// If the column is full-width or full-screened, this is the width that should be restored - /// upon unfullscreening and untoggling full-width. - width: ColumnWidth, - - /// Whether this column is full-width. - is_full_width: bool, - - /// Whether this column contains a single full-screened window. - is_fullscreen: bool, - - /// Latest known view size for this column's workspace. - view_size: Size<i32, Logical>, - - /// Latest known working area for this column's workspace. - working_area: Rectangle<i32, Logical>, - - /// Configurable properties of the layout. - options: Rc<Options>, -} - -impl OutputId { - pub fn new(output: &Output) -> Self { - Self(output.name()) - } -} - impl LayoutElement for Window { fn request_size(&self, size: Size<i32, Logical>) { self.toplevel().with_pending_state(|state| { @@ -394,117 +196,6 @@ impl LayoutElement for Window { } } -impl FocusRing { - fn update( - &mut self, - win_pos: Point<i32, Logical>, - win_size: Size<i32, Logical>, - is_border: bool, - ) { - if is_border { - self.buffers[0].resize((win_size.w + self.width * 2, self.width)); - self.buffers[1].resize((win_size.w + self.width * 2, self.width)); - self.buffers[2].resize((self.width, win_size.h)); - self.buffers[3].resize((self.width, win_size.h)); - - self.locations[0] = win_pos + Point::from((-self.width, -self.width)); - self.locations[1] = win_pos + Point::from((-self.width, win_size.h)); - self.locations[2] = win_pos + Point::from((-self.width, 0)); - self.locations[3] = win_pos + Point::from((win_size.w, 0)); - } else { - let size = win_size + Size::from((self.width * 2, self.width * 2)); - self.buffers[0].resize(size); - self.locations[0] = win_pos - Point::from((self.width, self.width)); - } - - self.is_border = is_border; - } - - fn set_active(&mut self, is_active: bool) { - let color = if is_active { - self.active_color.into() - } else { - self.inactive_color.into() - }; - - for buf in &mut self.buffers { - buf.set_color(color); - } - } - - fn render(&self, scale: Scale<f64>) -> impl Iterator<Item = SolidColorRenderElement> { - let mut rv = ArrayVec::<_, 4>::new(); - - if self.is_off { - return rv.into_iter(); - } - - let mut push = |buffer, location: Point<i32, Logical>| { - let elem = SolidColorRenderElement::from_buffer( - buffer, - location.to_physical_precise_round(scale), - scale, - 1., - Kind::Unspecified, - ); - rv.push(elem); - }; - - if self.is_border { - for (buf, loc) in zip(&self.buffers, self.locations) { - push(buf, loc); - } - } else { - push(&self.buffers[0], self.locations[0]); - } - - rv.into_iter() - } -} - -impl FocusRing { - fn new(config: config::FocusRing) -> Self { - Self { - buffers: Default::default(), - locations: Default::default(), - is_off: config.off, - is_border: false, - width: config.width.into(), - active_color: config.active_color, - inactive_color: config.inactive_color, - } - } -} - -impl WorkspaceSwitch { - fn current_idx(&self) -> f64 { - match self { - WorkspaceSwitch::Animation(anim) => anim.value(), - WorkspaceSwitch::Gesture(gesture) => gesture.current_idx, - } - } - - /// Returns `true` if the workspace switch is [`Animation`]. - /// - /// [`Animation`]: WorkspaceSwitch::Animation - #[must_use] - fn is_animation(&self) -> bool { - matches!(self, Self::Animation(..)) - } -} - -impl ColumnWidth { - fn resolve(self, options: &Options, view_width: i32) -> i32 { - match self { - ColumnWidth::Proportion(proportion) => { - ((view_width - options.gaps) as f64 * proportion).floor() as i32 - options.gaps - } - ColumnWidth::Preset(idx) => options.preset_widths[idx].resolve(options, view_width), - ColumnWidth::Fixed(width) => width, - } - } -} - impl<W: LayoutElement> Layout<W> { pub fn new(config: &Config) -> Self { Self { @@ -1578,1754 +1269,6 @@ impl<W: LayoutElement> Default for MonitorSet<W> { } } -impl<W: LayoutElement> Monitor<W> { - fn new(output: Output, workspaces: Vec<Workspace<W>>, options: Rc<Options>) -> Self { - Self { - output, - workspaces, - active_workspace_idx: 0, - workspace_switch: None, - options, - } - } - - fn active_workspace(&mut self) -> &mut Workspace<W> { - &mut self.workspaces[self.active_workspace_idx] - } - - fn activate_workspace(&mut self, idx: usize) { - if self.active_workspace_idx == idx { - return; - } - - let current_idx = self - .workspace_switch - .as_ref() - .map(|s| s.current_idx()) - .unwrap_or(self.active_workspace_idx as f64); - - self.active_workspace_idx = idx; - - self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new( - current_idx, - idx as f64, - Duration::from_millis(250), - ))); - } - - pub fn add_window( - &mut self, - workspace_idx: usize, - window: W, - activate: bool, - width: ColumnWidth, - is_full_width: bool, - ) { - let workspace = &mut self.workspaces[workspace_idx]; - - workspace.add_window(window.clone(), activate, width, is_full_width); - - // After adding a new window, workspace becomes this output's own. - workspace.original_output = OutputId::new(&self.output); - - if workspace_idx == self.workspaces.len() - 1 { - // Insert a new empty workspace. - let ws = Workspace::new(self.output.clone(), self.options.clone()); - self.workspaces.push(ws); - } - - if activate { - self.activate_workspace(workspace_idx); - } - } - - fn clean_up_workspaces(&mut self) { - assert!(self.workspace_switch.is_none()); - - for idx in (0..self.workspaces.len() - 1).rev() { - if self.active_workspace_idx == idx { - continue; - } - - if !self.workspaces[idx].has_windows() { - self.workspaces.remove(idx); - if self.active_workspace_idx > idx { - self.active_workspace_idx -= 1; - } - } - } - } - - pub fn move_left(&mut self) { - self.active_workspace().move_left(); - } - - pub fn move_right(&mut self) { - self.active_workspace().move_right(); - } - - pub fn move_down(&mut self) { - self.active_workspace().move_down(); - } - - pub fn move_up(&mut self) { - self.active_workspace().move_up(); - } - - pub fn move_down_or_to_workspace_down(&mut self) { - let workspace = self.active_workspace(); - if workspace.columns.is_empty() { - return; - } - let column = &mut workspace.columns[workspace.active_column_idx]; - let curr_idx = column.active_window_idx; - let new_idx = min(column.active_window_idx + 1, column.windows.len() - 1); - if curr_idx == new_idx { - self.move_to_workspace_down(); - } else { - workspace.move_down(); - } - } - - pub fn move_up_or_to_workspace_up(&mut self) { - let workspace = self.active_workspace(); - if workspace.columns.is_empty() { - return; - } - let curr_idx = workspace.columns[workspace.active_column_idx].active_window_idx; - let new_idx = curr_idx.saturating_sub(1); - if curr_idx == new_idx { - self.move_to_workspace_up(); - } else { - workspace.move_up(); - } - } - - pub fn focus_left(&mut self) { - self.active_workspace().focus_left(); - } - - pub fn focus_right(&mut self) { - self.active_workspace().focus_right(); - } - - pub fn focus_down(&mut self) { - self.active_workspace().focus_down(); - } - - pub fn focus_up(&mut self) { - self.active_workspace().focus_up(); - } - - pub fn focus_window_or_workspace_down(&mut self) { - let workspace = self.active_workspace(); - if workspace.columns.is_empty() { - self.switch_workspace_down(); - } else { - let column = &workspace.columns[workspace.active_column_idx]; - let curr_idx = column.active_window_idx; - let new_idx = min(column.active_window_idx + 1, column.windows.len() - 1); - if curr_idx == new_idx { - self.switch_workspace_down(); - } else { - workspace.focus_down(); - } - } - } - - pub fn focus_window_or_workspace_up(&mut self) { - let workspace = self.active_workspace(); - if workspace.columns.is_empty() { - self.switch_workspace_up(); - } else { - let curr_idx = workspace.columns[workspace.active_column_idx].active_window_idx; - let new_idx = curr_idx.saturating_sub(1); - if curr_idx == new_idx { - self.switch_workspace_up(); - } else { - workspace.focus_up(); - } - } - } - - pub fn move_to_workspace_up(&mut self) { - let source_workspace_idx = self.active_workspace_idx; - - let new_idx = source_workspace_idx.saturating_sub(1); - if new_idx == source_workspace_idx { - return; - } - - let workspace = &mut self.workspaces[source_workspace_idx]; - if workspace.columns.is_empty() { - return; - } - - let column = &mut workspace.columns[workspace.active_column_idx]; - let width = column.width; - let is_full_width = column.is_full_width; - let window = column.windows[column.active_window_idx].clone(); - workspace.remove_window(&window); - - self.add_window(new_idx, window, true, width, is_full_width); - } - - pub fn move_to_workspace_down(&mut self) { - let source_workspace_idx = self.active_workspace_idx; - - let new_idx = min(source_workspace_idx + 1, self.workspaces.len() - 1); - if new_idx == source_workspace_idx { - return; - } - - let workspace = &mut self.workspaces[source_workspace_idx]; - if workspace.columns.is_empty() { - return; - } - - let column = &mut workspace.columns[workspace.active_column_idx]; - let width = column.width; - let is_full_width = column.is_full_width; - let window = column.windows[column.active_window_idx].clone(); - workspace.remove_window(&window); - - self.add_window(new_idx, window, true, width, is_full_width); - } - - pub fn move_to_workspace(&mut self, idx: usize) { - let source_workspace_idx = self.active_workspace_idx; - - let new_idx = min(idx, self.workspaces.len() - 1); - if new_idx == source_workspace_idx { - return; - } - - let workspace = &mut self.workspaces[source_workspace_idx]; - if workspace.columns.is_empty() { - return; - } - - let column = &mut workspace.columns[workspace.active_column_idx]; - let width = column.width; - let is_full_width = column.is_full_width; - let window = column.windows[column.active_window_idx].clone(); - workspace.remove_window(&window); - - self.add_window(new_idx, window, true, width, is_full_width); - - // Don't animate this action. - self.workspace_switch = None; - - self.clean_up_workspaces(); - } - - pub fn switch_workspace_up(&mut self) { - self.activate_workspace(self.active_workspace_idx.saturating_sub(1)); - } - - pub fn switch_workspace_down(&mut self) { - self.activate_workspace(min( - self.active_workspace_idx + 1, - self.workspaces.len() - 1, - )); - } - - pub fn switch_workspace(&mut self, idx: usize) { - self.activate_workspace(min(idx, self.workspaces.len() - 1)); - // Don't animate this action. - self.workspace_switch = None; - - self.clean_up_workspaces(); - } - - pub fn consume_into_column(&mut self) { - self.active_workspace().consume_into_column(); - } - - pub fn expel_from_column(&mut self) { - self.active_workspace().expel_from_column(); - } - - pub fn center_column(&mut self) { - self.active_workspace().center_column(); - } - - pub fn focus(&self) -> Option<&W> { - let workspace = &self.workspaces[self.active_workspace_idx]; - if !workspace.has_windows() { - return None; - } - - let column = &workspace.columns[workspace.active_column_idx]; - Some(&column.windows[column.active_window_idx]) - } - - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { - if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch { - anim.set_current_time(current_time); - if anim.is_done() { - self.workspace_switch = None; - self.clean_up_workspaces(); - } - } - - for ws in &mut self.workspaces { - ws.advance_animations(current_time, is_active); - } - } - - pub fn are_animations_ongoing(&self) -> bool { - self.workspace_switch - .as_ref() - .is_some_and(|s| s.is_animation()) - || self.workspaces.iter().any(|ws| ws.are_animations_ongoing()) - } - - pub fn are_transitions_ongoing(&self) -> bool { - self.workspace_switch.is_some() - || self.workspaces.iter().any(|ws| ws.are_animations_ongoing()) - } - - fn update_config(&mut self, options: Rc<Options>) { - for ws in &mut self.workspaces { - ws.update_config(options.clone()); - } - - if self.options.struts != options.struts { - let view_size = output_size(&self.output); - let working_area = compute_working_area(&self.output, options.struts); - - for ws in &mut self.workspaces { - ws.set_view_size(view_size, working_area); - } - } - - self.options = options; - } - - fn toggle_width(&mut self) { - self.active_workspace().toggle_width(); - } - - fn toggle_full_width(&mut self) { - self.active_workspace().toggle_full_width(); - } - - fn set_column_width(&mut self, change: SizeChange) { - self.active_workspace().set_column_width(change); - } - - fn set_window_height(&mut self, change: SizeChange) { - self.active_workspace().set_window_height(change); - } - - fn move_workspace_down(&mut self) { - let new_idx = min(self.active_workspace_idx + 1, self.workspaces.len() - 1); - if new_idx == self.active_workspace_idx { - return; - } - - self.workspaces.swap(self.active_workspace_idx, new_idx); - - if new_idx == self.workspaces.len() - 1 { - // Insert a new empty workspace. - let ws = Workspace::new(self.output.clone(), self.options.clone()); - self.workspaces.push(ws); - } - - self.activate_workspace(new_idx); - self.workspace_switch = None; - - self.clean_up_workspaces(); - } - - fn move_workspace_up(&mut self) { - let new_idx = self.active_workspace_idx.saturating_sub(1); - if new_idx == self.active_workspace_idx { - return; - } - - self.workspaces.swap(self.active_workspace_idx, new_idx); - - if self.active_workspace_idx == self.workspaces.len() - 1 { - // Insert a new empty workspace. - let ws = Workspace::new(self.output.clone(), self.options.clone()); - self.workspaces.push(ws); - } - - self.activate_workspace(new_idx); - self.workspace_switch = None; - - self.clean_up_workspaces(); - } - - pub fn window_under( - &self, - pos_within_output: Point<f64, Logical>, - ) -> Option<(&W, Point<i32, Logical>)> { - match &self.workspace_switch { - Some(switch) => { - let size = output_size(&self.output); - - let render_idx = switch.current_idx(); - let before_idx = render_idx.floor() as usize; - let after_idx = render_idx.ceil() as usize; - - let offset = ((render_idx - before_idx as f64) * size.h as f64).round() as i32; - - let (idx, ws_offset) = if pos_within_output.y < (size.h - offset) as f64 { - (before_idx, Point::from((0, offset))) - } else { - (after_idx, Point::from((0, -size.h + offset))) - }; - - let ws = &self.workspaces[idx]; - let (win, win_pos) = ws.window_under(pos_within_output + ws_offset.to_f64())?; - Some((win, win_pos - ws_offset)) - } - None => { - let ws = &self.workspaces[self.active_workspace_idx]; - ws.window_under(pos_within_output) - } - } - } - - pub fn render_above_top_layer(&self) -> bool { - // Render above the top layer only if the view is stationary. - if self.workspace_switch.is_some() { - return false; - } - - let ws = &self.workspaces[self.active_workspace_idx]; - ws.render_above_top_layer() - } -} - -impl Monitor<Window> { - pub fn render_elements( - &self, - renderer: &mut GlesRenderer, - ) -> Vec<MonitorRenderElement<GlesRenderer>> { - let _span = tracy_client::span!("Monitor::render_elements"); - - let output_scale = Scale::from(self.output.current_scale().fractional_scale()); - let output_transform = self.output.current_transform(); - let output_mode = self.output.current_mode().unwrap(); - let size = output_transform.transform_size(output_mode.size); - - match &self.workspace_switch { - Some(switch) => { - let render_idx = switch.current_idx(); - let before_idx = render_idx.floor() as usize; - let after_idx = render_idx.ceil() as usize; - - let offset = ((render_idx - before_idx as f64) * size.h as f64).round() as i32; - - let before = self.workspaces[before_idx].render_elements(renderer); - let after = self.workspaces[after_idx].render_elements(renderer); - - let before = before.into_iter().filter_map(|elem| { - Some(RelocateRenderElement::from_element( - CropRenderElement::from_element( - elem, - output_scale, - Rectangle::from_extemities((0, offset), (size.w, size.h)), - )?, - (0, -offset), - Relocate::Relative, - )) - }); - let after = after.into_iter().filter_map(|elem| { - Some(RelocateRenderElement::from_element( - CropRenderElement::from_element( - elem, - output_scale, - Rectangle::from_extemities((0, 0), (size.w, offset)), - )?, - (0, -offset + size.h), - Relocate::Relative, - )) - }); - before.chain(after).collect() - } - None => { - let elements = self.workspaces[self.active_workspace_idx].render_elements(renderer); - elements - .into_iter() - .filter_map(|elem| { - Some(RelocateRenderElement::from_element( - CropRenderElement::from_element( - elem, - output_scale, - // HACK: set infinite crop bounds due to a damage tracking bug - // which causes glitched rendering for maximized GTK windows. - // FIXME: use proper bounds after fixing the Crop element. - Rectangle::from_loc_and_size( - (-i32::MAX / 2, -i32::MAX / 2), - (i32::MAX, i32::MAX), - ), - // Rectangle::from_loc_and_size((0, 0), size), - )?, - (0, 0), - Relocate::Relative, - )) - }) - .collect() - } - } - } -} - -impl<W: LayoutElement> Workspace<W> { - fn new(output: Output, options: Rc<Options>) -> Self { - let working_area = compute_working_area(&output, options.struts); - Self { - original_output: OutputId::new(&output), - view_size: output_size(&output), - working_area, - output: Some(output), - columns: vec![], - active_column_idx: 0, - focus_ring: FocusRing::new(options.focus_ring), - view_offset: 0, - view_offset_anim: None, - activate_prev_column_on_removal: false, - options, - } - } - - fn new_no_outputs(options: Rc<Options>) -> Self { - Self { - output: None, - original_output: OutputId(String::new()), - view_size: Size::from((1280, 720)), - working_area: Rectangle::from_loc_and_size((0, 0), (1280, 720)), - columns: vec![], - active_column_idx: 0, - focus_ring: FocusRing::new(options.focus_ring), - view_offset: 0, - view_offset_anim: None, - activate_prev_column_on_removal: false, - options, - } - } - - pub fn advance_animations(&mut self, current_time: Duration, is_active: bool) { - match &mut self.view_offset_anim { - Some(anim) => { - anim.set_current_time(current_time); - self.view_offset = anim.value().round() as i32; - if anim.is_done() { - self.view_offset_anim = None; - } - } - None => (), - } - - let view_pos = self.view_pos(); - - // This shall one day become a proper animation. - if !self.columns.is_empty() { - let col = &self.columns[self.active_column_idx]; - let active_win = &col.windows[col.active_window_idx]; - let geom = active_win.geometry(); - let has_ssd = active_win.has_ssd(); - - let win_pos = Point::from(( - self.col |
