hUI/hui/src/instance.rs

188 lines
7.5 KiB
Rust
Raw Normal View History

2024-02-20 10:49:44 -06:00
use std::collections::VecDeque;
use glam::Vec2;
2024-02-24 21:02:10 -06:00
use crate::{
draw::{
atlas::{TextureAtlasManager, TextureAtlasMeta},
UiDrawCall, UiDrawCommandList,
},
2024-02-20 10:49:44 -06:00
element::{MeasureContext, ProcessContext, UiElement},
event::UiEvent,
2024-02-24 21:02:10 -06:00
layout::{LayoutInfo, UiDirection},
2024-02-20 10:49:44 -06:00
state::StateRepo,
2024-02-24 21:02:10 -06:00
text::{FontHandle, TextRenderer}
2024-02-20 10:49:44 -06:00
};
2024-02-27 11:23:55 -06:00
/// 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)
2024-02-20 10:49:44 -06:00
pub struct UiInstance {
//mouse_position: Vec2,
stateful_state: StateRepo,
//event_queue: VecDeque<UiEvent>,
prev_draw_commands: UiDrawCommandList,
draw_commands: UiDrawCommandList,
2024-02-21 13:13:58 -06:00
draw_call: UiDrawCall,
draw_call_modified: bool,
2024-02-20 10:49:44 -06:00
text_renderer: TextRenderer,
2024-02-24 21:02:10 -06:00
atlas: TextureAtlasManager,
2024-02-20 10:49:44 -06:00
events: VecDeque<UiEvent>,
2024-02-26 08:19:13 -06:00
//True if in the middle of a laying out a frame
state: bool,
2024-02-20 10:49:44 -06:00
}
impl UiInstance {
2024-02-20 11:19:10 -06:00
/// Crate and initialize a new instance of the UI
///
/// In most cases, you should only do this *once*, during the initialization of your application
2024-02-20 10:49:44 -06:00
pub fn new() -> Self {
UiInstance {
//mouse_position: Vec2::ZERO,
stateful_state: StateRepo::default(),
//event_queue: VecDeque::new(),
// root_elements: Vec::new(),
prev_draw_commands: UiDrawCommandList::default(),
draw_commands: UiDrawCommandList::default(),
2024-02-21 13:13:58 -06:00
draw_call: UiDrawCall::default(),
draw_call_modified: false,
2024-02-20 10:49:44 -06:00
// ftm: FontTextureManager::default(),
text_renderer: TextRenderer::new(),
2024-02-24 21:02:10 -06:00
atlas: {
let mut atlas = TextureAtlasManager::default();
//HACK: Ensure that vec(0, 0) uv is white square
atlas.add_grayscale(1, &[255]);
atlas
},
2024-02-20 10:49:44 -06:00
events: VecDeque::new(),
2024-02-26 08:19:13 -06:00
state: false,
2024-02-20 10:49:44 -06:00
}
}
2024-02-20 11:19:10 -06:00
/// Parse and add a font from a raw byte slice to the UI\
2024-02-27 11:23:55 -06:00
/// TrueType (`.ttf`/`.ttc`) and OpenType (`.otf`) fonts are supported\
///
/// Returns a font handle ([`FontHandle`]).
2024-02-24 21:02:10 -06:00
pub fn add_font(&mut self, font: &[u8]) -> FontHandle {
2024-02-20 10:49:44 -06:00
self.text_renderer.add_font_from_bytes(font)
}
2024-02-20 11:19:10 -06:00
/// 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)
2024-02-20 10:49:44 -06:00
pub fn add<T: UiElement>(&mut self, element: T, max_size: Vec2) {
2024-02-26 08:19:13 -06:00
assert!(self.state, "must call UiInstance::begin before adding elements");
2024-02-20 10:49:44 -06:00
let layout = LayoutInfo {
position: Vec2::ZERO,
max_size,
direction: UiDirection::Vertical,
};
let measure = element.measure(MeasureContext {
state: &self.stateful_state,
layout: &layout,
text_measure: self.text_renderer.to_measure(),
});
element.process(ProcessContext {
measure: &measure,
state: &mut self.stateful_state,
layout: &layout,
draw: &mut self.draw_commands,
text_measure: self.text_renderer.to_measure(),
});
}
2024-02-27 11:23:55 -06:00
/// Prepare the UI for layout and processing\
/// You must call this function at the beginning of the frame, before adding any elements\
2024-02-20 11:19:10 -06:00
///
2024-02-27 11:23:55 -06:00
/// # 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.
2024-02-20 10:49:44 -06:00
pub fn begin(&mut self) {
2024-02-26 08:19:13 -06:00
assert!(!self.state, "must call UiInstance::end before calling UiInstance::begin again");
self.state = true;
2024-02-20 10:49:44 -06:00
std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
2024-02-21 13:13:58 -06:00
self.draw_call_modified = false;
2024-02-20 10:49:44 -06:00
self.draw_commands.commands.clear();
2024-02-24 21:02:10 -06:00
self.atlas.reset_modified();
2024-02-20 10:49:44 -06:00
}
2024-02-27 11:23:55 -06:00
/// End the frame and prepare the UI for rendering\
2024-02-20 11:19:10 -06:00
/// You must call this function at the end of the frame, before rendering the UI
2024-02-27 11:23:55 -06:00
///
/// # Panics
/// If called without calling [`UiInstance::begin`] first.\
/// This is an indication of a bug in your code and should be fixed.
2024-02-20 10:49:44 -06:00
pub fn end(&mut self) {
2024-02-26 08:19:13 -06:00
assert!(self.state, "must call UiInstance::begin before calling UiInstance::end");
self.state = false;
2024-02-20 10:49:44 -06:00
if self.draw_commands.commands == self.prev_draw_commands.commands {
return
}
2024-02-24 21:02:10 -06:00
self.draw_call = UiDrawCall::build(&self.draw_commands, &mut self.atlas, &mut self.text_renderer);
2024-02-21 13:13:58 -06:00
self.draw_call_modified = true;
2024-02-20 10:49:44 -06:00
}
2024-02-24 21:02:10 -06:00
/// Get the draw call information for the current frame
2024-02-20 11:19:10 -06:00
///
/// This function should only be used by the render backend.\
/// You should not call this directly unless you're implementing a custom render backend
///
2024-02-24 21:02:10 -06:00
/// Returns a tuple with a boolean indicating if the buffers have been modified since the last frame
2024-02-27 11:23:55 -06:00
///
/// 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
///
2024-02-21 13:13:58 -06:00
pub fn draw_call(&self) -> (bool, &UiDrawCall) {
2024-02-26 08:19:13 -06:00
if self.state {
log::warn!("UiInstance::draw_call called while in the middle of a frame, this is probably a mistake");
}
2024-02-21 13:13:58 -06:00
(self.draw_call_modified, &self.draw_call)
2024-02-20 10:49:44 -06:00
}
2024-02-24 21:02:10 -06:00
/// Get the texture atlas size and data for the current frame
2024-02-20 11:19:10 -06:00
///
/// This function should only be used by the render backend.\
/// You should not call this directly unless you're implementing a custom render backend
///
2024-02-27 11:23:55 -06:00
/// 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
///
2024-02-24 21:02:10 -06:00
/// 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 {
2024-02-26 08:19:13 -06:00
if self.state {
log::warn!("UiInstance::atlas called while in the middle of a frame, this is probably a mistake");
}
2024-02-24 21:02:10 -06:00
self.atlas.meta()
2024-02-20 10:49:44 -06:00
}
2024-02-20 11:19:10 -06:00
/// Push a platform event to the UI event queue
///
2024-02-27 11:23:55 -06:00
/// 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)
///
2024-02-20 11:19:10 -06:00
/// 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
2024-02-20 10:49:44 -06:00
pub fn push_event(&mut self, event: UiEvent) {
2024-02-26 08:19:13 -06:00
if self.state {
log::warn!("UiInstance::push_event called while in the middle of a frame, this is probably a mistake");
}
2024-02-20 10:49:44 -06:00
self.events.push_back(event);
}
}
impl Default for UiInstance {
fn default() -> Self {
Self::new()
}
}