Skip to main content

flux_tui/window/
handler.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use crossterm::event::Event;
6use singlevec::SingleVec;
7
8use crate::canvas::*;
9use crate::common::*;
10use crate::framebuffer::Framebuffer;
11use crate::window::*;
12
13#[derive(Clone)]
14struct WindowStructure {
15	rect: Rect,
16	has_border: bool,
17	depth: u8,
18	border: Thickness,
19	render_inner: bool,
20	weak: WindowWeakRef,
21	parent: Option<WindowUID>,
22	children: SingleVec<WindowUID>,
23}
24
25impl WindowStructure {
26	fn new(
27		ctx: &WindowRef,
28		rect: Rect,
29		has_border: bool,
30		border: Thickness,
31		depth: u8,
32		parent: Option<WindowUID>,
33	) -> Self {
34		Self {
35			rect,
36			has_border,
37			border,
38			depth,
39			render_inner: false,
40			weak: Rc::downgrade(ctx),
41			children: SingleVec::new(),
42			parent,
43		}
44	}
45
46	fn subtract_border(&self, rect: &mut Rect) {
47		rect.start.x += self.border.left;
48		rect.size.x -= self.border.width();
49		rect.start.y += self.border.top;
50		rect.size.y -= self.border.height();
51	}
52
53	fn inner_rect(&self) -> Rect {
54		let mut rect = self.rect;
55		if self.has_border {
56			self.subtract_border(&mut rect);
57		}
58		rect
59	}
60}
61
62
63struct Focus {
64	win: WindowRef,
65	//UID of the corresponding window
66	uid: WindowUID,
67}
68
69impl PartialEq for Focus {
70	fn eq(&self, other: &Self) -> bool {
71		self.uid == other.uid
72	}
73}
74
75struct SizeCache {
76	size: TPoint,
77	map: RefCell<HashMap<WindowUID, TPoint>>,
78}
79
80impl SizeCache {
81	fn new(size: TPoint) -> Self {
82		Self {
83			size,
84			map: RefCell::new(HashMap::with_capacity(128)),
85		}
86	}
87
88	fn resize(&mut self, size: TPoint) {
89		self.size = size;
90	}
91
92	// Returns true if the value changed
93	fn compare_n_update(&self, uid: WindowUID, size: TPoint) -> bool {
94		let mut brw = self.map.borrow_mut();
95		let old = brw
96			.insert(uid, size)
97			.unwrap_or(Vector2D::new(TSize::MIN, TSize::MIN));
98		old != size
99	}
100}
101
102
103pub struct WindowHandler {
104	structure: HashMap<WindowUID, WindowStructure>,
105	main_window: Rc<RefCell<dyn Window>>,
106	focus: Focus,
107	size_cache: SizeCache,
108	queue: WindowEventQueue,
109}
110
111impl WindowHandler {
112	pub(crate) fn new(main_window: WindowRef, size: TPoint) -> Self {
113		let focus = Self::find_focus(&main_window);
114		focus
115			.win
116			.borrow_mut()
117			.handle_event(&mut WindowEvent::new(Event::FocusGained));
118		let queue = WindowEventQueue::initialize();
119		// Make sure all windows initially get their inner size
120		let mut window_handler = Self {
121			main_window,
122			focus,
123			structure: HashMap::with_capacity(128),
124			size_cache: SizeCache::new(size),
125			queue,
126		};
127		window_handler.broadcast_resize(size);
128		window_handler
129	}
130
131	pub(crate) fn handle_event(&mut self, event: Event) {
132		let mut win_event = WindowEvent::new(event);
133		self.handle_event_inner(self.focus.uid, &mut win_event);
134	}
135
136	pub(crate) fn broadcast_resize(&mut self, size: TPoint) {
137		self.size_cache.resize(size);
138		let uid = Self::determine_window_structure(
139			&mut self.structure,
140			Rect::from(size.x, size.y),
141			&self.main_window,
142			u8::MIN,
143			None,
144		);
145		self.broadcast_resize_rec(uid);
146		self.structure
147			.retain(|_, s| s.weak.strong_count() > usize::MIN);
148	}
149
150	pub(crate) fn render(&mut self, framebuffer: &mut Framebuffer) {
151		self.handle_events();
152		let uid = self.main_window.borrow().uid();
153		self.render_window_rec(&mut Canvas::from_framebuffer(framebuffer), uid);
154	}
155
156	fn broadcast_resize_rec(&self, win: WindowUID) {
157		let struc = self.structure.get(&win).unwrap();
158		let rect = struc.inner_rect();
159		if self.size_cache.compare_n_update(win, rect.size) {
160			struc
161				.weak
162				.upgrade()
163				.unwrap()
164				.borrow_mut()
165				.handle_event(&mut WindowEvent::new(Event::Resize(
166					rect.size.x as TSize,
167					rect.size.y as TSize,
168				)));
169		}
170		for w in struc.children.iter() {
171			self.broadcast_resize_rec(*w);
172		}
173	}
174
175	fn get_parent_rect(&self, ws: &WindowStructure) -> Rect {
176		if let Some(uid) = ws.parent {
177			if let Some(parent) = self.structure.get(&uid) {
178				let inner = parent.inner_rect();
179				return Rect::from(inner.size.x, inner.size.y);
180			}
181		}
182		Rect::from(self.size_cache.size.x, self.size_cache.size.y)
183	}
184
185	pub(crate) fn handle_events(&mut self) {
186		let mut window: Option<TreeUpdate> = None;
187		let mut focus: Option<FocusUpdate> = None;
188		for e in self.queue.recv.try_iter() {
189			match e.property {
190				WindowProperty::Focus => {
191					if let Some(w) = self.structure.get(&e.uid) {
192						match focus.as_ref() {
193							Some(current) => {
194								if w.depth < current.depth {
195									focus = w.weak.upgrade().map(|v| FocusUpdate {
196										win: v,
197										depth: w.depth,
198									});
199								}
200							}
201							None => {
202								focus = w.weak.upgrade().map(|v| FocusUpdate {
203									win: v,
204									depth: w.depth,
205								})
206							}
207						}
208					}
209				}
210				WindowProperty::Children => {
211					if let Some(w) = self.structure.get(&e.uid) {
212						match window.as_ref() {
213							Some(current) => {
214								if w.depth < current.depth {
215									window = w.weak.upgrade().map(|v| TreeUpdate {
216										win: v,
217										depth: w.depth,
218										rect: w.rect,
219									});
220								}
221							}
222							None => {
223								window = w.weak.upgrade().map(|v| TreeUpdate {
224									win: v,
225									depth: w.depth,
226									rect: w.rect,
227								})
228							}
229						}
230					}
231				}
232				_ => {
233					// When an window changes its IsVisible Property update the hierarchy from the
234					// parent because its children have to be reevaluated
235					if let Some(w) = self.structure.get(&e.uid) {
236						if let Some(p) = w.parent {
237							match self.structure.get(&p) {
238								Some(parent) => match window.as_ref() {
239									Some(current) => {
240										if parent.depth < current.depth {
241											window = parent.weak.upgrade().map(|v| TreeUpdate {
242												win: v,
243												depth: parent.depth,
244												rect: self.get_parent_rect(parent),
245											});
246										}
247									}
248									None => {
249										window = parent.weak.upgrade().map(|v| TreeUpdate {
250											win: v,
251											depth: parent.depth,
252											rect: self.get_parent_rect(parent),
253										});
254									}
255								},
256								None => {
257									window = Some(TreeUpdate {
258										win: self.main_window.clone(),
259										depth: u8::MIN,
260										rect: self.get_parent_rect(w),
261									})
262								}
263							}
264						}
265					}
266				}
267			}
268		}
269
270		if let Some(update) = window.as_ref() {
271			let parent = {
272				match self.structure.get(&update.win.borrow().uid()) {
273					Some(ws) => ws.parent,
274					None => None,
275				}
276			};
277
278			Self::determine_window_structure(
279				&mut self.structure,
280				update.rect,
281				&update.win,
282				update.depth,
283				parent,
284			);
285		}
286
287		if let Some(update) = focus.as_ref() {
288			let focus = Self::find_focus(&update.win);
289			// Only change focus when the window is actually visible
290			if self.focus != focus && self.structure.contains_key(&focus.uid) {
291				self.focus
292					.win
293					.borrow_mut()
294					.handle_event(&mut WindowEvent::new(Event::FocusLost));
295				self.focus = focus;
296				self.focus
297					.win
298					.borrow_mut()
299					.handle_event(&mut WindowEvent::new(Event::FocusGained));
300			}
301		}
302
303		if window.is_some() {
304			self.structure
305				.retain(|_, s| s.weak.strong_count() > usize::MIN);
306
307			let uid = self.main_window.borrow().uid();
308			self.broadcast_resize_rec(uid);
309		}
310	}
311
312	fn determine_window_structure(
313		map: &mut HashMap<WindowUID, WindowStructure>,
314		base_rect: Rect,
315		window: &WindowRef,
316		depth: u8,
317		parent: Option<WindowUID>,
318	) -> WindowUID {
319		let mut rect = base_rect;
320		let has_border = Self::determine_window_rect(&mut rect, &mut *window.borrow_mut());
321		// Always save the size including the border
322		let mut ws = WindowStructure::new(
323			window,
324			rect,
325			has_border,
326			window.borrow().border().thickness(),
327			depth,
328			parent,
329		);
330		let uid = window.borrow().uid();
331		let inner_rect = ws.inner_rect();
332
333		if inner_rect.area() > TSize::MIN {
334			ws.render_inner = true;
335			let child_rect = Rect::from(inner_rect.size.x, inner_rect.size.y);
336			let sub_wins = window
337				.borrow_mut()
338				.children(SubWindowBuilder::from(child_rect));
339			for (sub, child_rect) in sub_wins
340				.children()
341				.iter()
342				.zip(sub_wins.rects())
343				.filter(|(sub, _)| sub.borrow().is_visible())
344			{
345				let ch_uid =
346					Self::determine_window_structure(map, *child_rect, sub, depth + 1, Some(uid));
347				ws.children.push(ch_uid);
348			}
349		}
350
351		map.insert(uid, ws);
352		uid
353	}
354
355	// Returns has_border, returns None if desired size cant be fulfilled
356	fn determine_window_rect(base_rect: &mut Rect, window: &mut dyn Window) -> bool {
357		let (h_align, v_align) = window.alignment();
358		let margin = window.margin();
359		let tn = window.border().thickness();
360		let (w, h) = (tn.width(), tn.height());
361
362		window.desired_size_pre_hook(Vector2D::new(base_rect.size.x, base_rect.size.y));
363		let mut size = window.desired_size(Vector2D::new(base_rect.size.x, base_rect.size.y));
364		let has_border = window.border() != BorderStyle::None;
365		// Make sure desired_size doesn't exceed base_rect's size
366		if size.x > base_rect.size.x || size.y > base_rect.size.y {
367			size = Vector2D::default();
368		}
369		// Preserve border
370		if size.x * size.y > 0 {
371			if size.x <= w && size.x + w <= base_rect.size.x {
372				size.x += w;
373			}
374			if size.y <= h && size.y + h <= base_rect.size.y {
375				size.y += h;
376			}
377		}
378
379		Self::handle_margin(
380			base_rect,
381			margin.left,
382			margin.top,
383			margin.right,
384			margin.bottom,
385		);
386		Self::handle_alignment(base_rect, 0, h_align.into(), size.x);
387		Self::handle_alignment(base_rect, 1, v_align.into(), size.y);
388		has_border && base_rect.size.x > w && base_rect.size.y > h
389	}
390
391	fn find_focus(window: &WindowRef) -> Focus {
392		let win_brw = window.borrow();
393		match win_brw.focus() {
394			Some(sub) => Self::find_focus(&sub),
395			None => Focus {
396				win: window.clone(),
397				uid: win_brw.uid(),
398			},
399		}
400	}
401
402	fn handle_event_inner(&self, win: WindowUID, event: &mut WindowEvent) {
403		if let Some(ws) = self.structure.get(&win) {
404			if let Some(window) = ws.weak.upgrade() {
405				if window.borrow().is_enabled() {
406					window.borrow_mut().handle_event(event);
407				}
408				if !event.handled {
409					if let Some(parent) = ws.parent {
410						self.handle_event_inner(parent, event);
411					}
412				}
413			}
414		}
415	}
416
417	fn render_window_rec(&self, canvas: &mut Canvas, win: WindowUID) {
418		let ws = self.structure.get(&win).unwrap();
419		if let Some(ctx) = ws.weak.upgrade() {
420			let brw = ctx.borrow();
421			let mut rect = ws.rect;
422			if ws.has_border {
423				Self::render_border(&*brw, &mut Canvas::from_existing(canvas, rect).unwrap())
424					.unwrap();
425				ws.subtract_border(&mut rect);
426			}
427
428			if ws.render_inner {
429				let mut inner_canvas = Canvas::from_existing(canvas, rect).unwrap();
430				brw.render(&mut inner_canvas);
431				for sub in ws.children.iter() {
432					self.render_window_rec(&mut Canvas::from_existing(canvas, rect).unwrap(), *sub);
433				}
434			}
435		}
436	}
437
438	fn handle_margin(rect: &mut Rect, left: TSize, top: TSize, right: TSize, bottom: TSize) {
439		if rect.size.x > left {
440			rect.start.x += left;
441			rect.size.x -= left;
442		}
443		if rect.size.x > right {
444			rect.size.x -= right;
445		}
446		if rect.size.y > top {
447			rect.start.y += top;
448			rect.size.y -= top;
449		}
450		if rect.size.y > bottom {
451			rect.size.y -= bottom;
452		}
453	}
454
455	fn handle_alignment(
456		rect: &mut Rect,
457		rect_index: usize,
458		alignment: Alignment,
459		base_size: TSize,
460	) {
461		if let Some(size) = rect.size[rect_index].checked_sub(base_size) {
462			match alignment {
463				Alignment::LowerBound => {
464					rect.size[rect_index] -= size;
465				}
466				Alignment::Center => {
467					let rem = size % 2;
468					if size + rem <= rect.size[rect_index] {
469						rect.start[rect_index] += size / 2 + rem;
470						rect.size[rect_index] -= size;
471					}
472				}
473				Alignment::HigherBound => {
474					rect.start[rect_index] += size;
475				}
476			}
477		}
478	}
479
480	fn render_border(window: &dyn Window, canvas: &mut Canvas) -> Result<(), GraphemeError> {
481		let border = window.border();
482		let tn = border.thickness();
483		let mut bcanvas = BorderCanvas::from(canvas, tn);
484		match border {
485			BorderStyle::Custom(f, _) => f(window, bcanvas),
486			BorderStyle::Preset { kind, bg, fg } => bcanvas.draw_preset_border(kind, bg, fg, "")?,
487			BorderStyle::None => {}
488		}
489
490		Ok(())
491	}
492}