srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src/manager.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/core/src/manager.rs')
-rw-r--r--crates/core/src/manager.rs81
1 files changed, 78 insertions, 3 deletions
diff --git a/crates/core/src/manager.rs b/crates/core/src/manager.rs
index 1fdd7eb..1f69413 100644
--- a/crates/core/src/manager.rs
+++ b/crates/core/src/manager.rs
@@ -2,6 +2,7 @@ use crate::geometry::Rect;
use crate::layout::{Layout, MasterStackLayout, NoOpLayout, TilingConfig};
use crate::monitor::{Monitor, MonitorId};
use crate::placement::{PlacementConfig, SmartPlacement, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH};
+use crate::rules::WindowRule;
use crate::window::{ResizeEdge, TitlebarHit, Window, WindowId};
use crate::workspace::{Workspace, WorkspaceId};
use std::collections::HashMap;
@@ -47,6 +48,7 @@ pub struct WindowManager {
pub placement: PlacementConfig,
drag: Option<DragState>,
resize: Option<ResizeState>,
+ rules: Vec<WindowRule>,
}
impl Default for WindowManager {
@@ -76,9 +78,16 @@ impl WindowManager {
placement: PlacementConfig::default(),
drag: None,
resize: None,
+ rules: Vec::new(),
}
}
+ /// Registers a window rule; on every subsequent `add_window`, the first
+ /// rule whose matcher matches the new window has its actions applied.
+ pub fn add_rule(&mut self, rule: WindowRule) {
+ self.rules.push(rule);
+ }
+
pub fn register_layout(&mut self, name: impl Into<String>, layout: Box<dyn Layout>) {
self.layouts.insert(name.into(), layout);
}
@@ -119,19 +128,45 @@ impl WindowManager {
/// it's left for the next `arrange_workspace` call to place.
pub fn add_window(&mut self, mut window: Window) -> WindowId {
let id = window.id;
- window.workspace = self.current_workspace;
+ let actions = self.rules.iter().find(|r| r.matcher.matches(&window)).map(|r| r.actions.clone());
+
+ let workspace = actions.as_ref().and_then(|a| a.workspace).unwrap_or(self.current_workspace);
+ window.workspace = workspace;
+ if let Some(a) = &actions {
+ if let Some(floating) = a.floating {
+ window.floating = floating;
+ }
+ if let Some(decorated) = a.decorated {
+ window.decorated = decorated;
+ }
+ if let Some(color) = a.border_color {
+ window.border_color = color;
+ }
+ if let Some(width) = a.border_width {
+ window.border_width = width;
+ }
+ }
+
if let Some(monitor) = self.primary_monitor() {
window.monitor = monitor.id;
- let layout_name = self.workspace(self.current_workspace).map(|w| w.layout.clone()).unwrap_or_default();
+ let layout_name = self.workspace(workspace).map(|w| w.layout.clone()).unwrap_or_default();
if layout_name != "tiling" {
- let existing: Vec<Rect> = self.windows_on_workspace(self.current_workspace).map(|w| w.geometry).collect();
+ let existing: Vec<Rect> = self.windows_on_workspace(workspace).map(|w| w.geometry).collect();
let size = (window.geometry.width, window.geometry.height);
window.geometry = SmartPlacement::place(monitor, &existing, size, &self.placement);
}
}
+ if let Some(geometry) = actions.as_ref().and_then(|a| a.geometry) {
+ window.geometry = geometry;
+ }
+ let maximize = actions.as_ref().and_then(|a| a.maximized).unwrap_or(false);
+
self.windows.insert(id, window);
self.order.push(id);
self.focused = Some(id);
+ if maximize {
+ self.toggle_maximize(id);
+ }
id
}
@@ -711,6 +746,46 @@ mod tests {
}
#[test]
+ fn matching_rule_floats_new_window_on_add() {
+ let mut wm = wm_with_monitor();
+ wm.set_layout(wm.current_workspace(), "tiling");
+ wm.add_rule(WindowRule {
+ matcher: crate::rules::WindowMatch { title_contains: Some("calculator".into()), class: None },
+ actions: crate::rules::WindowRuleActions { floating: Some(true), ..Default::default() },
+ });
+ let id = wm.alloc_window_id();
+ wm.add_window(Window::new(id, "Calculator"));
+ assert!(wm.is_floating(id));
+ }
+
+ #[test]
+ fn non_matching_rule_leaves_window_untouched() {
+ let mut wm = wm_with_monitor();
+ wm.add_rule(WindowRule {
+ matcher: crate::rules::WindowMatch { title_contains: Some("calculator".into()), class: None },
+ actions: crate::rules::WindowRuleActions { floating: Some(true), ..Default::default() },
+ });
+ let id = wm.alloc_window_id();
+ wm.add_window(Window::new(id, "Terminal"));
+ assert!(!wm.is_floating(id));
+ }
+
+ #[test]
+ fn rule_assigns_window_to_target_workspace() {
+ let mut wm = wm_with_monitor();
+ let target = wm.add_workspace("scratch", "dynamic");
+ wm.add_rule(WindowRule {
+ matcher: crate::rules::WindowMatch { title_contains: None, class: Some("scratchpad".into()) },
+ actions: crate::rules::WindowRuleActions { workspace: Some(target), ..Default::default() },
+ });
+ let id = wm.alloc_window_id();
+ let mut w = Window::new(id, "notes");
+ w.app_id = "scratchpad".into();
+ wm.add_window(w);
+ assert_eq!(wm.window(id).unwrap().workspace, target);
+ }
+
+ #[test]
fn removing_a_workspace_reassigns_its_windows() {
let mut wm = wm_with_monitor();
let ws2 = wm.add_workspace("2", "dynamic");