Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/11-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ With the scene's surface (the one with no name), what is repeated is the loose d

Its loose `prop`s and `fact`s, though, are **one for all the copies**: they are not drawing. So what a `follow` follows must not depend on which copy it is —`follow a = screen.index` ends up as the last copy's in every one—; say which monitor shows something in its `show:` instead, which is drawing and is read per copy.

**A named surface beside the copies** (one with its own `screens:`, such as a text naming the monitor a panel was opened on) reads `screen.index`, `screen.width` and `screen.height` from **the copy on its monitor**, and so do the loose `let`s it names: `let mine = if(panel_on == screen.index, 1, 0)` is 1 there when the copy it sits over is the panel's, as it is in that copy. On a monitor without a copy, `screen.index` is -1. So a panel the copies all open can be drawn once, on a surface of its own that follows the monitor, instead of once in every copy.

```
surface { size: full, 44; anchor: top; screens: each }
model mon max 4 { active: number = 1; title: text } // one record per monitor, from the logic
Expand Down
63 changes: 62 additions & 1 deletion src/language/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ struct Scope {
/// The strings with holes, already resolved where they were written: `Chip("{n.app}")`.
contents: HashMap<String, Content>,
alias: HashMap<String, String>,
/// Inside a named surface beside the copies of `screens: each`: the mark its own
/// reading of a loose `let` is stored with (`crosses#on.panel`), as a copy's is with its suffix.
let_mark: String,
/// Of those names, the ones that have parts: `label.width` belongs to the measure `label`.
with_parts: std::collections::HashSet<String>,
suffix: String,
Expand Down Expand Up @@ -1683,7 +1686,13 @@ impl<'a> Compiler<'a> {
let n = &self.global(n);
// Inside a screen copy, a `let` that was read once per
// copy is stored with its mark: that is its own.
let marked = self.scopes.iter().rev().find(|e| e.suffix.starts_with("#screen")).map(|e| format!("{n}{}", e.suffix)).filter(|m| self.lets.contains_key(m));
let marked = self
.scopes
.iter()
.rev()
.find_map(|e| if e.suffix.starts_with("#screen") { Some(&e.suffix) } else if !e.let_mark.is_empty() { Some(&e.let_mark) } else { None })
.map(|mark| format!("{n}{mark}"))
.filter(|m| self.lets.contains_key(m));
let n = match &marked {
Some(m) => m,
None => n,
Expand Down Expand Up @@ -2844,6 +2853,19 @@ impl<'a> Compiler<'a> {
self.lets.insert(format!("{name}#screen{k}"), own);
}
}
// And once for each named surface beside them, with what its monitor's copy holds.
for surface in self.beside_copies() {
let env = self.own_screen_scope(&surface);
let mark = env.let_mark.clone();
self.scopes.push(Rc::new(env));
let mut c2 = Cur::new(&n.head[3..], n.line, n.col);
let own = self.expr(&mut c2);
self.scopes.pop();
if let Ok(own) = own {
let own = if own.node_count() > 8 { self.computed_once(&name, own).e() } else { own };
self.lets.insert(format!("{name}{mark}"), own);
}
}
}
}
"zone" => {
Expand Down Expand Up @@ -4679,6 +4701,12 @@ impl<'a> Compiler<'a> {
env.alias.insert(format!("screen.{part}"), format!("{name}.{part}"));
}
Some(env)
} else if self.beside_copies().contains(&name) {
// Beside the copies of `screens: each`, a named surface reads its
// `screen.index`, `.width` and `.height` from the copy on ITS monitor
// (see `own_screen_scope`).
let env = self.own_screen_scope(&name);
Some(env)
} else {
None
};
Expand Down Expand Up @@ -5073,6 +5101,39 @@ impl<'a> Compiler<'a> {
props.iter().any(|p| self.copy_props.contains(p))
}

/// The named surfaces of a scene copied per monitor (not a lock, not a window):
/// each is one surface that goes to the monitors its `screens:` says.
fn beside_copies(&self) -> Vec<String> {
if !self.e.surfaces.iter().any(|s| s.name.is_empty() && matches!(s.screens, Screens::Number(_))) {
return Vec::new();
}
let mut out: Vec<String> = Vec::new();
for s in &self.e.surfaces {
if !s.name.is_empty() && !matches!(s.screens, Screens::Number(_)) && !s.lock_screen && s.window.is_none() && !out.contains(&s.name) {
out.push(s.name.clone());
}
}
out
}

/// Inside one of those: `screen.index`, `.width` and `.height` are the copy's on
/// its monitor, which the render keeps in `NAME.screen.*` (index -1 on a monitor
/// without a copy). They were the plain `screen.*`, which is copy 0's: a panel sent
/// to the second monitor measured the first, and every `if(panel_on == screen.index, …)`
/// it shared with the copies said no.
fn own_screen_scope(&mut self, name: &str) -> Scope {
let mut env = Scope { let_mark: format!("#on.{name}"), ..Default::default() };
for part in ["index", "width", "height"] {
let full_name = format!("{name}.screen.{part}");
if !self.facts.contains_key(&full_name) {
let h = self.e.fact(interned(&full_name), if part == "index" { -1.0 } else { 0.0 });
self.facts.insert(full_name.clone(), h);
}
env.alias.insert(format!("screen.{part}"), full_name);
}
env
}

/// Inside a `screens: each` surface: what holds for that copy.
fn screen_scope(&self, k: usize) -> Scope {
let mut env = Scope { suffix: format!("#screen{k}"), ..Default::default() };
Expand Down
62 changes: 62 additions & 0 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ pub fn run(

// ── state ────────────────────────────────────────────────────
let mut scene = Scene::default();
let mut own_screens = OwnScreens::default();
let mut props: Vec<Animated> = Vec::new();
let mut facts: Vec<f32> = Vec::new();
let mut inside: Vec<bool> = Vec::new();
Expand Down Expand Up @@ -995,6 +996,7 @@ pub fn run(
fresh.gestures.len(), fresh.rules.len(), fresh.zones.len()
);
scene = fresh;
own_screens = OwnScreens::of(&scene);
z_memo = None;
window_instrs = None;
draw.hidden.clear();
Expand Down Expand Up @@ -2873,6 +2875,7 @@ pub fn run(
// And the copies of a `screens: each` surface for monitors that are not
// there: `each max 4` with one monitor was four times the scene's rules
// every frame, for three copies nobody sees. The first copy always runs.
own_screens.update(&sheets, &texts, &mut facts);
let asleep: Vec<&crate::scene::Span> = {
let c = Ctx { props: &props, facts: &facts, seen: None };
let shown = |k: usize| sheets.iter().any(|l| l.view.surface == k);
Expand Down Expand Up @@ -5812,6 +5815,65 @@ fn error_banner(message: &str, width: f32) -> Vec<Instr> {
/// not one for each surface: the scene's named ones sit on the same monitor as its main
/// one and are not another monitor. The lock's are left out: they go without a
/// `SheetGone`, and their name is the monitor's with " (lock)" after it.
/// A named surface beside the copies of `screens: each` reads `NAME.screen.index`,
/// `.width` and `.height` for its `screen.*` (see the compiler): those of the copy on
/// the monitor it is shown on, which moves when its `screens:` text does. On a monitor
/// without a copy, index -1 (it is none of them) and the scene's `screen.width` and
/// `.height`. Where those names are is found once per scene, not every round.
#[derive(Default)]
struct OwnScreens {
/// Each such surface: its number, and its three facts (index, width, height).
surfaces: Vec<(usize, [usize; 3])>,
/// Each copy: its `screen.K.name` text and its width and height facts.
copies: Vec<(usize, Option<usize>, Option<usize>)>,
/// The scene's own `screen.width` and `screen.height`.
plain: (Option<usize>, Option<usize>),
}

impl OwnScreens {
fn of(scene: &Scene) -> Self {
let fact = |n: &str| scene.facts.iter().position(|h| h.0 == n);
let surfaces = scene
.surfaces
.iter()
.enumerate()
.filter(|(_, s)| !s.name.is_empty() && !matches!(s.screens, Screens::Number(_)) && !s.lock_screen && s.window.is_none())
.filter_map(|(k, s)| {
let [i, w, h] = ["index", "width", "height"].map(|part| fact(&format!("{}.screen.{part}", s.name)));
Some((k, [i?, w?, h?]))
})
.collect::<Vec<_>>();
if surfaces.is_empty() {
return Self::default();
}
let copies = (0..8)
.map_while(|c| {
let t = scene.texts.iter().position(|t| t.0 == format!("screen.{c}.name"))?;
Some((t, fact(&format!("screen.{c}.width")), fact(&format!("screen.{c}.height"))))
})
.collect();
Self { surfaces, copies, plain: (fact("screen.width"), fact("screen.height")) }
}

fn update(&self, sheets: &[Sheet], texts: &[String], facts: &mut [f32]) {
for &(k, [fi, fw, fh]) in &self.surfaces {
let Some(monitor) = sheets.iter().find(|l| l.view.surface == k && l.view.popup.is_none()).map(|l| l.name.as_str()) else { continue };
let copy = self.copies.iter().position(|(t, _, _)| texts.get(*t).is_some_and(|v| v == monitor));
let (index, (w, h)) = match copy {
Some(c) => (c as f32, (self.copies[c].1, self.copies[c].2)),
None => (-1.0, self.plain),
};
facts[fi] = index;
if let Some(v) = w.and_then(|i| facts.get(i).copied()) {
facts[fw] = v;
}
if let Some(v) = h.and_then(|i| facts.get(i).copied()) {
facts[fh] = v;
}
}
}
}

fn screens_showing(sheets: &[Sheet], surfaces: &[Surface]) -> usize {
sheets
.iter()
Expand Down
24 changes: 24 additions & 0 deletions src/scenes/from_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,30 @@ mod tests {
assert_eq!(b, vec![1050.0, 1150.0]);
}

#[test]
fn a_named_surface_beside_the_copies_reads_the_copy_on_its_monitor() {
// A panel of its own (`screens: on`) sent to the second monitor said copy 0's
// `screen.index` and `screen.width`, so a `let` it shared with the copies
// —"the panel is on this copy"— was false there.
let set = set_by_rules("scene Beside {
surface { size: full, 40; anchor: top; screens: each max 2 }
text on = \"B\"
fact panel_on = 1
let mine = if(panel_on == screen.index, 1, 0)
surface sheet {
size: 200, 200; anchor: top; screens: on
on demo { a = mine; b = screen.width; c = screen.index }
}
fact a = 0
fact b = 0
fact c = 0
}
", &[("screen.0.width", 100.0), ("screen.1.width", 300.0), ("sheet.screen.index", 1.0), ("sheet.screen.width", 300.0)]);
assert_eq!(set["a"], vec![1.0]);
assert_eq!(set["b"], vec![300.0]);
assert_eq!(set["c"], vec![1.0]);
}

#[test]
fn each_copy_of_a_surface_measures_its_own_monitor() {
// A phone's monitor of 442 came last and said `bar.width` for every copy: the bar on
Expand Down
Loading