Per-skärm-inställningar med skärmväljare, kant-i-kant-marginaler
All checks were successful
check / check (push) Successful in 1m39s

- outputs{} är nu generiska JSON-överridar: vilket configfält som helst
  kan sättas per skärm och deep-mergas över den globala configen
  (Config::for_output). Docka, autohide, dodge, beteende, hotspots och
  CSS (scopat på window.o-<connector>) läser skärmens effektiva config.
- Settings har skärmväljare överst: klickbar förhandsgranskning av
  skärmarna + deras barer, 'Alla skärmar' (standard) eller enskild
  skärm. Bara ändrade fält sparas som override (json-diff);
  återställ-knapp tar bort skärmens överridar. * = har egna.
- Marginaler: skuggutrymmet läggs inte längre på förankrade sidor så
  0 = kant i kant; minusvärden tillåts (A ner till -64, B/C -256).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 00:25:05 +02:00
parent 04d999642c
commit 558c178ec4
8 changed files with 609 additions and 167 deletions

View File

@@ -109,10 +109,25 @@ thread_local! {
/// Bygg om alla hotspot-ytor enligt configen (körs vid start + configbyte).
/// Idempotent: oförändrad hotspot-config + skärmuppsättning → ingen ombyggnad.
pub fn rebuild(ctx: &Rc<Ctx>) {
let n_monitors = gdk::Display::default()
.map(|d| d.monitors().n_items())
.unwrap_or(0);
let signature = format!("{:?}|{n_monitors}", ctx.cfg.borrow().hotspots);
let display = gdk::Display::default().expect("ingen display");
let monitors = display.monitors();
// signatur = alla skärmars EFFEKTIVA hotspot-config
let mut signature = String::new();
let mut mons: Vec<(String, gdk::Monitor)> = Vec::new();
for i in 0..monitors.n_items() {
let Some(monitor) = monitors.item(i).and_then(|o| o.downcast::<gdk::Monitor>().ok())
else {
continue;
};
let connector = monitor
.connector()
.map(|c| c.to_string())
.unwrap_or_else(|| format!("monitor-{i}"));
let eff = ctx.cfg.borrow().for_output(&connector);
signature += &format!("{connector}:{:?}|", eff.hotspots);
mons.push((connector, monitor));
}
let unchanged = LAST_SIGNATURE.with(|s| *s.borrow() == signature);
if unchanged {
return;
@@ -123,41 +138,33 @@ pub fn rebuild(ctx: &Rc<Ctx>) {
win.destroy();
}
let cfg = ctx.cfg.borrow();
let dwell = cfg.hotspots.dwell_ms;
let c = cfg.hotspots.corners.clone();
let e = cfg.hotspots.edges.clone();
drop(cfg);
let corner_defs: [(&Option<HotspotAction>, [Edge; 2]); 4] = [
(&c.top_left, [Edge::Top, Edge::Left]),
(&c.top_right, [Edge::Top, Edge::Right]),
(&c.bottom_left, [Edge::Bottom, Edge::Left]),
(&c.bottom_right, [Edge::Bottom, Edge::Right]),
];
// kanterna lämnar hörnen fria (hörn-ytor ligger ovanpå ändå — overlay
// med senare mappade ytor hamnar överst, så det kvittar i praktiken)
#[allow(clippy::type_complexity)]
let edge_defs: [(&Option<HotspotAction>, [Edge; 3], (i32, i32)); 4] = [
(&e.top, [Edge::Top, Edge::Left, Edge::Right], (-1, EDGE_PX)),
(&e.bottom, [Edge::Bottom, Edge::Left, Edge::Right], (-1, EDGE_PX)),
(&e.left, [Edge::Left, Edge::Top, Edge::Bottom], (EDGE_PX, -1)),
(&e.right, [Edge::Right, Edge::Top, Edge::Bottom], (EDGE_PX, -1)),
];
let display = gdk::Display::default().expect("ingen display");
let monitors = display.monitors();
let mut windows = Vec::new();
for i in 0..monitors.n_items() {
let Some(monitor) = monitors.item(i).and_then(|o| o.downcast::<gdk::Monitor>().ok())
else {
continue;
};
for (connector, monitor) in &mons {
let h = ctx.cfg.borrow().for_output(connector).hotspots;
let dwell = h.dwell_ms;
let c = h.corners.clone();
let e = h.edges.clone();
let corner_defs: [(&Option<HotspotAction>, [Edge; 2]); 4] = [
(&c.top_left, [Edge::Top, Edge::Left]),
(&c.top_right, [Edge::Top, Edge::Right]),
(&c.bottom_left, [Edge::Bottom, Edge::Left]),
(&c.bottom_right, [Edge::Bottom, Edge::Right]),
];
// kanterna lämnar hörnen fria (hörn-ytor ligger ovanpå ändå)
#[allow(clippy::type_complexity)]
let edge_defs: [(&Option<HotspotAction>, [Edge; 3], (i32, i32)); 4] = [
(&e.top, [Edge::Top, Edge::Left, Edge::Right], (-1, EDGE_PX)),
(&e.bottom, [Edge::Bottom, Edge::Left, Edge::Right], (-1, EDGE_PX)),
(&e.left, [Edge::Left, Edge::Top, Edge::Bottom], (EDGE_PX, -1)),
(&e.right, [Edge::Right, Edge::Top, Edge::Bottom], (EDGE_PX, -1)),
];
for (action, edges) in &corner_defs {
if let Some(a) = action {
windows.push(build_hotspot(
ctx,
&monitor,
monitor,
edges,
(CORNER_PX, CORNER_PX),
a.clone(),
@@ -167,7 +174,7 @@ pub fn rebuild(ctx: &Rc<Ctx>) {
}
for (action, edges, size) in &edge_defs {
if let Some(a) = action {
windows.push(build_hotspot(ctx, &monitor, edges, *size, a.clone(), dwell));
windows.push(build_hotspot(ctx, monitor, edges, *size, a.clone(), dwell));
}
}
}