diff --git a/hui-glium/src/lib.rs b/hui-glium/src/lib.rs index 646d9b7..4652b0c 100644 --- a/hui-glium/src/lib.rs +++ b/hui-glium/src/lib.rs @@ -156,6 +156,7 @@ impl GliumUiRenderer { const NO_FNT_TEX: &str = "Font texture exists in draw plan but not yet inited. Make sure to call update_font_texture() *before* update_draw_plan()"; Some(Rc::clone(self.font_texture.as_ref().expect(NO_FNT_TEX))) }, + Some(BindTexture::UserDefined(_)) => todo!("user defined textures are not implemented yet"), None => None, } } diff --git a/hui/src/draw.rs b/hui/src/draw.rs index 5c13283..eebd583 100644 --- a/hui/src/draw.rs +++ b/hui/src/draw.rs @@ -1,3 +1,5 @@ +//! Stuff related to tesselation and UI rendering. + use crate::{IfModified, text::{TextRenderer, FontHandle}}; mod corner_radius; @@ -24,6 +26,7 @@ pub enum UiDrawCommand { ///Rounded corners rounded_corners: Option, }, + /// Filled, colored circle Circle { ///Position in pixels position: Vec2, @@ -32,6 +35,7 @@ pub enum UiDrawCommand { ///Color (RGBA) color: Vec4, }, + /// Draw text using the specified font, size, color, and position Text { ///Position in pixels position: Vec2, @@ -56,12 +60,14 @@ impl UiDrawCommand { } } +/// List of draw commands #[derive(Default)] pub struct UiDrawCommandList { pub commands: Vec, } impl UiDrawCommandList { + /// Add a draw command to the list pub fn add(&mut self, command: UiDrawCommand) { self.commands.push(command); } @@ -74,12 +80,20 @@ impl UiDrawCommandList { // } // } + +/// Texture to bind for a draw call +/// +/// - FontTexture: The internally managed font texture +/// - UserDefined: User-defined texture, value is user-defined and usually depends on the render backend #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BindTexture { + /// The internally managed font texture FontTexture, - //UserDefined(usize), + /// User-defined texture, value is user-defined and usually depends on the render backend + UserDefined(usize), } +/// A vertex for UI rendering #[derive(Clone, Copy, Debug, PartialEq, Default)] pub struct UiVertex { pub position: Vec2, @@ -87,6 +101,7 @@ pub struct UiVertex { pub uv: Vec2, } +/// Represents a single draw call, should be handled by the render backend #[derive(Default)] pub struct UiDrawCall { pub vertices: Vec, @@ -94,6 +109,7 @@ pub struct UiDrawCall { pub bind_texture: Option, } +/// Represents a complete UI rendering plan (a list of optimized draw calls). #[derive(Default)] pub struct UiDrawPlan { pub calls: Vec @@ -131,6 +147,7 @@ impl CallSwapper { } impl UiDrawPlan { + /// Tesselate the UI and build a complete draw plan from a list of draw commands pub fn build(draw_commands: &UiDrawCommandList, tr: &mut TextRenderer) -> Self { let mut swapper = CallSwapper::new(); let mut prev_command: Option<&UiDrawCommand> = None; diff --git a/hui/src/element.rs b/hui/src/element.rs index 03eb2ae..16453e0 100644 --- a/hui/src/element.rs +++ b/hui/src/element.rs @@ -10,12 +10,14 @@ use crate::{ mod builtin; pub use builtin::*; +/// Context for the `Element::measure` function pub struct MeasureContext<'a> { pub state: &'a StateRepo, pub layout: &'a LayoutInfo, pub text_measure: TextMeasure<'a>, } +/// Context for the `Element::process` function pub struct ProcessContext<'a> { pub measure: &'a Response, pub state: &'a mut StateRepo, @@ -25,11 +27,36 @@ pub struct ProcessContext<'a> { } pub trait UiElement { + /// Get the name of the element, for example "Button" or "ProgressBar" fn name(&self) -> &'static str { "UiElement" } + + /// Get the unique id used for internal state management\ + /// This value must be unique for each instance of the element + /// + /// If the element is stateless, this function should return `None` fn state_id(&self) -> Option { None } + + /// Check if the element has state fn is_stateful(&self) -> bool { self.state_id().is_some() } + + /// Check if the element has no state fn is_stateless(&self) -> bool { self.state_id().is_none() } + + /// Initialize the state of the element\ + /// This function should be called exactly once during the lifetime of the element, + /// or if the state gets reset + /// + /// This function will not get called for stateless elements fn init_state(&self) -> Option> { None } + + /// Measure step, guaranteed to be called before the `process` step\ + /// May be called multiple times per single frame, so it should not contain any expensive calls\ + /// This function may not mutate any state.\ + /// + /// This function should return the size of the element along with any hints or layout metadata fn measure(&self, ctx: MeasureContext) -> Response; + + /// Process step, guaranteed to be called after the `measure` step\ + /// You should process the user inputs and render the element here. fn process(&self, ctx: ProcessContext); } diff --git a/hui/src/instance.rs b/hui/src/instance.rs index b072bb5..e502771 100644 --- a/hui/src/instance.rs +++ b/hui/src/instance.rs @@ -9,6 +9,8 @@ use crate:: { text::{TextRenderer, FontTextureInfo, FontHandle}, }; +/// The main instance of the UI system.\ +/// In most cases, you should only have one instance of this struct. pub struct UiInstance { //mouse_position: Vec2, stateful_state: StateRepo, @@ -22,6 +24,9 @@ pub struct UiInstance { } impl UiInstance { + /// Crate and initialize a new instance of the UI + /// + /// In most cases, you should only do this *once*, during the initialization of your application pub fn new() -> Self { UiInstance { //mouse_position: Vec2::ZERO, @@ -38,10 +43,16 @@ impl UiInstance { } } + /// Parse and add a font from a raw byte slice to the UI\ + /// Returns a font handle. pub fn add_font_from_bytes(&mut self, font: &[u8]) -> FontHandle { self.text_renderer.add_font_from_bytes(font) } + /// Add an element or an element tree to the UI + /// + /// Use the `max_size` parameter to specify the maximum size of the element\ + /// (usually, the size of the window/screen) pub fn add(&mut self, element: T, max_size: Vec2) { let layout = LayoutInfo { position: Vec2::ZERO, @@ -62,6 +73,9 @@ impl UiInstance { }); } + /// Prepare the UI for layout and processing + /// + /// You must call this function at the beginning of the frame, before adding any elements pub fn begin(&mut self) { std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands); self.draw_plan_modified = false; @@ -69,6 +83,9 @@ impl UiInstance { self.text_renderer.reset_frame(); } + /// End the frame and prepare the UI for rendering + /// + /// You must call this function at the end of the frame, before rendering the UI pub fn end(&mut self) { if self.draw_commands.commands == self.prev_draw_commands.commands { return @@ -77,14 +94,32 @@ impl UiInstance { self.draw_plan_modified = true; } + /// Get the draw plan (a list of draw calls) for the current frame + /// + /// This function should only be used by the render backend.\ + /// You should not call this directly unless you're implementing a custom render backend + /// + /// Returns a tuple with a boolean indicating if the draw plan was modified since the last frame pub fn draw_plan(&self) -> (bool, &UiDrawPlan) { (self.draw_plan_modified, &self.draw_plan) } + /// Get the font texture for the current frame + /// + /// This function should only be used by the render backend.\ + /// You should not call this directly unless you're implementing a custom render backend + /// + /// Make sure to check `FontTextureInfo::modified` to see if the texture was modified + /// since the last frame before uploading it to the GPU pub fn font_texture(&self) -> FontTextureInfo { self.text_renderer.font_texture() } + /// Push a platform event to the UI event queue + /// + /// This function should only be used by the platform backend.\ + /// You should not call this directly unless you're implementing a custom platform backend + /// or have a very specific usecase pub fn push_event(&mut self, event: UiEvent) { self.events.push_back(event); }