arch: split ConfigRepository, extract polling, consolidate conversions, decouple protocol
- Value↔JSON: From impls on domain Value behind `json` feature, delete 4 duplicate converters - ConfigRepository split into ConfigRepository (12), UserRepository (3), WidgetStateCache (2) - polling orchestration moved from bootstrap to application::polling_service - WidgetRenderer in client-domain owns scroll/cache, both clients use it - network loop consolidated into client-application::run_connection_loop - protocol crate drops domain dep, Wire↔Domain conversions move to adapters
This commit is contained in:
@@ -10,6 +10,7 @@ mod render_tree;
|
||||
mod scroll;
|
||||
mod text_layout;
|
||||
mod theme;
|
||||
mod widget_renderer;
|
||||
|
||||
pub use alignment::align_offset;
|
||||
pub use bounding_box::BoundingBox;
|
||||
@@ -24,3 +25,4 @@ pub use render_tree::RenderTree;
|
||||
pub use scroll::ScrollState;
|
||||
pub use text_layout::wrap_lines;
|
||||
pub use theme::ThemeConfig;
|
||||
pub use widget_renderer::{RenderUpdate, RepaintRequest, WidgetRenderer};
|
||||
|
||||
113
crates/client-domain/src/widget_renderer.rs
Normal file
113
crates/client-domain/src/widget_renderer.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::{BoundingBox, DrawCommand, RenderEngine, ScrollState};
|
||||
use domain::{DisplayHint, Value, WidgetError};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct RenderUpdate {
|
||||
pub bounds: BoundingBox,
|
||||
pub commands: Vec<DrawCommand>,
|
||||
}
|
||||
|
||||
struct WidgetCache {
|
||||
hint: DisplayHint,
|
||||
data: Vec<(String, Value)>,
|
||||
error: Option<WidgetError>,
|
||||
bounds: BoundingBox,
|
||||
scroll: ScrollState,
|
||||
}
|
||||
|
||||
pub struct RepaintRequest {
|
||||
pub widget_id: u16,
|
||||
pub bounds: BoundingBox,
|
||||
pub display_hint: DisplayHint,
|
||||
pub data: Vec<(String, Value)>,
|
||||
pub error: Option<WidgetError>,
|
||||
}
|
||||
|
||||
pub struct WidgetRenderer {
|
||||
widgets: HashMap<u16, WidgetCache>,
|
||||
}
|
||||
|
||||
impl Default for WidgetRenderer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRenderer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
widgets: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_repaints(
|
||||
&mut self,
|
||||
engine: &RenderEngine,
|
||||
repaints: Vec<RepaintRequest>,
|
||||
) -> Vec<RenderUpdate> {
|
||||
let mut updates = Vec::new();
|
||||
for req in repaints {
|
||||
let content_h = engine.content_height(
|
||||
&req.display_hint,
|
||||
&req.data,
|
||||
req.bounds.width,
|
||||
req.error.as_ref(),
|
||||
);
|
||||
let scroll = ScrollState::new(req.bounds.height, content_h);
|
||||
|
||||
let cmds = engine.render_widget(
|
||||
&req.display_hint,
|
||||
&req.data,
|
||||
req.bounds,
|
||||
scroll.offset(),
|
||||
req.error.as_ref(),
|
||||
);
|
||||
|
||||
updates.push(RenderUpdate {
|
||||
bounds: req.bounds,
|
||||
commands: cmds,
|
||||
});
|
||||
|
||||
self.widgets.insert(
|
||||
req.widget_id,
|
||||
WidgetCache {
|
||||
hint: req.display_hint,
|
||||
data: req.data,
|
||||
error: req.error,
|
||||
bounds: req.bounds,
|
||||
scroll,
|
||||
},
|
||||
);
|
||||
}
|
||||
updates
|
||||
}
|
||||
|
||||
pub fn tick_scroll(&mut self, engine: &RenderEngine, elapsed: Duration) -> Vec<RenderUpdate> {
|
||||
let mut updates = Vec::new();
|
||||
for cache in self.widgets.values_mut() {
|
||||
if cache.scroll.tick(elapsed) {
|
||||
let cmds = engine.render_widget(
|
||||
&cache.hint,
|
||||
&cache.data,
|
||||
cache.bounds,
|
||||
cache.scroll.offset(),
|
||||
cache.error.as_ref(),
|
||||
);
|
||||
updates.push(RenderUpdate {
|
||||
bounds: cache.bounds,
|
||||
commands: cmds,
|
||||
});
|
||||
}
|
||||
}
|
||||
updates
|
||||
}
|
||||
|
||||
pub fn has_active_scrollers(&self) -> bool {
|
||||
self.widgets.values().any(|c| c.scroll.is_active())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.widgets.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user