2024-02-20 10:49:44 -06:00
use glam ::Vec2 ;
2024-02-24 21:02:10 -06:00
use crate ::{
draw ::{
2024-03-06 19:04:24 -06:00
atlas ::{ TextureAtlasManager , TextureAtlasMeta } , TextureFormat , ImageHandle , UiDrawCall , UiDrawCommandList
2024-03-06 19:06:14 -06:00
} , element ::{ MeasureContext , ProcessContext , UiElement } , event ::{ EventQueue , UiEvent } , input ::UiInputState , layout ::{ LayoutInfo , Direction } , state ::StateRepo , 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-29 10:57:06 -06:00
events : EventQueue ,
input : UiInputState ,
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
2024-03-06 18:11:53 -06:00
atlas . add_rgba ( 1 , & [ 255 , 255 , 255 , 255 ] ) ;
2024-02-24 21:02:10 -06:00
atlas
} ,
2024-02-29 10:57:06 -06:00
events : EventQueue ::new ( ) ,
input : UiInputState ::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-03-06 18:11:53 -06:00
///
/// ## Panics:
/// If the font data is invalid or corrupt
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-03-06 18:11:53 -06:00
/// Add an image to the texture atlas\
/// Accepted texture formats are `Rgba` and `Grayscale`
///
2024-03-06 19:04:24 -06:00
/// Returns an image handle ([`ImageHandle`])\
2024-03-06 18:11:53 -06:00
/// This handle can be used to reference the texture in draw commands\
/// It's a light reference and can be cloned/copied freely, but will not be cleaned up even when dropped
2024-03-06 19:04:24 -06:00
pub fn add_image ( & mut self , format : TextureFormat , data : & [ u8 ] , width : usize ) -> ImageHandle {
2024-03-06 18:11:53 -06:00
self . atlas . add ( width , data , format )
}
2024-03-01 11:21:02 -06:00
/// Push a font to the font stack\
/// The font will be used for all text rendering until it is popped
///
/// This function is useful for replacing the default font, use sparingly\
/// (This library attempts to be stateless, however passing the font to every text element is not very practical)
pub fn push_font ( & mut self , font : FontHandle ) {
self . text_renderer . push_font ( font ) ;
}
/// Pop a font from the font stack\
///
/// ## Panics:
/// If the font stack is empty
pub fn pop_font ( & mut self ) {
self . text_renderer . pop_font ( ) ;
}
/// Get the current default font
pub fn current_font ( & self ) -> FontHandle {
self . text_renderer . current_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-03-06 18:11:53 -06:00
///
/// ## Panics:
/// If called while the UI is not active (call [`UiInstance::begin`] first)
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 ,
2024-03-06 19:06:14 -06:00
direction : Direction ::Vertical ,
2024-02-20 10:49:44 -06:00
} ;
let measure = element . measure ( MeasureContext {
state : & self . stateful_state ,
layout : & layout ,
text_measure : self . text_renderer . to_measure ( ) ,
2024-03-01 11:21:02 -06:00
current_font : self . text_renderer . current_font ( ) ,
2024-03-06 19:04:24 -06:00
images : self . atlas . context ( ) ,
2024-02-20 10:49:44 -06:00
} ) ;
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-03-01 11:21:02 -06:00
current_font : self . text_renderer . current_font ( ) ,
2024-03-06 19:04:24 -06:00
images : self . atlas . context ( ) ,
2024-02-20 10:49:44 -06:00
} ) ;
}
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-03-06 18:11:53 -06:00
/// ## Panics:
2024-02-27 11:23:55 -06:00
/// 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-29 10:57:06 -06:00
//check and update current state
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-29 10:57:06 -06:00
//first, drain and process the event queue
self . input . update_state ( & mut self . events ) ;
//then, reset the draw commands
2024-02-20 10:49:44 -06:00
std ::mem ::swap ( & mut self . prev_draw_commands , & mut self . draw_commands ) ;
self . draw_commands . commands . clear ( ) ;
2024-02-29 10:57:06 -06:00
self . draw_call_modified = false ;
//reset atlas modification flag
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
///
2024-03-06 18:11:53 -06:00
/// ## Panics:
2024-02-29 10:57:06 -06:00
/// If called without calling [`UiInstance::begin`] first. (or if called twice)\
2024-02-27 11:23:55 -06:00
/// 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-29 10:57:06 -06:00
//check and update current state
2024-02-26 08:19:13 -06:00
assert! ( self . state , " must call UiInstance::begin before calling UiInstance::end " ) ;
self . state = false ;
2024-02-29 10:57:06 -06:00
//check if the draw commands have been modified
2024-02-20 10:49:44 -06:00
if self . draw_commands . commands = = self . prev_draw_commands . commands {
return
}
2024-02-29 10:57:06 -06:00
//if they have, rebuild the draw call and set the modified flag
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-29 10:57:06 -06:00
self . events . push ( event ) ;
2024-02-20 10:49:44 -06:00
}
}
impl Default for UiInstance {
fn default ( ) -> Self {
Self ::new ( )
}
}