1use std::thread::JoinHandle;
2
3use crate::element::{ElementDecl, Value};
4use crate::protocol::ServerMsg;
5use crate::server;
6use crate::state::{self, Shared};
7use crate::window::Window;
8
9pub struct Context {
10 shared: Shared,
11 prev_frame: Vec<ElementDecl>,
12 current_frame: Vec<ElementDecl>,
13 http_port: u16,
14 ws_port: u16,
15 _http_handle: JoinHandle<()>,
16 _ws_handle: JoinHandle<()>,
17}
18
19impl Context {
20 pub fn new() -> Self {
23 Self::with_port(9080)
24 }
25
26 pub fn with_port(start_port: u16) -> Self {
28 let (http_port, ws_port) = server::find_port_pair(start_port);
29 let shared = state::new_shared();
30
31 let http_handle = server::spawn_http(shared.clone(), http_port);
32 let ws_handle = server::spawn_ws(shared.clone(), ws_port);
33
34 println!("wgui: UI available at http://127.0.0.1:{http_port}");
35
36 Self {
37 shared,
38 prev_frame: Vec::new(),
39 current_frame: Vec::new(),
40 http_port,
41 ws_port,
42 _http_handle: http_handle,
43 _ws_handle: ws_handle,
44 }
45 }
46
47 pub fn http_port(&self) -> u16 {
49 self.http_port
50 }
51
52 pub fn ws_port(&self) -> u16 {
54 self.ws_port
55 }
56
57 pub fn window(&mut self, name: &str) -> Window<'_> {
59 Window::new(name.to_string(), self)
60 }
61
62 pub(crate) fn consume_edit(&mut self, id: &str) -> Option<Value> {
64 let mut state = self.shared.lock().unwrap();
65 state.incoming_edits.remove(id)
66 }
67
68 pub(crate) fn declare(&mut self, decl: ElementDecl) {
70 self.current_frame.push(decl);
71 }
72
73 pub(crate) fn current_frame_len(&self) -> usize {
75 self.current_frame.len()
76 }
77
78 pub fn end_frame(&mut self) {
80 let mut outgoing = Vec::new();
81
82 for decl in &self.current_frame {
84 let prev = self.prev_frame.iter().find(|p| p.id == decl.id);
85 match prev {
86 None => {
87 outgoing.push(ServerMsg::Add {
89 element: decl.clone(),
90 });
91 }
92 Some(prev_decl) => {
93 let value_changed = prev_decl.value != decl.value || prev_decl.kind != decl.kind;
95 let meta_changed = prev_decl.meta != decl.meta;
96 if value_changed || meta_changed {
97 outgoing.push(ServerMsg::Update {
98 id: decl.id.clone(),
99 value: decl.value.clone(),
100 meta: if meta_changed { Some(decl.meta.clone()) } else { None },
101 });
102 }
103 }
104 }
105 }
106
107 for prev_decl in &self.prev_frame {
109 if !self.current_frame.iter().any(|d| d.id == prev_decl.id) {
110 outgoing.push(ServerMsg::Remove {
111 id: prev_decl.id.clone(),
112 });
113 }
114 }
115
116 {
118 let mut state = self.shared.lock().unwrap();
119 state.outgoing_msgs.extend(outgoing);
120 state.current_elements = self.current_frame.clone();
121 }
122
123 self.prev_frame = std::mem::take(&mut self.current_frame);
125 }
126}
127
128impl Drop for Context {
129 fn drop(&mut self) {
130 let mut state = self.shared.lock().unwrap();
131 state.shutdown = true;
132 }
133}