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 ( )
}
}