tawasuyu

tawasuyu/brahman

Rust 321TOML 122JSON 14Markdown 114332 Definiciones

⎇ 53dbdf0f1deea10d6fc567ea2b43e1e75d6d60cd crates/apps/yahweh-shell/src/layout_model.rs Rust · 140 líneas · 4.1 KB Historia Quién tocó qué crudo

chore: monorepo inicial con arje + minga + yahweh absorbidos 53dbdf0f1 · Sergio ·

1//! `LayoutModel` — fuente de verdad mutable del árbol de layout.
2//!
3//! Distinguimos dos clases de cambio:
4//!
5//! - [`LayoutModelEvent::StructureChanged`]: cambios que requieren rebuild
6//!   del árbol de entidades (kind, children, params relevantes). Estos
7//!   también invocan `cx.notify()` para que los `cx.observe` (StatusPanel,
8//!   etc.) refresquen.
9//! - [`LayoutModelEvent::FlexChanged`]: actualización de `flex` de un
10//!   nodo (típicamente proviene de un drag de divisor). NO requiere
11//!   rebuild — el SplitContainer ya tiene el flex aplicado en su Vec; solo
12//!   nos importa para persistir. Por eso no llamamos `cx.notify()`: solo
13//!   emitimos el evento, así los `cx.observe` (que rebuilden) se mantienen
14//!   silenciosos durante el drag.
15//!
16//! El `Persister` (ver `shell/persister.rs`) se subscribe vía
17//! `cx.subscribe` y reacciona a los dos.
18
19use gpui::{Context, EventEmitter};
20
21use yahweh_core::{LayerConfig, NodeId};
22
23#[derive(Clone, Debug)]
24pub enum LayoutModelEvent {
25    /// Estructural — kind / children / params. Triggerea rebuild en
26    /// `LayoutHost` y persist.
27    StructureChanged,
28    /// Solo flex de un nodo. Triggerea persist; NO rebuild.
29    FlexChanged,
30}
31
32pub struct LayoutModel {
33    tree: LayerConfig,
34}
35
36impl EventEmitter<LayoutModelEvent> for LayoutModel {}
37
38impl LayoutModel {
39    pub fn new(tree: LayerConfig) -> Self {
40        Self { tree }
41    }
42
43    pub fn tree(&self) -> &LayerConfig {
44        &self.tree
45    }
46
47    /// Reemplazo completo del árbol — para hot-reload del JSON.
48    pub fn replace_tree(&mut self, tree: LayerConfig, cx: &mut Context<Self>) {
49        self.tree = tree;
50        cx.emit(LayoutModelEvent::StructureChanged);
51        cx.notify();
52    }
53
54    /// Cambia el `kind` del nodo cuyo id JSON coincide con `target_id`.
55    pub fn set_kind(
56        &mut self,
57        target_id: &NodeId,
58        new_kind: &str,
59        cx: &mut Context<Self>,
60    ) {
61        let changed = mutate_node(&mut self.tree, target_id, &mut |node| {
62            if node.kind != new_kind {
63                node.kind = new_kind.to_string();
64                true
65            } else {
66                false
67            }
68        });
69        if changed {
70            cx.emit(LayoutModelEvent::StructureChanged);
71            cx.notify();
72        }
73    }
74
75    /// Setea el flex de un nodo. Solo emite `FlexChanged` (no notify) —
76    /// usado al final de un drag de divisor para persistir sin
77    /// triggerear rebuild.
78    pub fn set_flex(&mut self, target_id: &NodeId, flex: f32, cx: &mut Context<Self>) {
79        let new_val = Some(flex as f64);
80        let changed = mutate_node(&mut self.tree, target_id, &mut |node| {
81            if node.flex != new_val {
82                node.flex = new_val;
83                true
84            } else {
85                false
86            }
87        });
88        if changed {
89            cx.emit(LayoutModelEvent::FlexChanged);
90        }
91    }
92
93    /// Intercambia dos children del nodo `parent_id`. Triggerea
94    /// `StructureChanged` (rebuild + persist), porque cambia el orden de
95    /// instanciación. Si los índices son iguales o están out-of-bounds,
96    /// es no-op.
97    pub fn swap_children(
98        &mut self,
99        parent_id: &NodeId,
100        idx_a: usize,
101        idx_b: usize,
102        cx: &mut Context<Self>,
103    ) {
104        if idx_a == idx_b {
105            return;
106        }
107        let mut did_swap = false;
108        mutate_node(&mut self.tree, parent_id, &mut |node| {
109            if idx_a < node.children.len() && idx_b < node.children.len() {
110                node.children.swap(idx_a, idx_b);
111                did_swap = true;
112                true
113            } else {
114                false
115            }
116        });
117        if did_swap {
118            cx.emit(LayoutModelEvent::StructureChanged);
119            cx.notify();
120        }
121    }
122}
123
124fn mutate_node(
125    node: &mut LayerConfig,
126    target: &NodeId,
127    f: &mut impl FnMut(&mut LayerConfig) -> bool,
128) -> bool {
129    if let Some(id) = &node.id {
130        if id == target.as_str() {
131            return f(node);
132        }
133    }
134    for child in node.children.iter_mut() {
135        if mutate_node(child, target, f) {
136            return true;
137        }
138    }
139    false
140}