document stuff

This commit is contained in:
griffi-gh 2024-02-20 18:19:10 +01:00
parent 8de7c4673e
commit 9ff9b8356d
4 changed files with 81 additions and 1 deletions

View file

@ -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()"; 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(Rc::clone(self.font_texture.as_ref().expect(NO_FNT_TEX)))
}, },
Some(BindTexture::UserDefined(_)) => todo!("user defined textures are not implemented yet"),
None => None, None => None,
} }
} }

View file

@ -1,3 +1,5 @@
//! Stuff related to tesselation and UI rendering.
use crate::{IfModified, text::{TextRenderer, FontHandle}}; use crate::{IfModified, text::{TextRenderer, FontHandle}};
mod corner_radius; mod corner_radius;
@ -24,6 +26,7 @@ pub enum UiDrawCommand {
///Rounded corners ///Rounded corners
rounded_corners: Option<RoundedCorners>, rounded_corners: Option<RoundedCorners>,
}, },
/// Filled, colored circle
Circle { Circle {
///Position in pixels ///Position in pixels
position: Vec2, position: Vec2,
@ -32,6 +35,7 @@ pub enum UiDrawCommand {
///Color (RGBA) ///Color (RGBA)
color: Vec4, color: Vec4,
}, },
/// Draw text using the specified font, size, color, and position
Text { Text {
///Position in pixels ///Position in pixels
position: Vec2, position: Vec2,
@ -56,12 +60,14 @@ impl UiDrawCommand {
} }
} }
/// List of draw commands
#[derive(Default)] #[derive(Default)]
pub struct UiDrawCommandList { pub struct UiDrawCommandList {
pub commands: Vec<UiDrawCommand>, pub commands: Vec<UiDrawCommand>,
} }
impl UiDrawCommandList { impl UiDrawCommandList {
/// Add a draw command to the list
pub fn add(&mut self, command: UiDrawCommand) { pub fn add(&mut self, command: UiDrawCommand) {
self.commands.push(command); 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)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BindTexture { pub enum BindTexture {
/// The internally managed font texture
FontTexture, 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)] #[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct UiVertex { pub struct UiVertex {
pub position: Vec2, pub position: Vec2,
@ -87,6 +101,7 @@ pub struct UiVertex {
pub uv: Vec2, pub uv: Vec2,
} }
/// Represents a single draw call, should be handled by the render backend
#[derive(Default)] #[derive(Default)]
pub struct UiDrawCall { pub struct UiDrawCall {
pub vertices: Vec<UiVertex>, pub vertices: Vec<UiVertex>,
@ -94,6 +109,7 @@ pub struct UiDrawCall {
pub bind_texture: Option<BindTexture>, pub bind_texture: Option<BindTexture>,
} }
/// Represents a complete UI rendering plan (a list of optimized draw calls).
#[derive(Default)] #[derive(Default)]
pub struct UiDrawPlan { pub struct UiDrawPlan {
pub calls: Vec<UiDrawCall> pub calls: Vec<UiDrawCall>
@ -131,6 +147,7 @@ impl CallSwapper {
} }
impl UiDrawPlan { 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 { pub fn build(draw_commands: &UiDrawCommandList, tr: &mut TextRenderer) -> Self {
let mut swapper = CallSwapper::new(); let mut swapper = CallSwapper::new();
let mut prev_command: Option<&UiDrawCommand> = None; let mut prev_command: Option<&UiDrawCommand> = None;

View file

@ -10,12 +10,14 @@ use crate::{
mod builtin; mod builtin;
pub use builtin::*; pub use builtin::*;
/// Context for the `Element::measure` function
pub struct MeasureContext<'a> { pub struct MeasureContext<'a> {
pub state: &'a StateRepo, pub state: &'a StateRepo,
pub layout: &'a LayoutInfo, pub layout: &'a LayoutInfo,
pub text_measure: TextMeasure<'a>, pub text_measure: TextMeasure<'a>,
} }
/// Context for the `Element::process` function
pub struct ProcessContext<'a> { pub struct ProcessContext<'a> {
pub measure: &'a Response, pub measure: &'a Response,
pub state: &'a mut StateRepo, pub state: &'a mut StateRepo,
@ -25,11 +27,36 @@ pub struct ProcessContext<'a> {
} }
pub trait UiElement { pub trait UiElement {
/// Get the name of the element, for example "Button" or "ProgressBar"
fn name(&self) -> &'static str { "UiElement" } 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<u64> { None } fn state_id(&self) -> Option<u64> { None }
/// Check if the element has state
fn is_stateful(&self) -> bool { self.state_id().is_some() } 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() } 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<Box<dyn Any>> { None } fn init_state(&self) -> Option<Box<dyn Any>> { 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; 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); fn process(&self, ctx: ProcessContext);
} }

View file

@ -9,6 +9,8 @@ use crate:: {
text::{TextRenderer, FontTextureInfo, FontHandle}, 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 { pub struct UiInstance {
//mouse_position: Vec2, //mouse_position: Vec2,
stateful_state: StateRepo, stateful_state: StateRepo,
@ -22,6 +24,9 @@ pub struct UiInstance {
} }
impl 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 { pub fn new() -> Self {
UiInstance { UiInstance {
//mouse_position: Vec2::ZERO, //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 { pub fn add_font_from_bytes(&mut self, font: &[u8]) -> FontHandle {
self.text_renderer.add_font_from_bytes(font) 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<T: UiElement>(&mut self, element: T, max_size: Vec2) { pub fn add<T: UiElement>(&mut self, element: T, max_size: Vec2) {
let layout = LayoutInfo { let layout = LayoutInfo {
position: Vec2::ZERO, 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) { pub fn begin(&mut self) {
std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands); std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
self.draw_plan_modified = false; self.draw_plan_modified = false;
@ -69,6 +83,9 @@ impl UiInstance {
self.text_renderer.reset_frame(); 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) { pub fn end(&mut self) {
if self.draw_commands.commands == self.prev_draw_commands.commands { if self.draw_commands.commands == self.prev_draw_commands.commands {
return return
@ -77,14 +94,32 @@ impl UiInstance {
self.draw_plan_modified = true; 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) { pub fn draw_plan(&self) -> (bool, &UiDrawPlan) {
(self.draw_plan_modified, &self.draw_plan) (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 { pub fn font_texture(&self) -> FontTextureInfo {
self.text_renderer.font_texture() 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) { pub fn push_event(&mut self, event: UiEvent) {
self.events.push_back(event); self.events.push_back(event);
} }