Diff
diff --cc src/monitor.rs
index cae6f33,c87299c..caa18ad
--- a/src/monitor.rs
+++ b/src/monitor.rs
@@@ -6,6 -6,30 +6,36 @@@ use x11rb::rust_connection::RustConnect
type WmResult<T> = Result<T, WmError>;
+ #[derive(Debug, Clone)]
+ pub struct Pertag {
+ pub current_tag: usize,
+ pub previous_tag: usize,
+ pub num_masters: Vec<i32>,
+ pub master_factors: Vec<f32>,
+ pub layouts: Vec<String>,
+ pub show_bars: Vec<bool>,
+ }
+
+ impl Pertag {
- pub fn new(num_tags: usize, default_num_master: i32, default_master_factor: f32, default_show_bar: bool, default_layout: &str) -> Self {
++ pub fn new(
++ num_tags: usize,
++ default_num_master: i32,
++ default_master_factor: f32,
++ default_show_bar: bool,
++ default_layout: &str,
++ ) -> Self {
+ let len = num_tags + 1;
+ Self {
+ current_tag: 1,
+ previous_tag: 1,
+ num_masters: vec![default_num_master; len],
+ master_factors: vec![default_master_factor; len],
+ layouts: vec![default_layout.to_string(); len],
+ show_bars: vec![default_show_bar; len],
+ }
+ }
+ }
+
#[derive(Debug, Clone)]
pub struct Monitor {
pub layout_symbol: String,
@@@ -35,7 -59,7 +65,8 @@@
pub stack_head: Option<Window>,
pub bar_window: Option<Window>,
pub layout_indices: [usize; 2],
+ pub scroll_offset: i32,
+ pub pertag: Option<Pertag>,
}
impl Monitor {
@@@ -68,7 -92,7 +99,8 @@@
stack_head: None,
bar_window: None,
layout_indices: [0, 1],
+ scroll_offset: 0,
+ pertag: None,
}
}
diff --cc src/window_manager.rs
index 6c8a546,94180b4..26b9499
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@@ -1,8 -1,7 +1,8 @@@
use crate::Config;
+use crate::animations::{AnimationConfig, ScrollAnimation};
use crate::bar::Bar;
use crate::client::{Client, TagMask};
- use crate::errors::WmError;
+ use crate::errors::{ConfigError, WmError};
use crate::keyboard::{self, Arg, KeyAction, handlers};
use crate::layout::GapConfig;
use crate::layout::tiling::TilingLayout;
@@@ -617,219 -733,21 +740,232 @@@ impl WindowManager
Ok(())
}
+ fn tick_animations(&mut self) -> WmResult<()> {
+ if self.scroll_animation.is_active() {
+ if let Some(new_offset) = self.scroll_animation.update() {
+ if let Some(m) = self.monitors.get_mut(self.selected_monitor) {
+ m.scroll_offset = new_offset;
+ }
+ self.apply_layout()?;
+ self.update_bar()?;
+ }
+ }
+ Ok(())
+ }
+
+ fn scroll_layout(&mut self, direction: i32) -> WmResult<()> {
+ if self.layout.name() != "scrolling" {
+ return Ok(());
+ }
+
+ let monitor_index = self.selected_monitor;
+ let monitor = match self.monitors.get(monitor_index) {
+ Some(m) => m.clone(),
+ None => return Ok(()),
+ };
+
+ let visible_count = if monitor.num_master > 0 {
+ monitor.num_master as usize
+ } else {
+ 2
+ };
+
+ let mut tiled_count = 0;
+ let mut current = self.next_tiled(monitor.clients_head, &monitor);
+ while let Some(window) = current {
+ tiled_count += 1;
+ if let Some(client) = self.clients.get(&window) {
+ current = self.next_tiled(client.next, &monitor);
+ } else {
+ break;
+ }
+ }
+
+ if tiled_count <= visible_count {
+ if let Some(m) = self.monitors.get_mut(monitor_index) {
+ m.scroll_offset = 0;
+ }
+ return Ok(());
+ }
+
+ let outer_gap = if self.gaps_enabled {
+ self.config.gap_outer_vertical
+ } else {
+ 0
+ };
+ let inner_gap = if self.gaps_enabled {
+ self.config.gap_inner_vertical
+ } else {
+ 0
+ };
+
+ let available_width = monitor.screen_width - 2 * outer_gap as i32;
+ let total_inner_gaps = inner_gap as i32 * (visible_count - 1) as i32;
+ let window_width = (available_width - total_inner_gaps) / visible_count as i32;
+ let scroll_amount = window_width + inner_gap as i32;
+
+ let total_width = tiled_count as i32 * window_width + (tiled_count - 1) as i32 * inner_gap as i32;
+ let max_scroll = (total_width - available_width).max(0);
+
+ let current_offset = monitor.scroll_offset;
+ let target_offset = if self.scroll_animation.is_active() {
+ self.scroll_animation.target() + direction * scroll_amount
+ } else {
+ current_offset + direction * scroll_amount
+ };
+ let target_offset = target_offset.clamp(0, max_scroll);
+
+ self.scroll_animation.start(current_offset, target_offset, &self.animation_config);
+
+ Ok(())
+ }
+
+ fn scroll_to_window(&mut self, target_window: Window, animate: bool) -> WmResult<()> {
+ if self.layout.name() != "scrolling" {
+ return Ok(());
+ }
+
+ let monitor_index = self.selected_monitor;
+ let monitor = match self.monitors.get(monitor_index) {
+ Some(m) => m.clone(),
+ None => return Ok(()),
+ };
+
+ let visible_count = if monitor.num_master > 0 {
+ monitor.num_master as usize
+ } else {
+ 2
+ };
+
+ let outer_gap = if self.gaps_enabled {
+ self.config.gap_outer_vertical
+ } else {
+ 0
+ };
+ let inner_gap = if self.gaps_enabled {
+ self.config.gap_inner_vertical
+ } else {
+ 0
+ };
+
+ let mut tiled_windows = Vec::new();
+ let mut current = self.next_tiled(monitor.clients_head, &monitor);
+ while let Some(window) = current {
+ tiled_windows.push(window);
+ if let Some(client) = self.clients.get(&window) {
+ current = self.next_tiled(client.next, &monitor);
+ } else {
+ break;
+ }
+ }
+
+ let target_idx = tiled_windows.iter().position(|&w| w == target_window);
+ let target_idx = match target_idx {
+ Some(idx) => idx,
+ None => return Ok(()),
+ };
+
+ let tiled_count = tiled_windows.len();
+ if tiled_count <= visible_count {
+ if let Some(m) = self.monitors.get_mut(monitor_index) {
+ m.scroll_offset = 0;
+ }
+ return Ok(());
+ }
+
+ let available_width = monitor.screen_width - 2 * outer_gap as i32;
+ let total_inner_gaps = inner_gap as i32 * (visible_count - 1) as i32;
+ let window_width = (available_width - total_inner_gaps) / visible_count as i32;
+ let scroll_step = window_width + inner_gap as i32;
+
+ let total_width = tiled_count as i32 * window_width + (tiled_count - 1) as i32 * inner_gap as i32;
+ let max_scroll = (total_width - available_width).max(0);
+
+ let target_scroll = (target_idx as i32) * scroll_step;
+ let new_offset = target_scroll.clamp(0, max_scroll);
+
+ let current_offset = monitor.scroll_offset;
+ if current_offset != new_offset {
+ if animate {
+ self.scroll_animation.start(current_offset, new_offset, &self.animation_config);
+ } else {
+ if let Some(m) = self.monitors.get_mut(monitor_index) {
+ m.scroll_offset = new_offset;
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn toggle_bar(&mut self) -> WmResult<()> {
+ if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
+ monitor.show_bar = !monitor.show_bar;
+ self.show_bar = monitor.show_bar;
+ if let Some(ref mut pertag) = monitor.pertag {
+ pertag.show_bars[pertag.current_tag] = monitor.show_bar;
+ }
+ }
+ self.apply_layout()?;
+ self.update_bar()?;
+ Ok(())
+ }
+
fn get_layout_symbol(&self) -> String {
let layout_name = self.layout.name();
+
+ if layout_name == "scrolling" {
+ if let Some(monitor) = self.monitors.get(self.selected_monitor) {
+ let visible_count = if monitor.num_master > 0 {
+ monitor.num_master as usize
+ } else {
+ 2
+ };
+
+ let mut tiled_count = 0;
+ let mut current = self.next_tiled(monitor.clients_head, monitor);
+ while let Some(window) = current {
+ tiled_count += 1;
+ if let Some(client) = self.clients.get(&window) {
+ current = self.next_tiled(client.next, monitor);
+ } else {
+ break;
+ }
+ }
+
+ if tiled_count > 0 {
+ let outer_gap = if self.gaps_enabled {
+ self.config.gap_outer_vertical
+ } else {
+ 0
+ };
+ let inner_gap = if self.gaps_enabled {
+ self.config.gap_inner_vertical
+ } else {
+ 0
+ };
+
+ let available_width = monitor.screen_width - 2 * outer_gap as i32;
+ let total_inner_gaps = inner_gap as i32 * (visible_count.min(tiled_count) - 1) as i32;
+ let window_width = if tiled_count <= visible_count {
+ (available_width - total_inner_gaps) / tiled_count as i32
+ } else {
+ (available_width - inner_gap as i32 * (visible_count - 1) as i32) / visible_count as i32
+ };
+
+ let scroll_step = window_width + inner_gap as i32;
+ let first_visible = if scroll_step > 0 {
+ (monitor.scroll_offset / scroll_step) + 1
+ } else {
+ 1
+ };
+ let last_visible = (first_visible + visible_count as i32 - 1).min(tiled_count as i32);
+
+ return format!("[{}-{}/{}]", first_visible, last_visible, tiled_count);
+ }
+ }
+ }
+
self.config
.layout_symbols
.iter()
@@@ -2204,26 -2067,9 +2299,26 @@@
let final_tags = self.clients.get(&window).map(|c| c.tags).unwrap_or(tags);
let _ = self.save_client_tag(window, final_tags);
+ if client_monitor == self.selected_monitor
+ && let Some(old_sel) = self
+ .monitors
+ .get(self.selected_monitor)
+ .and_then(|m| m.selected_client)
+ {
- self.unfocus(old_sel)?;
++ self.unfocus(old_sel, false)?;
+ }
+
+ if let Some(m) = self.monitors.get_mut(client_monitor) {
+ m.selected_client = Some(window);
+ }
+
+ if self.layout.name() == "scrolling" {
+ self.scroll_to_window(window, false)?;
+ }
+
self.apply_layout()?;
self.connection.map_window(window)?;
- self.focus(Some(window))?;
+ self.focus(None)?;
self.update_bar()?;
if self.layout.name() == "tabbed" {
@@@ -2460,11 -2403,7 +2652,12 @@@
};
self.focus(Some(next_window))?;
+
+ if self.layout.name() == "scrolling" {
+ self.scroll_to_window(next_window, true)?;
+ }
+
+ self.restack()?;
self.update_tab_bars()?;
Ok(())