aboutsummaryrefslogtreecommitdiff
path: root/src/layout/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout/mod.rs')
-rw-r--r--src/layout/mod.rs2079
1 files changed, 11 insertions, 2068 deletions
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.column_x(self.active_column_idx) - view_pos,
- col.window_y(col.active_window_idx),
- ));
-
- self.focus_ring.update(win_pos, geom.size, has_ssd);
- self.focus_ring.set_active(is_active);
- }
- }
-
- pub fn are_animations_ongoing(&self) -> bool {
- self.view_offset_anim.is_some()
- }
-
- fn update_config(&mut self, options: Rc<Options>) {
- let c = &options.focus_ring;
- self.focus_ring.is_off = c.off;
- self.focus_ring.width = c.width.into();
- self.focus_ring.active_color = c.active_color;
- self.focus_ring.inactive_color = c.inactive_color;
- // The focus ring buffer will be updated in a subsequent update_animations call.
-
- for column in &mut self.columns {
- column.update_config(options.clone());
- }
-
- self.options = options;
- }
-
- fn windows(&self) -> impl Iterator<Item = &W> + '_ {
- self.columns.iter().flat_map(|col| col.windows.iter())
- }
-
- fn set_output(&mut self, output: Option<Output>) {
- if self.output == output {
- return;
- }
-
- if let Some(output) = self.output.take() {
- for win in self.windows() {
- win.output_leave(&output);
- }
- }
-
- self.output = output;
-
- if let Some(output) = &self.output {
- let working_area = compute_working_area(output, self.options.struts);
- self.set_view_size(output_size(output), working_area);
-
- for win in self.windows() {
- self.enter_output_for_window(win);
- }
- }
- }
-
- fn enter_output_for_window(&self, window: &W) {
- if let Some(output) = &self.output {
- prepare_for_output(window, output);
-
- // FIXME: proper overlap.
- window.output_enter(
- output,
- Rectangle::from_loc_and_size((0, 0), (i32::MAX, i32::MAX)),
- );
- }
- }
-
- fn set_view_size(&mut self, size: Size<i32, Logical>, working_area: Rectangle<i32, Logical>) {
- if self.view_size == size && self.working_area == working_area {
- return;
- }
-
- self.view_size = size;
- self.working_area = working_area;
-
- for col in &mut self.columns {
- col.set_view_size(self.view_size, self.working_area);
- }
- }
-
- fn toplevel_bounds(&self) -> Size<i32, Logical> {
- Size::from((
- max(self.working_area.size.w - self.options.gaps * 2, 1),
- max(self.working_area.size.h - self.options.gaps * 2, 1),
- ))
- }
-
- pub fn configure_new_window(&self, window: &Window) {
- let width = if let Some(width) = self.options.default_width {
- max(1, width.resolve(&self.options, self.working_area.size.w))
- } else {
- 0
- };
-
- let height = self.working_area.size.h - self.options.gaps * 2;
- let size = Size::from((width, max(height, 1)));
-
- let bounds = self.toplevel_bounds();
-
- if let Some(output) = self.output.as_ref() {
- prepare_for_output(window, output);
- }
-
- window.toplevel().with_pending_state(|state| {
- state.size = Some(size);
- state.bounds = Some(bounds);
- });
- }
-
- fn compute_new_view_offset_for_column(&self, current_x: i32, idx: usize) -> i32 {
- if self.columns[idx].is_fullscreen {
- return 0;
- }
-
- let new_col_x = self.column_x(idx);
-
- let final_x = if let Some(anim) = &self.view_offset_anim {
- current_x - self.view_offset + anim.to().round() as i32
- } else {
- current_x
- };
-
- let new_offset = compute_new_view_offset(
- final_x + self.working_area.loc.x,
- self.working_area.size.w,
- new_col_x,
- self.columns[idx].width(),