Commit 4cbf881c67bd
tarball3 files changed+239-230
@@ -2,6 +2,8 @@
22
//! tiling tree per output filtered by those views, floating windows,33
//! scratchpads, focus, and restoring windows when an output comes back.44
5
+mod outputs;6
+mod scratchpad;57
mod tree;68
79
#[cfg(test)]@@ -10,6 +12,7 @@ mod tests;
1012
pub use monolith_planner::Rect;1113
pub use tree::{Axis, Dir, Tree};1214
15
+use outputs::Parked;1316
use slotmap::{SlotMap, new_key_type};1417
use std::collections::HashMap;1518
@@ -90,14 +93,6 @@ pub struct Scratchpad {
9093
pub output: Option<OutputId>,9194
}9295
93
-#[derive(Clone, Debug)]94
-struct Parked {95
- tree: Tree<WindowId>,96
- viewed: Vec<TagId>,97
- floating: Vec<WindowId>,98
- area: Rect,99
-}100
-10196
#[derive(Clone, Debug, Default)]10297
pub struct Arrangement {10398
/// Every tiled window on the output. Hidden ones are zero-size seeds.@@ -220,139 +215,6 @@ impl Layout {
220215
}221216
}222217
223
- /// Adds an output. One that was unplugged before gets back its tree,224
- /// views and the windows that had to leave it.225
- pub fn add_output(&mut self, name: &str, area: Rect) -> OutputId {226
- let id = self.outputs.insert(Output {227
- name: name.into(),228
- area,229
- reserved: Insets::default(),230
- tree: Tree::default(),231
- viewed: Vec::new(),232
- floating: Vec::new(),233
- });234
- if let Some(parked) = self.parked.remove(name) {235
- self.restore(id, parked);236
- }237
- self.active.get_or_insert(id);238
- let detached: Vec<WindowId> = self239
- .windows240
- .iter()241
- .filter(|(_, w)| w.output.is_none())242
- .map(|(id, _)| id)243
- .collect();244
- for w in detached {245
- self.windows[w].output = Some(id);246
- self.place(w);247
- }248
- id249
- }250
-251
- fn restore(&mut self, id: OutputId, parked: Parked) {252
- let name = self.outputs[id].name.clone();253
- let ours = |l: &Self, w: WindowId| {254
- l.windows255
- .get(w)256
- .is_some_and(|x| x.evicted_from.as_deref() == Some(&name) && x.scratchpad.is_none())257
- };258
- let mut tree = parked.tree;259
- tree.retain(|w| ours(self, w) && !self.windows[w].floating);260
- let floating: Vec<WindowId> = parked261
- .floating262
- .into_iter()263
- .filter(|&w| ours(self, w) && self.windows[w].floating)264
- .collect();265
- for w in tree266
- .windows()267
- .chain(floating.iter().copied())268
- .collect::<Vec<_>>()269
- {270
- self.unplace(w);271
- let win = &mut self.windows[w];272
- if let (Some(from), Some(rect)) = (win.output, win.float_rect) {273
- win.float_rect = Some(translate(rect, self.outputs[from].area, parked.area));274
- }275
- win.output = Some(id);276
- win.evicted_from = None;277
- }278
- let out = &mut self.outputs[id];279
- out.tree = tree;280
- out.floating = floating;281
- out.viewed = parked282
- .viewed283
- .into_iter()284
- .filter(|&t| self.tags.contains_key(t))285
- .collect();286
- }287
-288
- /// Removes an output. Its windows move to an output viewing their tag,289
- /// or the active one, and remember where they came from.290
- pub fn remove_output(&mut self, o: OutputId) {291
- let Some(out) = self.outputs.remove(o) else {292
- return;293
- };294
- self.parked.insert(295
- out.name.clone(),296
- Parked {297
- tree: out.tree,298
- viewed: out.viewed,299
- floating: out.floating,300
- area: out.area,301
- },302
- );303
- if self.active == Some(o) {304
- self.active = self.outputs.keys().next();305
- }306
- for sp in &mut self.scratchpads {307
- if sp.output == Some(o) {308
- sp.output = self.active;309
- }310
- }311
- let leaving: Vec<WindowId> = self312
- .windows313
- .iter()314
- .filter(|(_, w)| w.output == Some(o))315
- .map(|(id, _)| id)316
- .collect();317
- for w in leaving {318
- let tag = self.windows[w].tag;319
- let target = self320
- .outputs321
- .keys()322
- .find(|&t| self.is_viewed(t, tag))323
- .or(self.active);324
- let win = &mut self.windows[w];325
- if let (Some(t), Some(rect)) = (target, win.float_rect) {326
- win.float_rect = Some(translate(rect, out.area, self.outputs[t].area));327
- }328
- win.output = target;329
- if win.scratchpad.is_some() {330
- continue;331
- }332
- win.evicted_from = Some(out.name.clone());333
- if target.is_some() {334
- self.place(w);335
- }336
- }337
- }338
-339
- pub fn set_output_area(&mut self, o: OutputId, area: Rect) {340
- if let Some(out) = self.outputs.get_mut(o) {341
- out.area = area;342
- }343
- }344
-345
- /// Returns whether anything changed.346
- pub fn set_reserved(&mut self, o: OutputId, reserved: Insets) -> bool {347
- match self.outputs.get_mut(o) {348
- Some(out) if out.reserved != reserved => {349
- out.reserved = reserved;350
- true351
- }352
- _ => false,353
- }354
- }355
-356218
pub fn is_viewed(&self, o: OutputId, tag: TagId) -> bool {357219
tag == self.homeless358220
|| self@@ -760,95 +622,6 @@ impl Layout {
760622
}761623
}762624
763
- /// Adds a window to a named scratchpad, creating it. Scratchpad windows764
- /// float and are shown only by summoning.765
- pub fn scratchpad_add(&mut self, w: WindowId, name: &str) {766
- let Some(win) = self.windows.get(w) else {767
- return;768
- };769
- if let Some(s) = win.scratchpad {770
- self.leave_scratchpad(s, w);771
- }772
- self.unplace(w);773
- let s = match self.scratchpads.iter().position(|sp| sp.name == name) {774
- Some(s) => s,775
- None => {776
- self.scratchpads.push(Scratchpad {777
- name: name.into(),778
- windows: Vec::new(),779
- shown: None,780
- output: None,781
- });782
- self.scratchpads.len() - 1783
- }784
- };785
- self.scratchpads[s].windows.push(w);786
- let area = self.windows[w].output.map(|o| self.outputs[o].area);787
- let win = &mut self.windows[w];788
- win.scratchpad = Some(s);789
- win.floating = true;790
- if win.float_rect.is_none() {791
- win.float_rect = area.map(|a| centred(a, 0.6));792
- }793
- }794
-795
- /// Cycles a scratchpad through off, each window, and off again, on the796
- /// active output.797
- pub fn summon(&mut self, name: &str) -> Option<WindowId> {798
- let s = self.scratchpads.iter().position(|sp| sp.name == name)?;799
- let o = self.active?;800
- let sp = &mut self.scratchpads[s];801
- let next = match sp.shown {802
- None => 0,803
- Some(i) => i + 1,804
- };805
- if next >= sp.windows.len() {806
- sp.shown = None;807
- return None;808
- }809
- let from = sp.output.map(|f| self.outputs[f].area);810
- sp.shown = Some(next);811
- sp.output = Some(o);812
- let w = sp.windows[next];813
- self.last_scratchpad = Some(s);814
- let area = self.outputs[o].area;815
- let win = &mut self.windows[w];816
- if win.output != Some(o) {817
- win.float_rect = match (from, win.float_rect) {818
- (Some(from), Some(r)) => Some(translate(r, from, area)),819
- _ => Some(centred(area, 0.6)),820
- };821
- win.output = Some(o);822
- }823
- self.focus(w);824
- Some(w)825
- }826
-827
- /// Hides a scratchpad, by default the last one summoned.828
- pub fn dismiss(&mut self, name: Option<&str>) {829
- let s = match name {830
- Some(name) => self.scratchpads.iter().position(|sp| sp.name == name),831
- None => self.last_scratchpad,832
- };833
- if let Some(sp) = s.and_then(|s| self.scratchpads.get_mut(s)) {834
- sp.shown = None;835
- }836
- }837
-838
- fn leave_scratchpad(&mut self, s: usize, w: WindowId) {839
- let sp = &mut self.scratchpads[s];840
- let Some(i) = sp.windows.iter().position(|&x| x == w) else {841
- return;842
- };843
- sp.windows.remove(i);844
- sp.shown = match sp.shown {845
- Some(j) if j == i => None,846
- Some(j) if j > i => Some(j - 1),847
- other => other,848
- };849
- self.windows[w].scratchpad = None;850
- }851
-852625
/// Target geometry for everything on `o`, in output-local logical853626
/// coordinates offset by the output's area.854627
pub fn arrange(&self, o: OutputId) -> Arrangement {@@ -0,0 +1,144 @@
1
+use crate::{Insets, Layout, Output, OutputId, Rect, TagId, Tree, WindowId, translate};2
+3
+#[derive(Clone, Debug)]4
+pub(crate) struct Parked {5
+ tree: Tree<WindowId>,6
+ viewed: Vec<TagId>,7
+ floating: Vec<WindowId>,8
+ area: Rect,9
+}10
+11
+impl Layout {12
+ /// Adds an output. One that was unplugged before gets back its tree,13
+ /// views and the windows that had to leave it.14
+ pub fn add_output(&mut self, name: &str, area: Rect) -> OutputId {15
+ let id = self.outputs.insert(Output {16
+ name: name.into(),17
+ area,18
+ reserved: Insets::default(),19
+ tree: Tree::default(),20
+ viewed: Vec::new(),21
+ floating: Vec::new(),22
+ });23
+ if let Some(parked) = self.parked.remove(name) {24
+ self.restore(id, parked);25
+ }26
+ self.active.get_or_insert(id);27
+ let detached: Vec<WindowId> = self28
+ .windows29
+ .iter()30
+ .filter(|(_, w)| w.output.is_none())31
+ .map(|(id, _)| id)32
+ .collect();33
+ for w in detached {34
+ self.windows[w].output = Some(id);35
+ self.place(w);36
+ }37
+ id38
+ }39
+40
+ fn restore(&mut self, id: OutputId, parked: Parked) {41
+ let name = self.outputs[id].name.clone();42
+ let ours = |l: &Self, w: WindowId| {43
+ l.windows44
+ .get(w)45
+ .is_some_and(|x| x.evicted_from.as_deref() == Some(&name) && x.scratchpad.is_none())46
+ };47
+ let mut tree = parked.tree;48
+ tree.retain(|w| ours(self, w) && !self.windows[w].floating);49
+ let floating: Vec<WindowId> = parked50
+ .floating51
+ .into_iter()52
+ .filter(|&w| ours(self, w) && self.windows[w].floating)53
+ .collect();54
+ for w in tree55
+ .windows()56
+ .chain(floating.iter().copied())57
+ .collect::<Vec<_>>()58
+ {59
+ self.unplace(w);60
+ let win = &mut self.windows[w];61
+ if let (Some(from), Some(rect)) = (win.output, win.float_rect) {62
+ win.float_rect = Some(translate(rect, self.outputs[from].area, parked.area));63
+ }64
+ win.output = Some(id);65
+ win.evicted_from = None;66
+ }67
+ let out = &mut self.outputs[id];68
+ out.tree = tree;69
+ out.floating = floating;70
+ out.viewed = parked71
+ .viewed72
+ .into_iter()73
+ .filter(|&t| self.tags.contains_key(t))74
+ .collect();75
+ }76
+77
+ /// Removes an output. Its windows move to an output viewing their tag,78
+ /// or the active one, and remember where they came from.79
+ pub fn remove_output(&mut self, o: OutputId) {80
+ let Some(out) = self.outputs.remove(o) else {81
+ return;82
+ };83
+ self.parked.insert(84
+ out.name.clone(),85
+ Parked {86
+ tree: out.tree,87
+ viewed: out.viewed,88
+ floating: out.floating,89
+ area: out.area,90
+ },91
+ );92
+ if self.active == Some(o) {93
+ self.active = self.outputs.keys().next();94
+ }95
+ for sp in &mut self.scratchpads {96
+ if sp.output == Some(o) {97
+ sp.output = self.active;98
+ }99
+ }100
+ let leaving: Vec<WindowId> = self101
+ .windows102
+ .iter()103
+ .filter(|(_, w)| w.output == Some(o))104
+ .map(|(id, _)| id)105
+ .collect();106
+ for w in leaving {107
+ let tag = self.windows[w].tag;108
+ let target = self109
+ .outputs110
+ .keys()111
+ .find(|&t| self.is_viewed(t, tag))112
+ .or(self.active);113
+ let win = &mut self.windows[w];114
+ if let (Some(t), Some(rect)) = (target, win.float_rect) {115
+ win.float_rect = Some(translate(rect, out.area, self.outputs[t].area));116
+ }117
+ win.output = target;118
+ if win.scratchpad.is_some() {119
+ continue;120
+ }121
+ win.evicted_from = Some(out.name.clone());122
+ if target.is_some() {123
+ self.place(w);124
+ }125
+ }126
+ }127
+128
+ pub fn set_output_area(&mut self, o: OutputId, area: Rect) {129
+ if let Some(out) = self.outputs.get_mut(o) {130
+ out.area = area;131
+ }132
+ }133
+134
+ /// Returns whether anything changed.135
+ pub fn set_reserved(&mut self, o: OutputId, reserved: Insets) -> bool {136
+ match self.outputs.get_mut(o) {137
+ Some(out) if out.reserved != reserved => {138
+ out.reserved = reserved;139
+ true140
+ }141
+ _ => false,142
+ }143
+ }144
+}@@ -0,0 +1,92 @@
1
+use crate::{Layout, Scratchpad, WindowId, centred, translate};2
+3
+impl Layout {4
+ /// Adds a window to a named scratchpad, creating it. Scratchpad windows5
+ /// float and are shown only by summoning.6
+ pub fn scratchpad_add(&mut self, w: WindowId, name: &str) {7
+ let Some(win) = self.windows.get(w) else {8
+ return;9
+ };10
+ if let Some(s) = win.scratchpad {11
+ self.leave_scratchpad(s, w);12
+ }13
+ self.unplace(w);14
+ let s = match self.scratchpads.iter().position(|sp| sp.name == name) {15
+ Some(s) => s,16
+ None => {17
+ self.scratchpads.push(Scratchpad {18
+ name: name.into(),19
+ windows: Vec::new(),20
+ shown: None,21
+ output: None,22
+ });23
+ self.scratchpads.len() - 124
+ }25
+ };26
+ self.scratchpads[s].windows.push(w);27
+ let area = self.windows[w].output.map(|o| self.outputs[o].area);28
+ let win = &mut self.windows[w];29
+ win.scratchpad = Some(s);30
+ win.floating = true;31
+ if win.float_rect.is_none() {32
+ win.float_rect = area.map(|a| centred(a, 0.6));33
+ }34
+ }35
+36
+ /// Cycles a scratchpad through off, each window, and off again, on the37
+ /// active output.38
+ pub fn summon(&mut self, name: &str) -> Option<WindowId> {39
+ let s = self.scratchpads.iter().position(|sp| sp.name == name)?;40
+ let o = self.active?;41
+ let sp = &mut self.scratchpads[s];42
+ let next = match sp.shown {43
+ None => 0,44
+ Some(i) => i + 1,45
+ };46
+ if next >= sp.windows.len() {47
+ sp.shown = None;48
+ return None;49
+ }50
+ let from = sp.output.map(|f| self.outputs[f].area);51
+ sp.shown = Some(next);52
+ sp.output = Some(o);53
+ let w = sp.windows[next];54
+ self.last_scratchpad = Some(s);55
+ let area = self.outputs[o].area;56
+ let win = &mut self.windows[w];57
+ if win.output != Some(o) {58
+ win.float_rect = match (from, win.float_rect) {59
+ (Some(from), Some(r)) => Some(translate(r, from, area)),60
+ _ => Some(centred(area, 0.6)),61
+ };62
+ win.output = Some(o);63
+ }64
+ self.focus(w);65
+ Some(w)66
+ }67
+68
+ /// Hides a scratchpad, by default the last one summoned.69
+ pub fn dismiss(&mut self, name: Option<&str>) {70
+ let s = match name {71
+ Some(name) => self.scratchpads.iter().position(|sp| sp.name == name),72
+ None => self.last_scratchpad,73
+ };74
+ if let Some(sp) = s.and_then(|s| self.scratchpads.get_mut(s)) {75
+ sp.shown = None;76
+ }77
+ }78
+79
+ pub(crate) fn leave_scratchpad(&mut self, s: usize, w: WindowId) {80
+ let sp = &mut self.scratchpads[s];81
+ let Some(i) = sp.windows.iter().position(|&x| x == w) else {82
+ return;83
+ };84
+ sp.windows.remove(i);85
+ sp.shown = match sp.shown {86
+ Some(j) if j == i => None,87
+ Some(j) if j > i => Some(j - 1),88
+ other => other,89
+ };90
+ self.windows[w].scratchpad = None;91
+ }92
+}