allow rect in add, clean up elem api

This commit is contained in:
griffi-gh 2024-04-17 15:21:17 +02:00
parent 92f8975702
commit cdaf4c0781
3 changed files with 43 additions and 34 deletions

View file

@ -1,11 +1,11 @@
//! element API and built-in elements like `Container`, `Button`, `Text`, etc. //! element API and built-in elements like `Container`, `Button`, `Text`, etc.
use std::any::Any;
use crate::{ use crate::{
draw::{atlas::ImageCtx, UiDrawCommandList}, draw::{atlas::ImageCtx, UiDrawCommandList},
input::InputCtx, input::InputCtx,
layout::{LayoutInfo, Size2d}, layout::{LayoutInfo, Size2d},
measure::Response, measure::Response,
rect::Rect,
signal::SignalStore, signal::SignalStore,
state::StateRepo, state::StateRepo,
text::{FontHandle, TextMeasure}, text::{FontHandle, TextMeasure},
@ -50,27 +50,6 @@ pub trait UiElement {
/// You should implement this function whenever possible, otherwise some features may not work at all, such as the `Remaining` size /// You should implement this function whenever possible, otherwise some features may not work at all, such as the `Remaining` size
fn size(&self) -> Option<Size2d> { None } fn size(&self) -> Option<Size2d> { None }
/// Get the unique id used for internal state management\
/// This value must be unique for each instance of the element
///
/// If the element is stateless, this function should return `None`
fn state_id(&self) -> Option<u64> { None }
/// Check if the element has state.\
/// Should not be overridden
fn is_stateful(&self) -> bool { self.state_id().is_some() }
/// Check if the element has no state\
/// Should not be overridden
fn is_stateless(&self) -> bool { !self.is_stateful() }
/// Initialize the state of the element\
/// This function should be called exactly once during the lifetime of the element,
/// or if the state gets reset
///
/// This function will not get called for stateless elements
fn init_state(&self) -> Option<Box<dyn Any>> { None }
/// Measure step, guaranteed to be called before the `process` step\ /// Measure step, guaranteed to be called before the `process` step\
/// May be called multiple times per single frame, so it should not contain any expensive calls\ /// May be called multiple times per single frame, so it should not contain any expensive calls\
/// This function may not mutate any state.\ /// This function may not mutate any state.\
@ -108,7 +87,7 @@ pub trait UiElementExt: UiElement {
fn add_child(self, ui: &mut ElementList); fn add_child(self, ui: &mut ElementList);
/// Add element as a ui root. /// Add element as a ui root.
fn add_root(self, ui: &mut UiInstance, max_size: glam::Vec2); fn add_root(self, ui: &mut UiInstance, max_size: impl Into<Rect>);
} }
impl<T: UiElement + 'static> UiElementExt for T { impl<T: UiElement + 'static> UiElementExt for T {
@ -116,7 +95,7 @@ impl<T: UiElement + 'static> UiElementExt for T {
ui.add(self) ui.add(self)
} }
fn add_root(self, ui: &mut UiInstance, max_size: glam::Vec2) { fn add_root(self, ui: &mut UiInstance, rect: impl Into<Rect>) {
ui.add(self, max_size); ui.add(self, rect);
} }
} }

View file

@ -1,16 +1,20 @@
use glam::Vec2; use glam::Vec2;
use crate::{ use crate::{
element::{MeasureContext, ProcessContext, UiElement},
layout::{Direction, LayoutInfo},
text::{FontHandle, TextRenderer},
draw::{ draw::{
ImageHandle, TextureFormat, UiDrawCall, UiDrawCommandList, ImageHandle,
TextureFormat,
UiDrawCall,
UiDrawCommandList,
atlas::{TextureAtlasManager, TextureAtlasMeta}, atlas::{TextureAtlasManager, TextureAtlasMeta},
}, },
element::{MeasureContext, ProcessContext, UiElement}, signal::{Signal, SignalStore},
event::{EventQueue, UiEvent}, event::{EventQueue, UiEvent},
input::UiInputState, input::UiInputState,
layout::{Direction, LayoutInfo}, rect::Rect,
signal::{SignalStore, Signal},
state::StateRepo, state::StateRepo,
text::{FontHandle, TextRenderer}
}; };
/// The main instance of the UI system. /// The main instance of the UI system.
@ -144,16 +148,17 @@ impl UiInstance {
/// Add an element or an element tree to the UI /// Add an element or an element tree to the UI
/// ///
/// Use the `max_size` parameter to specify the maximum size of the element\ /// Use the `rect` parameter to specify the position and size of the element\
/// (usually, the size of the window/screen) /// (usually, the size of the window/screen)
/// ///
/// ## Panics: /// ## Panics:
/// If called while the UI is not active (call [`UiInstance::begin`] first) /// If called while the UI is not active (call [`UiInstance::begin`] first)
pub fn add<T: UiElement>(&mut self, element: T, max_size: Vec2) { pub fn add(&mut self, element: impl UiElement, rect: impl Into<Rect>) {
assert!(self.state, "must call UiInstance::begin before adding elements"); assert!(self.state, "must call UiInstance::begin before adding elements");
let rect: Rect = rect.into();
let layout = LayoutInfo { let layout = LayoutInfo {
position: Vec2::ZERO, position: rect.position,
max_size, max_size: rect.size,
direction: Direction::Vertical, direction: Direction::Vertical,
remaining_space: None, remaining_space: None,
}; };

View file

@ -11,6 +11,24 @@ pub struct Rect {
} }
impl Rect { impl Rect {
pub const fn new(position: Vec2, size: Vec2) -> Self {
Self { position, size }
}
pub const fn from_position(position: Vec2) -> Self {
Self {
position,
size: Vec2::ZERO,
}
}
pub const fn from_size(size: Vec2) -> Self {
Self {
position: Vec2::ZERO,
size,
}
}
/// Check if the rect contains a point. /// Check if the rect contains a point.
pub fn contains_point(&self, point: Vec2) -> bool { pub fn contains_point(&self, point: Vec2) -> bool {
point.cmpge(self.position).all() && point.cmple(self.position + self.size).all() point.cmpge(self.position).all() && point.cmple(self.position + self.size).all()
@ -63,3 +81,10 @@ impl Rect {
} }
} }
} }
impl From<Vec2> for Rect {
/// Create a new `Rect` from a `Vec2`, where x and y are the width and height of the rect respectively.
fn from(size: Vec2) -> Self {
Self::from_size(size)
}
}