mirror of
https://github.com/griffi-gh/hUI.git
synced 2025-04-01 13:36:30 -05:00
wip rewrite/restructure
This commit is contained in:
parent
13503dbcb0
commit
b49f0c2dba
hui-examples
hui-glium/src
hui-painter
hui
|
@ -60,7 +60,7 @@ pub fn ui<T>(
|
|||
let mut frame = display.draw();
|
||||
frame.clear_color_srgb(0.5, 0.5, 0.5, 1.);
|
||||
|
||||
hui.begin();
|
||||
hui.begin_frame();
|
||||
|
||||
let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||
draw(&mut hui, size, &mut result);
|
||||
|
|
|
@ -36,7 +36,7 @@ fn main() {
|
|||
|
||||
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||
|
||||
hui.begin();
|
||||
hui.begin_frame();
|
||||
|
||||
let z = instant.elapsed().as_secs_f32().sin().powi(2);
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ fn main() {
|
|||
|
||||
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||
|
||||
hui.begin();
|
||||
hui.begin_frame();
|
||||
|
||||
hui.add(Container {
|
||||
size: (Size::Relative(1.), Size::Relative(1.)).into(),
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
use std::rc::Rc;
|
||||
use glam::Vec2;
|
||||
use glium::{
|
||||
backend::{Context, Facade}, implement_vertex, index::PrimitiveType, texture::{RawImage2d, Texture2d}, uniform, uniforms::{MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction}, Api, Blend, DrawParameters, IndexBuffer, Program, Surface, VertexBuffer
|
||||
};
|
||||
use hui::{
|
||||
draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
|
||||
backend::{Context, Facade},
|
||||
index::PrimitiveType,
|
||||
texture::{RawImage2d, Texture2d},
|
||||
uniforms::{
|
||||
MagnifySamplerFilter,
|
||||
MinifySamplerFilter,
|
||||
Sampler,
|
||||
SamplerBehavior,
|
||||
SamplerWrapFunction
|
||||
},
|
||||
Api,
|
||||
Blend,
|
||||
DrawParameters,
|
||||
IndexBuffer,
|
||||
Program,
|
||||
Surface,
|
||||
VertexBuffer,
|
||||
implement_vertex,
|
||||
uniform,
|
||||
};
|
||||
use hui::UiInstance;
|
||||
|
||||
const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert");
|
||||
const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag");
|
||||
|
|
|
@ -8,7 +8,7 @@ rust-version = "1.85"
|
|||
version = "0.1.0-alpha.6"
|
||||
edition = "2024"
|
||||
license = "GPL-3.0-or-later"
|
||||
publish = false # TODO: change to true once ready
|
||||
publish = true
|
||||
include = [
|
||||
"src/**/*.rs",
|
||||
"Cargo.toml",
|
||||
|
|
|
@ -1,3 +1,114 @@
|
|||
// pub struct BackendData<'a> {
|
||||
// pub
|
||||
// }
|
||||
use crate::{
|
||||
paint::{buffer::PaintBuffer, command::{PaintCommand, PaintRoot}},
|
||||
texture::TextureAtlasBackendData,
|
||||
PainterInstance,
|
||||
};
|
||||
|
||||
pub struct Presentatation {
|
||||
current_buffer: PaintBuffer,
|
||||
cur_hash: Option<u64>,
|
||||
prev_hash: Option<u64>,
|
||||
version_counter: u64,
|
||||
}
|
||||
|
||||
impl Presentatation {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_buffer: PaintBuffer::new(),
|
||||
cur_hash: None,
|
||||
prev_hash: None,
|
||||
version_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// If the paint command has changed since the last draw call, draw it and return true.\
|
||||
/// Otherwise, returns false.
|
||||
pub fn draw(&mut self, painter: &mut PainterInstance, cmd: &impl PaintRoot) -> bool {
|
||||
self.prev_hash = self.cur_hash;
|
||||
self.cur_hash = Some(cmd.cache_hash());
|
||||
|
||||
if self.prev_hash == self.cur_hash {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.current_buffer.clear();
|
||||
cmd.paint_root(painter, &mut self.current_buffer);
|
||||
|
||||
self.version_counter = self.version_counter.wrapping_add(1);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Get the current paint buffer
|
||||
pub fn buffer(&self) -> &PaintBuffer {
|
||||
&self.current_buffer
|
||||
}
|
||||
|
||||
/// Get the complete backend data for the current presentation
|
||||
///
|
||||
/// It contains the current paint buffer and the hash of the presentation\
|
||||
/// Unlike the `TextureAtlasBackendData`, the version is non-incremental
|
||||
pub fn backend_data(&self) -> PresentatationBackendData {
|
||||
PresentatationBackendData {
|
||||
buffer: &self.current_buffer,
|
||||
version: self.version_counter,
|
||||
hash: self.cur_hash.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Presentatation {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Backend data for the Presentation
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PresentatationBackendData<'a> {
|
||||
/// The current paint buffer
|
||||
pub buffer: &'a PaintBuffer,
|
||||
|
||||
/// The version of the presentation
|
||||
///
|
||||
/// This is incremented every time the buffer hash changes
|
||||
pub version: u64,
|
||||
|
||||
/// Unique hash of current paint buffer commands
|
||||
pub hash: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct BackendData<'a> {
|
||||
pub presentation: PresentatationBackendData<'a>,
|
||||
pub atlas: TextureAtlasBackendData<'a>,
|
||||
}
|
||||
|
||||
impl PainterInstance {
|
||||
pub fn backend_data<'a>(&'a self, presentation: &'a Presentatation) -> BackendData<'a> {
|
||||
BackendData {
|
||||
presentation: presentation.backend_data(),
|
||||
atlas: self.textures.backend_data(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub trait HasPainter {
|
||||
// fn painter(&self) -> &PainterInstance;
|
||||
// fn painter_mut(&self) -> &mut PainterInstance;
|
||||
// }
|
||||
|
||||
// pub trait PresentFrontend: HasPainter {
|
||||
// fn commands(&self) -> &dyn PaintCommand;
|
||||
|
||||
// fn present(&self, backend: &mut dyn PresentBackend) {
|
||||
// backend.presentation().draw(
|
||||
// self.painter_mut(),
|
||||
// self.commands(),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
pub trait RenderBackend {
|
||||
fn presentation(&self) -> &mut Presentatation;
|
||||
}
|
||||
|
|
|
@ -38,20 +38,4 @@ impl PainterInstance {
|
|||
pub fn fonts_mut(&mut self) -> &mut FontManager {
|
||||
&mut self.fonts
|
||||
}
|
||||
|
||||
// pub fn atlas(&self) -> &TextureAtlas {
|
||||
// &self.atlas
|
||||
// }
|
||||
|
||||
// pub fn atlas_mut(&mut self) -> &mut TextureAtlas {
|
||||
// &mut self.atlas
|
||||
// }
|
||||
|
||||
// pub fn fonts(&self) -> &FontManager {
|
||||
// &self.fonts
|
||||
// }
|
||||
|
||||
// pub fn fonts_mut(&mut self) -> &mut FontManager {
|
||||
// &mut self.fonts
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ use crate::{
|
|||
paint::{
|
||||
buffer::{PaintBuffer, Vertex},
|
||||
command::PaintCommand,
|
||||
}, text::FontHandle, PainterInstance
|
||||
},
|
||||
text::FontHandle,
|
||||
util::hash_vec4,
|
||||
PainterInstance,
|
||||
};
|
||||
|
||||
// TODO align, multichunk etc
|
||||
|
@ -145,9 +148,23 @@ impl PaintCommand for PaintText {
|
|||
|
||||
fn cache_hash(&self) -> u64 {
|
||||
let mut hasher = rustc_hash::FxHasher::default();
|
||||
|
||||
// cache font/size/color
|
||||
self.text.font.hash(&mut hasher);
|
||||
hasher.write_u32(self.text.size.to_bits());
|
||||
hasher.write(self.text.text.as_bytes());
|
||||
hash_vec4(&mut hasher, self.text.color);
|
||||
|
||||
// cache text content
|
||||
match self.text.text {
|
||||
Cow::Owned(ref s) => hasher.write(s.as_bytes()),
|
||||
Cow::Borrowed(s) => {
|
||||
// since the lifetime is 'static, the str is guaranteed to never change
|
||||
// so we can safely compare the ptr + len instead of the content
|
||||
hasher.write_usize(s.as_ptr() as usize);
|
||||
hasher.write_usize(s.len());
|
||||
}
|
||||
}
|
||||
|
||||
hasher.finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ impl TextureAllocation {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TextureAtlasBackendData<'a> {
|
||||
pub data: &'a [u8],
|
||||
pub size: UVec2,
|
||||
|
|
|
@ -38,12 +38,6 @@ default = ["el_all", "image", "builtin_font", "derive"]
|
|||
## Enable derive macros
|
||||
derive = ["dep:hui-derive"]
|
||||
|
||||
## Enable image loading support using the `image` crate
|
||||
image = ["dep:image"]
|
||||
|
||||
## Enable the built-in font (ProggyTiny, adds *35kb* to the executable)
|
||||
builtin_font = []
|
||||
|
||||
#! #### Built-in elements:
|
||||
|
||||
## Enable all built-in elements
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use hui_painter::{
|
||||
paint::{buffer::PaintBuffer, command::{PaintCommand, PaintList, PaintRoot}},
|
||||
text::FontHandle,
|
||||
texture::{SourceTextureFormat, TextureAtlasBackendData, TextureHandle},
|
||||
PainterInstance,
|
||||
backend::{BackendData, Presentatation}, paint::{buffer::PaintBuffer, command::{PaintCommand, PaintList, PaintRoot}}, text::FontHandle, texture::{SourceTextureFormat, TextureAtlasBackendData, TextureHandle}, PainterInstance
|
||||
};
|
||||
use crate::{
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
|
@ -21,17 +18,12 @@ use crate::{
|
|||
/// (Please note that it's possible to render multiple UI "roots" using a single instance)
|
||||
pub struct UiInstance {
|
||||
painter: PainterInstance,
|
||||
prev_draw_command_hash: Option<u64>,
|
||||
cur_draw_command_hash: Option<u64>,
|
||||
draw_commands: PaintList,
|
||||
paint_buffer: PaintBuffer,
|
||||
paint_commands: PaintList,
|
||||
stateful_state: StateRepo,
|
||||
events: EventQueue,
|
||||
input: UiInputState,
|
||||
signal: SignalStore,
|
||||
font_stack: FontStack,
|
||||
/// True if in the middle of a laying out a frame
|
||||
state: bool,
|
||||
}
|
||||
|
||||
impl UiInstance {
|
||||
|
@ -41,16 +33,12 @@ impl UiInstance {
|
|||
pub fn new() -> Self {
|
||||
UiInstance {
|
||||
painter: PainterInstance::new(),
|
||||
prev_draw_command_hash: None,
|
||||
cur_draw_command_hash: None,
|
||||
draw_commands: PaintList::default(),
|
||||
paint_buffer: PaintBuffer::new(),
|
||||
paint_commands: PaintList::default(),
|
||||
font_stack: FontStack::new(),
|
||||
stateful_state: StateRepo::new(),
|
||||
events: EventQueue::new(),
|
||||
input: UiInputState::new(),
|
||||
signal: SignalStore::new(),
|
||||
state: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +76,8 @@ impl UiInstance {
|
|||
/// # Panics:
|
||||
/// - If the file exists but contains invalid image data\
|
||||
/// (this will change to a soft error in the future)
|
||||
///
|
||||
/// Deprecated.
|
||||
#[cfg(feature = "image")]
|
||||
#[deprecated]
|
||||
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<TextureHandle, std::io::Error> {
|
||||
|
@ -148,7 +138,6 @@ impl UiInstance {
|
|||
/// ## Panics:
|
||||
/// If called while the UI is not active (call [`UiInstance::begin`] first)
|
||||
pub fn add(&mut self, element: impl UiElement, rect: impl Into<Rect>) {
|
||||
assert!(self.state, "must call UiInstance::begin before adding elements");
|
||||
let rect: Rect = rect.into();
|
||||
let layout = LayoutInfo {
|
||||
position: rect.position,
|
||||
|
@ -169,79 +158,28 @@ impl UiInstance {
|
|||
measure: &measure,
|
||||
state: &mut self.stateful_state,
|
||||
layout: &layout,
|
||||
paint_target: &mut self.draw_commands,
|
||||
paint_target: &mut self.paint_commands,
|
||||
input: self.input.ctx(),
|
||||
signal: &mut self.signal,
|
||||
current_font,
|
||||
});
|
||||
}
|
||||
|
||||
/// Prepare the UI for layout and processing\
|
||||
/// You must call this function at the beginning of the frame, before adding any elements\
|
||||
/// Reset the state from the previous frame, and prepare the UI for layout and processing\
|
||||
/// You must call this function at the start 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) {
|
||||
//check and update current state
|
||||
assert!(!self.state, "must call UiInstance::end before calling UiInstance::begin again");
|
||||
self.state = true;
|
||||
|
||||
pub fn begin_frame(&mut self) {
|
||||
//first, drain and process the event queue
|
||||
self.input.update_state(&mut self.events);
|
||||
|
||||
//then, reset the (remaining) signals
|
||||
self.signal.clear();
|
||||
|
||||
// Compute the hash of the current commands
|
||||
self.prev_draw_command_hash = Some(self.draw_commands.cache_hash());
|
||||
|
||||
// Clear the draw commands
|
||||
self.draw_commands.clear();
|
||||
|
||||
//then, reset the draw commands
|
||||
// std::mem::swap(&mut self.prev_draw_commands, &mut self.draw_commands);
|
||||
// self.draw_commands.commands.clear();
|
||||
// self.draw_call_modified = false;
|
||||
|
||||
//reset atlas modification flag
|
||||
// self.atlas.reset_modified();
|
||||
}
|
||||
|
||||
/// 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. (or if called twice)\
|
||||
/// This is an indication of a bug in your code and should be fixed.
|
||||
pub fn end(&mut self) {
|
||||
//check and update current state
|
||||
assert!(self.state, "must call UiInstance::begin before calling UiInstance::end");
|
||||
self.state = false;
|
||||
|
||||
//check if the draw commands have been modified
|
||||
if let Some(prev_hash) = self.prev_draw_command_hash {
|
||||
let cur_hash = self.draw_commands.cache_hash();
|
||||
self.cur_draw_command_hash = Some(cur_hash);
|
||||
if cur_hash == prev_hash {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//if they have, rebuild the draw call and set the modified flag
|
||||
self.paint_buffer.clear();
|
||||
self.draw_commands.paint_root(&mut self.painter, &mut self.paint_buffer);
|
||||
}
|
||||
|
||||
/// Get data intended for rendering backend
|
||||
///
|
||||
/// You should call this function *before* calling [`UiInstance::begin`] or after calling [`UiInstance::end`]\
|
||||
///
|
||||
/// This function should only be used by the rendering backend.\
|
||||
/// You should not call this directly unless you're implementing a custom render backend
|
||||
/// or have a very specific usecase (not using one)
|
||||
fn backend_data(&self) -> (&TextureAtlasBackendData, &PaintBuffer) {
|
||||
(&self.painter, &self.paint_buffer)
|
||||
self.paint_commands.clear();
|
||||
}
|
||||
|
||||
/// Push a platform event to the UI event queue
|
||||
|
@ -256,9 +194,6 @@ impl UiInstance {
|
|||
/// You should not call this directly unless you're implementing a custom platform backend
|
||||
/// or have a very specific usecase (not using one)
|
||||
pub fn push_event(&mut self, event: UiEvent) {
|
||||
if self.state {
|
||||
log::warn!("UiInstance::push_event called while in the middle of a frame, this is probably a mistake");
|
||||
}
|
||||
self.events.push(event);
|
||||
}
|
||||
|
||||
|
@ -275,6 +210,11 @@ impl UiInstance {
|
|||
pub fn process_signals<T: Signal + 'static>(&mut self, f: impl FnMut(T)) {
|
||||
self.signal.drain::<T>().for_each(f);
|
||||
}
|
||||
|
||||
/// Get the paint commands needed to render the UI
|
||||
pub fn paint_command(&self) -> &impl PaintCommand {
|
||||
&self.paint_commands
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UiInstance {
|
||||
|
|
Loading…
Reference in a new issue