diff --git a/hui-examples/examples/modern_testbed.rs b/hui-examples/examples/modern_testbed.rs index 09a3397..94aec6f 100644 --- a/hui-examples/examples/modern_testbed.rs +++ b/hui-examples/examples/modern_testbed.rs @@ -7,7 +7,6 @@ use winit::{ use hui::{ UiInstance, color, size, layout::Alignment, - rectangle::Corners, element::{ container::Container, text::Text, UiElementExt }, diff --git a/hui/src/draw/atlas.rs b/hui/src/draw/atlas.rs index d097e20..ca1e6fa 100644 --- a/hui/src/draw/atlas.rs +++ b/hui/src/draw/atlas.rs @@ -19,6 +19,15 @@ pub struct TextureAtlasMeta<'a> { pub modified: bool, } +/// Texture handle, stores the internal index of a texture within the texture atlas and can be cheaply copied. +/// +/// Only valid for the `UiInstance` that created it.\ +/// Using it with other instances may result in panics or unexpected behavior. +/// +/// Handle values are not guaranteed to be valid.\ +/// Creating or transmuting an invalid handle is allowed and is *not* UB. +/// +/// Internal value is an implementation detail and should not be relied upon. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] pub struct TextureHandle { //pub(crate) rc: Rc<()>, @@ -27,19 +36,14 @@ pub struct TextureHandle { #[derive(Clone, Debug)] pub(crate) struct TextureAllocation { - //pub(crate) rc: Weak<()>, - - /// Unique index of the texture allocation - pub index: u32, - /// Position in the texture atlas - pub(crate) position: UVec2, + pub position: UVec2, /// Requested texture size pub size: UVec2, /// True if the texture was rotated by 90 degrees - pub(crate) rotated: bool, + pub rotated: bool, } /// Manages a texture atlas and the allocation of space within it\ @@ -126,7 +130,6 @@ impl TextureAtlasManager { let index = self.count; self.count += 1; let allocation = TextureAllocation { - index, position: UVec2::new(result.x as u32, result.y as u32), size, //If the size does not match the requested size, the texture was rotated diff --git a/hui/src/element/builtin/text.rs b/hui/src/element/builtin/text.rs index 9c2781c..f088816 100644 --- a/hui/src/element/builtin/text.rs +++ b/hui/src/element/builtin/text.rs @@ -2,7 +2,11 @@ use std::borrow::Cow; use derive_setters::Setters; use glam::{vec2, Vec4}; use crate::{ - draw::UiDrawCommand, element::{MeasureContext, ProcessContext, UiElement}, layout::UiSize, measure::Response, text::{FontHandle, DEFAULT_FONT} + draw::UiDrawCommand, + element::{MeasureContext, ProcessContext, UiElement}, + layout::UiSize, + measure::Response, + text::FontHandle, }; @@ -30,7 +34,7 @@ impl Default for Text { text: "".into(), size: (UiSize::Auto, UiSize::Auto), color: Vec4::new(1., 1., 1., 1.), - font: DEFAULT_FONT, + font: FontHandle::default(), text_size: 16, } } diff --git a/hui/src/instance.rs b/hui/src/instance.rs index 2ef1aff..a58b3da 100644 --- a/hui/src/instance.rs +++ b/hui/src/instance.rs @@ -12,8 +12,10 @@ use crate::{ text::{FontHandle, TextRenderer} }; -/// The main instance of the UI system.\ -/// In most cases, you should only have one instance of this struct. +/// The main instance of the UI system. +/// +/// In most cases, you should only have one instance of this struct, but multiple instances are allowed\ +/// (Please note that it's possible to render multiple UI "roots" using a single instance) pub struct UiInstance { //mouse_position: Vec2, stateful_state: StateRepo, @@ -57,7 +59,9 @@ impl UiInstance { } /// Parse and add a font from a raw byte slice to the UI\ - /// Returns a font handle. + /// TrueType (`.ttf`/`.ttc`) and OpenType (`.otf`) fonts are supported\ + /// + /// Returns a font handle ([`FontHandle`]). pub fn add_font(&mut self, font: &[u8]) -> FontHandle { self.text_renderer.add_font_from_bytes(font) } @@ -87,9 +91,12 @@ impl UiInstance { }); } - /// Prepare the UI for layout and processing + /// Prepare the UI for layout and processing\ + /// You must call this function at the beginning of the frame, before adding any elements\ /// - /// You must call this function at the beginning of the frame, before adding any elements + /// # Panics + /// If called twice in a row (for example, if you forget to call [`UiInstance::end`])\ + /// This is an indication of a bug in your code and should be fixed. pub fn begin(&mut self) { assert!(!self.state, "must call UiInstance::end before calling UiInstance::begin again"); self.state = true; @@ -99,9 +106,12 @@ impl UiInstance { self.atlas.reset_modified(); } - /// End the frame and prepare the UI for rendering - /// + /// End the frame and prepare the UI for rendering\ /// You must call this function at the end of the frame, before rendering the UI + /// + /// # Panics + /// If called without calling [`UiInstance::begin`] first.\ + /// This is an indication of a bug in your code and should be fixed. pub fn end(&mut self) { assert!(self.state, "must call UiInstance::begin before calling UiInstance::end"); self.state = false; @@ -118,6 +128,12 @@ impl UiInstance { /// You should not call this directly unless you're implementing a custom render backend /// /// Returns a tuple with a boolean indicating if the buffers have been modified since the last frame + /// + /// You should only call this function *after* [`UiInstance::end`]\ + /// Calling it in the middle of a frame will result in a warning but will not cause a panic\ + /// (please note that doing so is probably a mistake and should be fixed in your code)\ + /// Doing so anyway will return draw call data for the previous frame, but the `modified` flag will *always* be incorrect until [`UiInstance::end`] is called + /// pub fn draw_call(&self) -> (bool, &UiDrawCall) { if self.state { log::warn!("UiInstance::draw_call called while in the middle of a frame, this is probably a mistake"); @@ -130,6 +146,12 @@ impl UiInstance { /// This function should only be used by the render backend.\ /// You should not call this directly unless you're implementing a custom render backend /// + /// You should only call this function *after* [`UiInstance::end`]\ + /// Calling it in the middle of a frame will result in a warning but will not cause a panic\ + /// (please note that doing so is probably a mistake and should be fixed in your code)\ + /// Using this function in the middle of a frame will return partially modified atlas data that may be outdated or incomplete\ + /// This will lead to rendering artifacts, 1-frame delays and flashes and is probably not what you want + /// /// Make sure to check [`TextureAtlasMeta::modified`] to see if the texture has been modified /// since the beginning of the current frame before uploading it to the GPU pub fn atlas(&self) -> TextureAtlasMeta { @@ -141,6 +163,12 @@ impl UiInstance { /// Push a platform event to the UI event queue /// + /// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\ + /// Calling it in the middle of a frame will result in a warning but will not cause a panic\ + /// (please note that doing so is probably a mistake and should be fixed in your code)\ + /// In this case, the event will be processed in the next frame, but in some cases may affect the current frame.\ + /// (The exact behavior is not guaranteed and you should avoid doing this if possible) + /// /// 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 diff --git a/hui/src/text.rs b/hui/src/text.rs index a65ee1b..d5ac06e 100644 --- a/hui/src/text.rs +++ b/hui/src/text.rs @@ -7,10 +7,9 @@ use font::FontManager; pub use font::FontHandle; #[cfg(feature="builtin_font")] pub use font::BUILTIN_FONT; -pub(crate) use font::DEFAULT_FONT; use fontdue::{Font, FontSettings}; use ftm::FontTextureManager; -pub use ftm::GlyphCacheEntry; +use ftm::GlyphCacheEntry; use crate::draw::atlas::TextureAtlasManager; diff --git a/hui/src/text/font.rs b/hui/src/text/font.rs index 093f408..e9e89e5 100644 --- a/hui/src/text/font.rs +++ b/hui/src/text/font.rs @@ -1,15 +1,28 @@ use fontdue::Font; +/// Font handle, stores the internal font id and can be cheaply copied. +/// +/// Only valid for the `UiInstance` that created it.\ +/// Using it with other instances may result in panics or unexpected behavior. +/// +/// Handle values are not guaranteed to be valid.\ +/// Creating or transmuting an invalid handle is allowed and is *not* UB. +/// +/// Internal value is an implementation detail and should not be relied upon. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct FontHandle(pub(crate) usize); #[cfg(feature = "builtin_font")] pub const BUILTIN_FONT: FontHandle = FontHandle(0); -pub(crate) const DEFAULT_FONT: FontHandle = { - #[cfg(feature = "builtin_font")] { BUILTIN_FONT } - #[cfg(not(feature = "builtin_font"))] { FontHandle(usize::MAX) } -}; +impl Default for FontHandle { + /// Default font handle is the builtin font, if the feature is enabled;\ + /// Otherwise returns an invalid handle. + fn default() -> Self { + #[cfg(feature = "builtin_font")] { BUILTIN_FONT } + #[cfg(not(feature = "builtin_font"))] { Self(usize::MAX) } + } +} #[cfg(feature = "builtin_font")] const BUILTIN_FONT_DATA: &[u8] = include_bytes!("../../assets/font/ProggyTiny.ttf");