diff --git a/src/bin/main.rs b/src/bin/main.rs index f39ef474..3fcc9ce5 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -102,7 +102,7 @@ fn main() -> Result<()> { // TODO: [Threads, Panic] Make this close all the child threads too! panic::set_hook(Box::new(|info| panic_hook(info))); - tuice::launch_with_application(app, receiver); + tuice::launch_with_application(app, receiver, &mut terminal)?; // FIXME: Move terminal construction INSIDE // I think doing it in this order is safe... *thread_termination_lock.lock().unwrap() = true; diff --git a/src/tuice/application.rs b/src/tuice/application.rs index 6fd2e34b..540c8a3c 100644 --- a/src/tuice/application.rs +++ b/src/tuice/application.rs @@ -1,5 +1,7 @@ use std::{fmt::Debug, sync::mpsc::Receiver}; +use tui::Terminal; + use super::{ runtime::{self, RuntimeEvent}, Element, Event, @@ -32,9 +34,13 @@ pub trait Application: Sized { fn global_event_handler(&mut self, event: Event, messages: &mut Vec) {} } -/// Launches some application with tuice. -pub fn launch_with_application( - application: A, receiver: Receiver>, -) { - runtime::launch(application, receiver); +/// Launches some application with tuice. Note this will take over the calling thread. +pub fn launch_with_application( + application: A, receiver: Receiver>, terminal: &mut Terminal, +) -> anyhow::Result<()> +where + A: Application + 'static, + B: tui::backend::Backend, +{ + runtime::launch(application, receiver, terminal) } diff --git a/src/tuice/component/base/flex.rs b/src/tuice/component/base/flex.rs index 4faa2757..fc7c8844 100644 --- a/src/tuice/component/base/flex.rs +++ b/src/tuice/component/base/flex.rs @@ -49,7 +49,7 @@ impl<'a, Message> FlexElement<'a, Message> { } pub(crate) fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size { - todo!() + self.element.layout(bounds, node) } } diff --git a/src/tuice/context.rs b/src/tuice/context.rs index 43540e44..f40742e3 100644 --- a/src/tuice/context.rs +++ b/src/tuice/context.rs @@ -8,7 +8,13 @@ pub struct DrawContext<'a> { } impl<'a> DrawContext<'_> { - pub(crate) fn new() {} + /// Creates a new [`DrawContext`], with the offset set to `(0, 0)`. + pub(crate) fn root(root: &'a LayoutNode) -> DrawContext<'a> { + DrawContext { + current_node: root, + current_offset: (0, 0), + } + } pub(crate) fn rect(&self) -> Rect { self.current_node.rect diff --git a/src/tuice/layout/build_layout.rs b/src/tuice/layout/build_layout.rs index 241bfd9e..d9fdddae 100644 --- a/src/tuice/layout/build_layout.rs +++ b/src/tuice/layout/build_layout.rs @@ -2,13 +2,13 @@ use tui::layout::Rect; use crate::tuice::{Bounds, Element, LayoutNode, TmpComponent}; -pub fn build_layout_tree(area: Rect, root: &Element<'_, Message>) -> LayoutNode { - let mut root_layout_node = LayoutNode::from_rect(area); +pub fn build_layout_tree(rect: Rect, root: &Element<'_, Message>) -> LayoutNode { + let mut root_layout_node = LayoutNode::from_rect(rect); let bounds = Bounds { min_width: 0, min_height: 0, - max_width: area.width, - max_height: area.height, + max_width: rect.width, + max_height: rect.height, }; let _ = root.layout(bounds, &mut root_layout_node); diff --git a/src/tuice/runtime.rs b/src/tuice/runtime.rs index e5d1048b..1666c21d 100644 --- a/src/tuice/runtime.rs +++ b/src/tuice/runtime.rs @@ -1,10 +1,10 @@ use std::sync::mpsc::Receiver; -use tui::layout::Rect; +use tui::{backend::Backend, layout::Rect, Terminal}; use crate::tuice::Status; -use super::{Application, Event, TmpComponent}; +use super::{build_layout_tree, Application, Element, Event, TmpComponent}; #[derive(Clone, Copy, Debug)] pub enum RuntimeEvent { @@ -13,10 +13,15 @@ pub enum RuntimeEvent { Custom(Message), } -pub(crate) fn launch( - mut application: A, receiver: Receiver>, -) { +pub(crate) fn launch( + mut application: A, receiver: Receiver>, terminal: &mut Terminal, +) -> anyhow::Result<()> +where + A: Application + 'static, + B: Backend, +{ let mut user_interface = application.view(); + draw(&mut user_interface, terminal)?; while !application.is_terminated() { if let Ok(event) = receiver.recv() { @@ -38,7 +43,7 @@ pub(crate) fn launch( } user_interface = application.view(); - // FIXME: Draw! + draw(&mut user_interface, terminal)?; } RuntimeEvent::Custom(message) => { application.update(message); @@ -48,6 +53,8 @@ pub(crate) fn launch( height: _, } => { user_interface = application.view(); + // FIXME: Also nuke any cache and the like... + draw(&mut user_interface, terminal)?; } } } else { @@ -56,4 +63,21 @@ pub(crate) fn launch( } application.destroy(); + + Ok(()) +} + +fn draw(user_interface: &mut Element<'_, M>, terminal: &mut Terminal) -> anyhow::Result<()> +where + B: Backend, +{ + terminal.draw(|frame| { + let rect = frame.size(); + let layout = build_layout_tree(rect, &user_interface); + let context = super::DrawContext::root(&layout); + + user_interface.draw(context, frame); + })?; + + Ok(()) }