| 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> = self |
| 28 | .windows |
| 29 | .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 | id |
| 38 | } |
| 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.windows |
| 44 | .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> = parked |
| 50 | .floating |
| 51 | .into_iter() |
| 52 | .filter(|&w| ours(self, w) && self.windows[w].floating) |
| 53 | .collect(); |
| 54 | for w in tree |
| 55 | .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 = parked |
| 71 | .viewed |
| 72 | .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> = self |
| 101 | .windows |
| 102 | .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 = self |
| 109 | .outputs |
| 110 | .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 | true |
| 140 | } |
| 141 | _ => false, |
| 142 | } |
| 143 | } |
| 144 | } |