- 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
114 lines
2.9 KiB
Rust
114 lines
2.9 KiB
Rust
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();
|
|
}
|
|
}
|