Per-skärm-inställningar med skärmväljare, kant-i-kant-marginaler
All checks were successful
check / check (push) Successful in 1m39s
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:
@@ -197,13 +197,10 @@ impl Default for Behavior {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct OutputConfig {
|
||||
pub enabled: Option<bool>,
|
||||
pub only_this_output: Option<bool>,
|
||||
pub pins: Option<Vec<String>>,
|
||||
}
|
||||
// Per-skärm-överridar: en partiell Config i JSON-form som deep-mergas
|
||||
// över den globala. Extranycklarna "enabled" och "only_this_output" är
|
||||
// skärm-specifika och läses separat (de finns inte i Config).
|
||||
pub type OutputOverride = serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
@@ -316,7 +313,7 @@ pub struct Config {
|
||||
pub context_menu: Vec<MenuItem>,
|
||||
pub pins: Vec<String>,
|
||||
pub special_buttons: SpecialButtons,
|
||||
pub outputs: HashMap<String, OutputConfig>,
|
||||
pub outputs: HashMap<String, OutputOverride>,
|
||||
pub autohide: Autohide,
|
||||
pub hotspots: Hotspots,
|
||||
pub log: LogConfig,
|
||||
@@ -356,26 +353,94 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deep-merge: värden i `patch` skrivs in i `base` (objekt rekursivt,
|
||||
/// allt annat ersätts).
|
||||
pub fn json_merge(base: &mut serde_json::Value, patch: &serde_json::Value) {
|
||||
if let (Some(base_map), Some(patch_map)) = (base.as_object_mut(), patch.as_object()) {
|
||||
for (k, v) in patch_map {
|
||||
match base_map.get_mut(k) {
|
||||
Some(slot) if slot.is_object() && v.is_object() => json_merge(slot, v),
|
||||
_ => {
|
||||
base_map.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*base = patch.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal diff: objektet med bara de löv som skiljer `after` från `before`.
|
||||
/// Null om inget skiljer.
|
||||
pub fn json_diff(before: &serde_json::Value, after: &serde_json::Value) -> serde_json::Value {
|
||||
use serde_json::Value;
|
||||
if before == after {
|
||||
return Value::Null;
|
||||
}
|
||||
if let (Some(b), Some(a)) = (before.as_object(), after.as_object()) {
|
||||
let mut out = serde_json::Map::new();
|
||||
for (k, av) in a {
|
||||
match b.get(k) {
|
||||
Some(bv) => {
|
||||
let d = json_diff(bv, av);
|
||||
if !d.is_null() {
|
||||
out.insert(k.clone(), d);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
out.insert(k.clone(), av.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
if out.is_empty() {
|
||||
Value::Null
|
||||
} else {
|
||||
Value::Object(out)
|
||||
}
|
||||
} else {
|
||||
after.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Effektiva pins för en viss skärm (output-överstyrning eller globala).
|
||||
pub fn pins_for(&self, output: &str) -> &[String] {
|
||||
self.outputs
|
||||
.get(output)
|
||||
.and_then(|o| o.pins.as_deref())
|
||||
.unwrap_or(&self.pins)
|
||||
/// Effektiv config för en skärm: global + per-skärm-överridar mergade.
|
||||
pub fn for_output(&self, output: &str) -> Config {
|
||||
match self.outputs.get(output) {
|
||||
None => self.clone(),
|
||||
Some(over) => {
|
||||
let mut base = serde_json::to_value(self).expect("config serialiserbar");
|
||||
json_merge(&mut base, over);
|
||||
match serde_json::from_value::<Config>(base) {
|
||||
Ok(mut cfg) => {
|
||||
// undvik rekursiva överridar
|
||||
cfg.outputs = self.outputs.clone();
|
||||
cfg
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("trasig override för {output} ({e}) — kör globalt");
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Effektiva pins för en viss skärm.
|
||||
pub fn pins_for(&self, output: &str) -> Vec<String> {
|
||||
self.for_output(output).pins
|
||||
}
|
||||
|
||||
pub fn output_enabled(&self, output: &str) -> bool {
|
||||
self.outputs
|
||||
.get(output)
|
||||
.and_then(|o| o.enabled)
|
||||
.and_then(|o| o["enabled"].as_bool())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
pub fn only_this_output(&self, output: &str) -> bool {
|
||||
self.outputs
|
||||
.get(output)
|
||||
.and_then(|o| o.only_this_output)
|
||||
.and_then(|o| o["only_this_output"].as_bool())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user