| 1 | use crate::{Layout, Scratchpad, WindowId, centre_in, centred, translate}; |
| 2 | |
| 3 | impl Layout { |
| 4 | /// Adds a window to a named scratchpad, creating it. Scratchpad windows |
| 5 | /// 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() - 1 |
| 24 | } |
| 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 the |
| 37 | /// 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].usable()); |
| 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 usable = self.outputs[o].usable(); |
| 57 | let win = &mut self.windows[w]; |
| 58 | if win.output != Some(o) { |
| 59 | win.float_rect = match (from, win.float_rect) { |
| 60 | (Some(from), Some(r)) => Some(translate(r, from, usable)), |
| 61 | _ => Some(centred(area, 0.6)), |
| 62 | }; |
| 63 | win.output = Some(o); |
| 64 | } |
| 65 | win.float_rect = win.float_rect.map(|r| centre_in(usable, r.w, r.h)); |
| 66 | self.focus(w); |
| 67 | Some(w) |
| 68 | } |
| 69 | |
| 70 | /// Hides a scratchpad, by default the last one summoned. |
| 71 | pub fn dismiss(&mut self, name: Option<&str>) { |
| 72 | let s = match name { |
| 73 | Some(name) => self.scratchpads.iter().position(|sp| sp.name == name), |
| 74 | None => self.last_scratchpad, |
| 75 | }; |
| 76 | if let Some(sp) = s.and_then(|s| self.scratchpads.get_mut(s)) { |
| 77 | sp.shown = None; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | pub(crate) fn leave_scratchpad(&mut self, s: usize, w: WindowId) { |
| 82 | let sp = &mut self.scratchpads[s]; |
| 83 | let Some(i) = sp.windows.iter().position(|&x| x == w) else { |
| 84 | return; |
| 85 | }; |
| 86 | sp.windows.remove(i); |
| 87 | sp.shown = match sp.shown { |
| 88 | Some(j) if j == i => None, |
| 89 | Some(j) if j > i => Some(j - 1), |
| 90 | other => other, |
| 91 | }; |
| 92 | self.windows[w].scratchpad = None; |
| 93 | } |
| 94 | } |