Compare commits

...

10 commits

27 changed files with 342 additions and 309 deletions

View file

@ -32,7 +32,7 @@
.with_align(Alignment::Center) .with_align(Alignment::Center)
.with_padding(5.) .with_padding(5.)
.with_gap(10.) .with_gap(10.)
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.5, 0.5, 0.5, 1.), color: (0.5, 0.5, 0.5, 1.),
corner_radius: 10., corner_radius: 10.,
}) })
@ -44,7 +44,7 @@
.add_child(ui); .add_child(ui);
Container::default() Container::default()
.with_padding((10., 20.)) .with_padding((10., 20.))
.with_background(frame_rect! { .with_background(rect_frame! {
color: color::DARK_RED, color: color::DARK_RED,
corner_radius: (2.5, 30., 2.5, 2.5), corner_radius: (2.5, 30., 2.5, 2.5),
}) })

View file

@ -9,8 +9,8 @@ use winit::{
}; };
use hui::{ use hui::{
element::{ element::{
container::Container, fill_rect::FillRect, progress_bar::ProgressBar, ElementList, UiElement container::Container, frame_view::FrameView, progress_bar::ProgressBar, ElementList, UiElement
}, frame::FrameRect, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance }, frame::RectFrame, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance
}; };
use hui_glium::GliumUiRenderer; use hui_glium::GliumUiRenderer;
@ -71,11 +71,11 @@ fn main() {
padding: Sides::all(5.), padding: Sides::all(5.),
gap: 10., gap: 10.,
children: ElementList(vec![ children: ElementList(vec![
Box::new(FillRect { Box::new(FrameView {
size: (Size::Relative(0.5), Size::Absolute(30.)).into(), size: (Size::Relative(0.5), Size::Absolute(30.)).into(),
frame: Box::new(vec4(0.75, 0., 0., 1.)), frame: Box::new(vec4(0.75, 0., 0., 1.)),
}), }),
Box::new(FillRect { Box::new(FrameView {
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(), size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
frame: Box::new(Corners::left_right( frame: Box::new(Corners::left_right(
vec4(1., 0., 0., 1.), vec4(1., 0., 0., 1.),
@ -85,19 +85,19 @@ fn main() {
]), ]),
..Default::default() ..Default::default()
}), }),
Box::new(FillRect { Box::new(FrameView {
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(), size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
frame: Box::new(vec4(0., 0.75, 0., 1.)), frame: Box::new(vec4(0., 0.75, 0., 1.)),
}), }),
Box::new(Container { Box::new(Container {
gap: 5., gap: 5.,
padding: Sides::all(5.), padding: Sides::all(5.),
background_frame: Box::new(FrameRect::color(vec4(0., 0., 0., 0.5))), background_frame: Box::new(RectFrame::color(vec4(0., 0., 0., 0.5))),
direction: Direction::Horizontal, direction: Direction::Horizontal,
children: { children: {
let mut x: Vec<Box<dyn UiElement>> = vec![]; let mut x: Vec<Box<dyn UiElement>> = vec![];
for i in 0..10 { for i in 0..10 {
x.push(Box::new(FillRect { x.push(Box::new(FrameView {
size: (Size::Absolute(50.), Size::Absolute(50.)).into(), size: (Size::Absolute(50.), Size::Absolute(50.)).into(),
frame: Box::new(if i == 1 { frame: Box::new(if i == 1 {
vec4(0.75, 0.75, 0.75, 0.75) vec4(0.75, 0.75, 0.75, 0.75)
@ -111,7 +111,7 @@ fn main() {
..Default::default() ..Default::default()
}), }),
Box::new(Container { Box::new(Container {
background_frame: Box::new(FrameRect::color((1., 0., 0.)).with_corner_radius(Corners { background_frame: Box::new(RectFrame::color((1., 0., 0.)).with_corner_radius(Corners {
top_left: 0., top_left: 0.,
top_right: 30., top_right: 30.,
bottom_left: 0., bottom_left: 0.,
@ -124,7 +124,7 @@ fn main() {
right: 40., right: 40.,
}, },
children: ElementList(vec![ children: ElementList(vec![
Box::new(FillRect { Box::new(FrameView {
size: (Size::Absolute(50.), Size::Absolute(50.)).into(), size: (Size::Absolute(50.), Size::Absolute(50.)).into(),
frame: Box::new(vec4(1., 1., 1., 0.75)), frame: Box::new(vec4(1., 1., 1., 0.75)),
}), }),

View file

@ -5,7 +5,7 @@ use hui::{
progress_bar::ProgressBar, progress_bar::ProgressBar,
text::Text, text::Text,
UiElementExt, UiElementExt,
}, frame::FrameRect, frame_rect, layout::{Alignment, Direction}, size }, frame::RectFrame, rect_frame, layout::{Alignment, Direction}, size
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -31,7 +31,7 @@ ui_main!{
.with_gap(5.) .with_gap(5.)
.with_padding(10.) .with_padding(10.)
.with_size(size!(450, auto)) .with_size(size!(450, auto))
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.2, 0.2, 0.5), color: (0.2, 0.2, 0.5),
corner_radius: 8. corner_radius: 8.
}) })
@ -43,11 +43,11 @@ ui_main!{
.add_child(ui); .add_child(ui);
ProgressBar::default() ProgressBar::default()
.with_value(mom_ratio) .with_value(mom_ratio)
.with_background(frame_rect! { .with_background(rect_frame! {
color: color::BLACK, color: color::BLACK,
corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT
}) })
.with_foreground(frame_rect! { .with_foreground(rect_frame! {
color: color::BLUE, color: color::BLUE,
corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT
}) })

View file

@ -9,8 +9,8 @@ use winit::{
}; };
use hui::{ use hui::{
element::{ element::{
container::Container, fill_rect::FillRect, spacer::Spacer, text::Text, ElementList container::Container, frame_view::FrameView, spacer::Spacer, text::Text, ElementList
}, frame::FrameRect, layout::Size, UiInstance }, frame::RectFrame, layout::Size, UiInstance
}; };
use hui_glium::GliumUiRenderer; use hui_glium::GliumUiRenderer;
@ -49,7 +49,7 @@ fn main() {
hui.add(Container { hui.add(Container {
size: (Size::Relative(1.), Size::Relative(1.)).into(), size: (Size::Relative(1.), Size::Relative(1.)).into(),
background_frame: Box::new(FrameRect::color((0.1, 0.1, 0.1, 1.))), background_frame: Box::new(RectFrame::color((0.1, 0.1, 0.1, 1.))),
children: elements(|elem| { children: elements(|elem| {
elem.push(Box::new(Text { elem.push(Box::new(Text {
text: "THIS LINE SHOULD BE SHARP!".into(), text: "THIS LINE SHOULD BE SHARP!".into(),
@ -71,11 +71,11 @@ fn main() {
..Default::default() ..Default::default()
})); }));
} }
elem.push(Box::new(FillRect { elem.push(Box::new(FrameView {
size: (Size::Relative(1.), Size::Absolute(10.)).into(), size: (Size::Relative(1.), Size::Absolute(10.)).into(),
frame: Box::new(vec4(0., 0., 1., 1.)), frame: Box::new(vec4(0., 0., 1., 1.)),
})); }));
elem.push(Box::new(FillRect { elem.push(Box::new(FrameView {
size: (Size::Relative(1.), Size::Absolute(10.)).into(), size: (Size::Relative(1.), Size::Absolute(10.)).into(),
frame: Box::new(vec4(1., 1., 0., 1.)), frame: Box::new(vec4(1., 1., 0., 1.)),
})); }));
@ -86,11 +86,11 @@ fn main() {
..Default::default() ..Default::default()
})); }));
if instant.elapsed().as_secs() & 1 != 0 { if instant.elapsed().as_secs() & 1 != 0 {
elem.push(Box::new(FillRect { elem.push(Box::new(FrameView {
size: (Size::Relative(1.), Size::Absolute(10.)).into(), size: (Size::Relative(1.), Size::Absolute(10.)).into(),
frame: Box::new(vec4(1., 0., 0., 1.)), frame: Box::new(vec4(1., 0., 0., 1.)),
})); }));
elem.push(Box::new(FillRect { elem.push(Box::new(FrameView {
size: (Size::Relative(1.), Size::Absolute(10.)).into(), size: (Size::Relative(1.), Size::Absolute(10.)).into(),
frame: Box::new(vec4(0., 0., 0., 1.)), frame: Box::new(vec4(0., 0., 0., 1.)),
})); }));

View file

@ -1,7 +1,7 @@
use hui::{ use hui::{
color, size, frame_rect, color, size, rect_frame,
element::{container::Container, text::Text, UiElementExt}, element::{container::Container, text::Text, UiElementExt},
frame::FrameRect, frame::RectFrame,
layout::Alignment, layout::Alignment,
}; };
@ -15,7 +15,7 @@ ui_main!(|ui, size, _| {
.with_align(Alignment::Center) .with_align(Alignment::Center)
.with_padding(5.) .with_padding(5.)
.with_gap(10.) .with_gap(10.)
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0.5, 0.5, 0.5, 1.), color: (0.5, 0.5, 0.5, 1.),
corner_radius: 10., corner_radius: 10.,
}) })
@ -27,7 +27,7 @@ ui_main!(|ui, size, _| {
.add_child(ui); .add_child(ui);
Container::default() Container::default()
.with_padding((10., 20.)) .with_padding((10., 20.))
.with_background(frame_rect! { .with_background(rect_frame! {
color: color::DARK_RED, color: color::DARK_RED,
corner_radius: (2.5, 30., 2.5, 2.5), corner_radius: (2.5, 30., 2.5, 2.5),
}) })

View file

@ -1,6 +1,6 @@
use glam::vec4; use glam::vec4;
use hui::{ use hui::{
size, frame_rect, size, rect_frame,
color, color,
element::{ element::{
container::Container, container::Container,
@ -8,7 +8,7 @@ use hui::{
text::Text, text::Text,
UiElementExt UiElementExt
}, },
frame::FrameRect, frame::RectFrame,
layout::Alignment, layout::Alignment,
rect::Corners, rect::Corners,
text::FontHandle text::FontHandle
@ -45,7 +45,7 @@ ui_main!(
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_padding((10., 15.)) .with_padding((10., 15.))
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0., 0., 0., 0.5), color: (0., 0., 0., 0.5),
corner_radius: 8., corner_radius: 8.,
}) })
@ -70,7 +70,7 @@ ui_main!(
.with_align((Alignment::Center, Alignment::Begin)) .with_align((Alignment::Center, Alignment::Begin))
.with_padding(15.) .with_padding(15.)
.with_gap(10.) .with_gap(10.)
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0., 0., 0., 0.5), color: (0., 0., 0., 0.5),
corner_radius: 8., corner_radius: 8.,
}) })
@ -111,7 +111,7 @@ ui_main!(
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_padding(10.) .with_padding(10.)
.with_background(frame_rect!{ .with_background(rect_frame!{
color: (0., 0., 0., 0.5), color: (0., 0., 0., 0.5),
corner_radius: 8., corner_radius: 8.,
}) })

View file

@ -7,7 +7,7 @@ use hui::{
text::Text, text::Text,
transformer::ElementTransformExt, transformer::ElementTransformExt,
UiElementExt UiElementExt
}, frame::FrameRect, frame_rect, layout::Alignment, rect::Corners, size, text::FontHandle }, frame::RectFrame, rect_frame, layout::Alignment, rect::Corners, size, text::FontHandle
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -37,7 +37,7 @@ ui_main!(
.with_align((Alignment::Center, Alignment::Begin)) .with_align((Alignment::Center, Alignment::Begin))
.with_padding(15.) .with_padding(15.)
.with_gap(10.) .with_gap(10.)
.with_background(frame_rect! { .with_background(rect_frame! {
color: (0., 0., 0., 0.5), color: (0., 0., 0., 0.5),
corner_radius: 8. corner_radius: 8.
}) })

View file

@ -2,9 +2,9 @@ use std::time::Instant;
use hui::{ use hui::{
color, element::{ color, element::{
container::Container, container::Container,
fill_rect::FillRect, frame_view::FrameView,
UiElementExt UiElementExt
}, frame_rect, layout::{Alignment, Direction}, size }, rect_frame, layout::{Alignment, Direction}, size
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -28,9 +28,9 @@ ui_main!(
.with_wrap(true) .with_wrap(true)
.with_children(|ui| { .with_children(|ui| {
for i in 0..10 { for i in 0..10 {
FillRect::default() FrameView::default()
.with_size(size!((40 + i * 10))) .with_size(size!((40 + i * 10)))
.with_frame(frame_rect! { .with_frame(rect_frame! {
color: color::DARK_RED, color: color::DARK_RED,
corner_radius: 8. corner_radius: 8.
}) })

View file

@ -3,7 +3,7 @@ use hui::{
color, color,
element::{ element::{
container::Container, container::Container,
fill_rect::FillRect, frame_view::FrameView,
slider::Slider, slider::Slider,
text::Text, text::Text,
UiElementExt UiElementExt
@ -54,7 +54,7 @@ ui_main!(
.add_child(ui); .add_child(ui);
}) })
.add_child(ui); .add_child(ui);
FillRect::default() FrameView::default()
.with_size(size!(600, 75)) .with_size(size!(600, 75))
.with_frame(NinePatchFrame::from_asset(*asset).with_color(color::GREEN)) .with_frame(NinePatchFrame::from_asset(*asset).with_color(color::GREEN))
.add_child(ui); .add_child(ui);
@ -62,7 +62,7 @@ ui_main!(
.with_color(color::BLACK) .with_color(color::BLACK)
.with_text_size(32) .with_text_size(32)
.add_child(ui); .add_child(ui);
FillRect::default() FrameView::default()
.with_size(size!(700, 50)) .with_size(size!(700, 50))
.with_frame(NinePatchFrame::from_asset(*asset).with_color(( .with_frame(NinePatchFrame::from_asset(*asset).with_color((
(1., 0., 1.), (1., 0., 1.),

View file

@ -6,7 +6,7 @@ use hui::{
layout::{Alignment, Direction}, layout::{Alignment, Direction},
element::{ element::{
container::Container, container::Container,
fill_rect::FillRect, frame_view::FrameView,
image::Image, image::Image,
text::Text, text::Text,
UiElementExt UiElementExt
@ -62,7 +62,7 @@ ui_main!(
.add_child(ui); .add_child(ui);
}) })
.add_child(ui); .add_child(ui);
FillRect::default() FrameView::default()
.with_size(size!(100%, 1)) .with_size(size!(100%, 1))
.with_frame(color::rgb_hex(0x2d2d30)) .with_frame(color::rgb_hex(0x2d2d30))
.add_child(ui); .add_child(ui);
@ -75,7 +75,7 @@ ui_main!(
.with_size(size!(54, 100%)) .with_size(size!(54, 100%))
.with_background(color::rgb_hex(0x343334)) .with_background(color::rgb_hex(0x343334))
.add_child(ui); .add_child(ui);
FillRect::default() FrameView::default()
.with_size(size!(1, 100%)) .with_size(size!(1, 100%))
.with_frame(color::rgb_hex(0x2d2d30)) .with_frame(color::rgb_hex(0x2d2d30))
.add_child(ui); .add_child(ui);

View file

@ -58,7 +58,7 @@ pixel_perfect_text = []
## Enable all built-in elements ## Enable all built-in elements
el_all = [ el_all = [
"el_container", "el_container",
"el_fill_rect", "el_frame_view",
"el_spacer", "el_spacer",
"el_br", "el_br",
"el_text", "el_text",
@ -72,8 +72,8 @@ el_all = [
## Enable the built-in `Container` element ## Enable the built-in `Container` element
el_container = [] el_container = []
## Enable the built-in `FillRect` element ## Enable the built-in `FrameView` element
el_fill_rect = [] el_frame_view = []
## Enable the built-in `Spacer` element ## Enable the built-in `Spacer` element
el_spacer = [] el_spacer = []

View file

@ -38,6 +38,8 @@ pub struct TextureAtlasMeta<'a> {
/// Texture handle, stores the internal index of a texture within the texture atlas and can be cheaply copied. /// Texture handle, stores the internal index of a texture within the texture atlas and can be cheaply copied.
/// ///
/// Please note that dropping a handle does not deallocate the texture from the atlas, you must do it manually.
///
/// Only valid for the `UiInstance` that created it.\ /// Only valid for the `UiInstance` that created it.\
/// Using it with other instances may result in panics or unexpected behavior. /// Using it with other instances may result in panics or unexpected behavior.
/// ///

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},
@ -17,8 +17,8 @@ pub use builtin::*;
/// Context for the `Element::measure` function /// Context for the `Element::measure` function
pub struct MeasureContext<'a> { pub struct MeasureContext<'a> {
pub state: &'a StateRepo,
pub layout: &'a LayoutInfo, pub layout: &'a LayoutInfo,
pub state: &'a StateRepo,
pub text_measure: TextMeasure<'a>, pub text_measure: TextMeasure<'a>,
pub current_font: FontHandle, pub current_font: FontHandle,
pub images: ImageCtx<'a>, pub images: ImageCtx<'a>,
@ -29,9 +29,9 @@ pub struct MeasureContext<'a> {
/// Context for the `Element::process` function /// Context for the `Element::process` function
pub struct ProcessContext<'a> { pub struct ProcessContext<'a> {
pub measure: &'a Response, pub measure: &'a Response,
pub state: &'a mut StateRepo,
pub layout: &'a LayoutInfo, pub layout: &'a LayoutInfo,
pub draw: &'a mut UiDrawCommandList, pub draw: &'a mut UiDrawCommandList,
pub state: &'a mut StateRepo,
pub text_measure: TextMeasure<'a>, pub text_measure: TextMeasure<'a>,
pub current_font: FontHandle, pub current_font: FontHandle,
pub images: ImageCtx<'a>, pub images: ImageCtx<'a>,
@ -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

@ -3,8 +3,8 @@
#[cfg(feature = "el_container")] #[cfg(feature = "el_container")]
pub mod container; pub mod container;
#[cfg(feature = "el_fill_rect")] #[cfg(feature = "el_frame_view")]
pub mod fill_rect; pub mod frame_view;
#[cfg(feature = "el_spacer")] #[cfg(feature = "el_spacer")]
pub mod spacer; pub mod spacer;

View file

@ -4,7 +4,7 @@ use derive_setters::Setters;
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use crate::{ use crate::{
element::{ElementList, MeasureContext, ProcessContext, UiElement}, element::{ElementList, MeasureContext, ProcessContext, UiElement},
frame::{Frame, FrameRect}, frame::{Frame, RectFrame},
layout::{compute_size, Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d, WrapBehavior}, layout::{compute_size, Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d, WrapBehavior},
measure::{Hints, Response}, measure::{Hints, Response},
rect::Sides, rect::Sides,
@ -82,7 +82,7 @@ impl Default for Container {
gap: 0., gap: 0.,
padding: Sides::all(0.), padding: Sides::all(0.),
align: Alignment2d::default(), align: Alignment2d::default(),
background_frame: Box::<FrameRect>::default(), background_frame: Box::<RectFrame>::default(),
wrap: WrapBehavior::Allow, wrap: WrapBehavior::Allow,
children: ElementList(Vec::new()), children: ElementList(Vec::new()),
} }
@ -375,7 +375,7 @@ impl UiElement for Container {
// }); // });
// } // }
self.background_frame.draw(ctx.draw, ctx.layout.position, ctx.measure.size); self.background_frame.draw(ctx.draw, (ctx.layout.position, ctx.measure.size).into());
//padding //padding
position += vec2(self.padding.left, self.padding.top); position += vec2(self.padding.left, self.padding.top);
@ -444,8 +444,8 @@ impl UiElement for Container {
//measure //measure
let el_measure = element.measure(MeasureContext { let el_measure = element.measure(MeasureContext {
state: ctx.state,
layout: &el_layout, layout: &el_layout,
state: ctx.state,
text_measure: ctx.text_measure, text_measure: ctx.text_measure,
current_font: ctx.current_font, current_font: ctx.current_font,
images: ctx.images, images: ctx.images,
@ -486,15 +486,13 @@ impl UiElement for Container {
//process //process
element.process(ProcessContext { element.process(ProcessContext {
measure: &el_measure, measure: &el_measure,
state: ctx.state,
layout: &el_layout, layout: &el_layout,
draw: ctx.draw, draw: ctx.draw,
state: ctx.state,
text_measure: ctx.text_measure, text_measure: ctx.text_measure,
current_font: ctx.current_font, current_font: ctx.current_font,
images: ctx.images, images: ctx.images,
input: ctx.input, input: ctx.input,
//HACK: i have no idea what to do with this
//this sucks
signal: ctx.signal, signal: ctx.signal,
}); });

View file

@ -1,73 +0,0 @@
//! Simple filled rectangle with the specified size, background and corner radius
use derive_setters::Setters;
use glam::vec2;
use crate::{
draw::{RoundedCorners, UiDrawCommand},
element::{MeasureContext, ProcessContext, UiElement},
frame::{Frame, FrameRect},
layout::{compute_size, Size, Size2d},
measure::Response,
size
};
/// Simple filled rectangle with the specified size, background, and corner radius
#[derive(Setters)]
#[setters(prefix = "with_")]
pub struct FillRect {
/// Size of the rectangle
#[setters(into)]
pub size: Size2d,
/// Frame
#[setters(skip)]
pub frame: Box<dyn Frame>,
}
impl FillRect {
pub fn with_frame(mut self, frame: impl Frame + 'static) -> Self {
self.frame = Box::new(frame);
self
}
}
impl Default for FillRect {
fn default() -> Self {
Self {
size: size!(10, 10),
frame: Box::new(FrameRect::color((0., 0., 0., 0.5))),
}
}
}
impl UiElement for FillRect {
fn name(&self) -> &'static str {
"fill_rect"
}
fn size(&self) -> Option<Size2d> {
Some(self.size)
}
fn measure(&self, ctx: MeasureContext) -> Response {
Response {
size: compute_size(ctx.layout, self.size, ctx.layout.max_size),
..Default::default()
}
}
fn process(&self, ctx: ProcessContext) {
// if !self.background.is_transparent() {
// ctx.draw.add(UiDrawCommand::Rectangle {
// position: ctx.layout.position,
// size: ctx.measure.size,
// color: self.background.corners(),
// texture: None,
// rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
// RoundedCorners::from_radius(self.corner_radius)
// }),
// });
// }
self.frame.draw(ctx.draw, ctx.layout.position, ctx.measure.size);
}
}

View file

@ -0,0 +1,68 @@
//! Simple element that displays the specified frame
use derive_setters::Setters;
use crate::{
element::{MeasureContext, ProcessContext, UiElement},
frame::{Frame, RectFrame},
layout::{compute_size, Size2d},
measure::Response,
size
};
/// Simple rectangle that displays the specified frame
#[derive(Setters)]
#[setters(prefix = "with_")]
pub struct FrameView {
/// Size of the rectangle
#[setters(into)]
pub size: Size2d,
/// Frame
#[setters(skip)]
pub frame: Box<dyn Frame>,
}
impl FrameView {
pub fn new(frame: impl Frame + 'static) -> Self {
Self {
size: size!(10, 10),
frame: Box::new(frame),
}
}
//setters:
pub fn with_frame(mut self, frame: impl Frame + 'static) -> Self {
self.frame = Box::new(frame);
self
}
}
impl Default for FrameView {
fn default() -> Self {
Self {
size: size!(10, 10),
frame: Box::new(RectFrame::color((0., 0., 0., 0.5))),
}
}
}
impl UiElement for FrameView {
fn name(&self) -> &'static str {
"frame_view"
}
fn size(&self) -> Option<Size2d> {
Some(self.size)
}
fn measure(&self, ctx: MeasureContext) -> Response {
Response {
size: compute_size(ctx.layout, self.size, ctx.layout.max_size),
..Default::default()
}
}
fn process(&self, ctx: ProcessContext) {
self.frame.draw(ctx.draw, (ctx.layout.position, ctx.measure.size).into());
}
}

View file

@ -2,7 +2,7 @@ use derive_setters::Setters;
use glam::vec2; use glam::vec2;
use crate::{ use crate::{
element::{MeasureContext, ProcessContext, UiElement}, element::{MeasureContext, ProcessContext, UiElement},
frame::{Frame, FrameRect}, frame::{Frame, RectFrame},
layout::{compute_size, Size, Size2d}, layout::{compute_size, Size, Size2d},
measure::Response, measure::Response,
}; };
@ -47,8 +47,8 @@ impl Default for ProgressBar {
Self { Self {
value: 0., value: 0.,
size: Size::Auto.into(), size: Size::Auto.into(),
foreground: Box::new(FrameRect::color((0.0, 0.0, 1.0, 1.0))), foreground: Box::new(RectFrame::color((0.0, 0.0, 1.0, 1.0))),
background: Box::new(FrameRect::color((0.0, 0.0, 0.0, 1.0))), background: Box::new(RectFrame::color((0.0, 0.0, 0.0, 1.0))),
} }
} }
} }
@ -75,10 +75,10 @@ impl UiElement for ProgressBar {
//FIXME: these optimizations may not be valid //FIXME: these optimizations may not be valid
if value < 1. || !self.foreground.covers_opaque() { if value < 1. || !self.foreground.covers_opaque() {
self.background.draw(ctx.draw, ctx.layout.position, ctx.measure.size); self.background.draw(ctx.draw, (ctx.layout.position, ctx.measure.size).into());
} }
if value > 0. { if value > 0. {
self.foreground.draw(ctx.draw, ctx.layout.position, ctx.measure.size * vec2(value, 1.)); self.foreground.draw(ctx.draw, (ctx.layout.position, ctx.measure.size * vec2(value, 1.)).into());
} }
// let rounded_corners = // let rounded_corners =

View file

@ -4,7 +4,7 @@ use derive_setters::Setters;
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use crate::{ use crate::{
draw::UiDrawCommand, element::{MeasureContext, ProcessContext, UiElement}, frame::{Frame, FrameRect}, layout::{compute_size, Size2d}, measure::Response, rect::FillColor, signal::{trigger::SignalTriggerArg, Signal} draw::UiDrawCommand, element::{MeasureContext, ProcessContext, UiElement}, frame::{Frame, RectFrame}, layout::{compute_size, Size2d}, measure::Response, rect::FillColor, signal::{trigger::SignalTriggerArg, Signal}
}; };
@ -79,9 +79,9 @@ impl Default for Slider {
Self { Self {
value: 0.0, value: 0.0,
size: Size2d::default(), size: Size2d::default(),
handle: Box::new(FrameRect::color((0.0, 0.0, 1.))), handle: Box::new(RectFrame::color((0.0, 0.0, 1.))),
track: Box::new(FrameRect::color((0.5, 0.5, 0.5))), track: Box::new(RectFrame::color((0.5, 0.5, 0.5))),
track_active: Box::new(FrameRect::color((0.0, 0.0, 0.75))), track_active: Box::new(RectFrame::color((0.0, 0.0, 0.75))),
track_height: 0.25, track_height: 0.25,
handle_size: (15.0, 1.), handle_size: (15.0, 1.),
follow_mode: SliderFollowMode::default(), follow_mode: SliderFollowMode::default(),
@ -156,8 +156,10 @@ impl UiElement for Slider {
if !(self.track_active.covers_opaque() && self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value >= 1.) { if !(self.track_active.covers_opaque() && self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value >= 1.) {
self.track.draw( self.track.draw(
ctx.draw, ctx.draw,
ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.), (
ctx.measure.size * vec2(1., self.track_height), ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.),
ctx.measure.size * vec2(1., self.track_height),
).into()
); );
} }
@ -168,8 +170,10 @@ impl UiElement for Slider {
if !(self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value <= 0.) { if !(self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value <= 0.) {
self.track_active.draw( self.track_active.draw(
ctx.draw, ctx.draw,
ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.), (
(ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height) + handle_size * Vec2::X / 2., ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.),
(ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height) + handle_size * Vec2::X / 2.,
).into()
); );
} }
@ -187,10 +191,12 @@ impl UiElement for Slider {
if (self.handle_size.0 > 0. && self.handle_size.1 > 0.) { if (self.handle_size.0 > 0. && self.handle_size.1 > 0.) {
self.handle.draw( self.handle.draw(
ctx.draw, ctx.draw,
ctx.layout.position + (
((ctx.measure.size.x - handle_size.x) * self.value) * Vec2::X + ctx.layout.position +
ctx.measure.size.y * ((1. - self.handle_size.1) * 0.5) * Vec2::Y, ((ctx.measure.size.x - handle_size.x) * self.value) * Vec2::X +
handle_size, ctx.measure.size.y * ((1. - self.handle_size.1) * 0.5) * Vec2::Y,
handle_size,
).into()
); );
} }

View file

@ -1,7 +1,6 @@
//! modular procedural background system //! modular procedural background system
use glam::Vec2; use crate::{draw::UiDrawCommandList, rect::Rect};
use crate::draw::UiDrawCommandList;
pub mod point; pub mod point;
mod rect; mod rect;
@ -9,12 +8,12 @@ pub mod stack;
pub mod nine_patch; pub mod nine_patch;
mod impls; mod impls;
pub use rect::FrameRect; pub use rect::RectFrame;
/// Trait for a drawable frame /// Trait for a drawable frame
pub trait Frame { pub trait Frame {
/// Draw the frame at the given position and size /// Draw the frame at the given rect's position and size
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2); fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect);
/// Check if the frame is guaranteed to be fully opaque and fully cover the parent frame regardless of it's size /// Check if the frame is guaranteed to be fully opaque and fully cover the parent frame regardless of it's size
/// ///

View file

@ -3,14 +3,14 @@ use super::Frame;
use crate::{ use crate::{
color, color,
draw::{ImageHandle, UiDrawCommand, UiDrawCommandList}, draw::{ImageHandle, UiDrawCommand, UiDrawCommandList},
rect::{Corners, FillColor}, rect::{Rect, Corners, FillColor},
}; };
impl Frame for ImageHandle { impl Frame for ImageHandle {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
draw.add(UiDrawCommand::Rectangle { draw.add(UiDrawCommand::Rectangle {
position, position: rect.position,
size: parent_size, size: rect.size,
color: color::WHITE.into(), color: color::WHITE.into(),
texture: Some(*self), texture: Some(*self),
texture_uv: None, texture_uv: None,
@ -24,10 +24,10 @@ impl Frame for ImageHandle {
} }
impl Frame for FillColor { impl Frame for FillColor {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
draw.add(UiDrawCommand::Rectangle { draw.add(UiDrawCommand::Rectangle {
position, position: rect.position,
size: parent_size, size: rect.size,
color: self.corners(), color: self.corners(),
texture: None, texture: None,
texture_uv: None, texture_uv: None,
@ -45,8 +45,8 @@ impl Frame for FillColor {
// Corners (RGBA): // Corners (RGBA):
impl Frame for Corners<Vec4> { impl Frame for Corners<Vec4> {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -54,8 +54,8 @@ impl Frame for Corners<Vec4> {
} }
impl Frame for (Vec4, Vec4, Vec4, Vec4) { impl Frame for (Vec4, Vec4, Vec4, Vec4) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -63,8 +63,8 @@ impl Frame for (Vec4, Vec4, Vec4, Vec4) {
} }
impl Frame for ((f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32)) { impl Frame for ((f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32)) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -72,8 +72,8 @@ impl Frame for ((f32, f32, f32, f32), (f32, f32, f32, f32), (f32, f32, f32, f32)
} }
impl Frame for [[f32; 4]; 4] { impl Frame for [[f32; 4]; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -83,46 +83,46 @@ impl Frame for [[f32; 4]; 4] {
// Corners (RGB): // Corners (RGB):
impl Frame for Corners<Vec3> { impl Frame for Corners<Vec3> {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
impl Frame for (Vec3, Vec3, Vec3, Vec3) { impl Frame for (Vec3, Vec3, Vec3, Vec3) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
impl Frame for ((f32, f32, f32), (f32, f32, f32), (f32, f32, f32), (f32, f32, f32)) { impl Frame for ((f32, f32, f32), (f32, f32, f32), (f32, f32, f32), (f32, f32, f32)) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
impl Frame for [[f32; 3]; 4] { impl Frame for [[f32; 3]; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
// RGBA: // RGBA:
impl Frame for Vec4 { impl Frame for Vec4 {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -130,8 +130,8 @@ impl Frame for Vec4 {
} }
impl Frame for (f32, f32, f32, f32) { impl Frame for (f32, f32, f32, f32) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -139,8 +139,8 @@ impl Frame for (f32, f32, f32, f32) {
} }
impl Frame for [f32; 4] { impl Frame for [f32; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() FillColor::from(*self).is_opaque()
@ -150,28 +150,28 @@ impl Frame for [f32; 4] {
// RGB: // RGB:
impl Frame for Vec3 { impl Frame for Vec3 {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
impl Frame for (f32, f32, f32) { impl Frame for (f32, f32, f32) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }
impl Frame for [f32; 3] { impl Frame for [f32; 3] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
FillColor::from(*self).draw(draw, position, parent_size) FillColor::from(*self).draw(draw, rect)
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
FillColor::from(*self).is_opaque() true
} }
} }

View file

@ -4,7 +4,11 @@
//! This is useful for creating scalable UI elements like buttons, windows, etc. //! This is useful for creating scalable UI elements like buttons, windows, etc.
use glam::{vec2, UVec2, Vec2}; use glam::{vec2, UVec2, Vec2};
use crate::{color, draw::{ImageHandle, UiDrawCommand}, rect::{Corners, FillColor, Rect}}; use crate::{
color,
draw::{ImageHandle, UiDrawCommand, UiDrawCommandList},
rect::{Rect, Corners, FillColor}
};
use super::Frame; use super::Frame;
/// Represents a 9-patch image asset /// Represents a 9-patch image asset
@ -49,10 +53,10 @@ impl Default for NinePatchFrame {
} }
impl Frame for NinePatchFrame { impl Frame for NinePatchFrame {
fn draw(&self, draw: &mut crate::draw::UiDrawCommandList, position: glam::Vec2, parent_size: glam::Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
// without this, shїt gets messed up when the position is not a whole number // without this, shїt gets messed up when the position is not a whole number
//XXX: should we round the size as well? //XXX: should we round the size as well?
let position = position.round(); let position = rect.position.round();
let img_sz = UVec2::from(self.asset.size).as_vec2(); let img_sz = UVec2::from(self.asset.size).as_vec2();
@ -79,13 +83,13 @@ impl Frame for NinePatchFrame {
let size_h = ( let size_h = (
corners_image_px.top_left.x, corners_image_px.top_left.x,
parent_size.x - corners_image_px.top_left.x - (img_sz.x - corners_image_px.top_right.x), rect.size.x - corners_image_px.top_left.x - (img_sz.x - corners_image_px.top_right.x),
img_sz.x - corners_image_px.top_right.x, img_sz.x - corners_image_px.top_right.x,
); );
let size_v = ( let size_v = (
corners_image_px.top_left.y, corners_image_px.top_left.y,
parent_size.y - corners_image_px.top_left.y - (img_sz.y - corners_image_px.bottom_left.y), rect.size.y - corners_image_px.top_left.y - (img_sz.y - corners_image_px.bottom_left.y),
img_sz.y - corners_image_px.bottom_left.y, img_sz.y - corners_image_px.bottom_left.y,
); );

View file

@ -2,7 +2,7 @@ use glam::Vec2;
use crate::{ use crate::{
color, color,
draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList}, draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList},
rect::{Corners, FillColor}, rect::{Rect, Corners, FillColor},
}; };
use super::{Frame, point::FramePoint2d}; use super::{Frame, point::FramePoint2d};
@ -10,7 +10,7 @@ use super::{Frame, point::FramePoint2d};
/// ///
/// Can optionally be tinted, textured, and have rounded corners /// Can optionally be tinted, textured, and have rounded corners
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct FrameRect { pub struct RectFrame {
/// Background color of the frame\ /// Background color of the frame\
/// ///
/// If the container has a background texture, it will be multiplied by this color /// If the container has a background texture, it will be multiplied by this color
@ -35,26 +35,26 @@ pub struct FrameRect {
pub corner_radius: Corners<f32>, pub corner_radius: Corners<f32>,
} }
// impl<T: Into<FillColor>> From<T> for FrameRect { // impl<T: Into<FillColor>> From<T> for RectFrame {
// fn from(color: T) -> Self { // fn from(color: T) -> Self {
// Self::from_color(color) // Self::from_color(color)
// } // }
// } // }
impl From<FillColor> for FrameRect { impl From<FillColor> for RectFrame {
fn from(color: FillColor) -> Self { fn from(color: FillColor) -> Self {
Self::color(color) Self::color(color)
} }
} }
impl From<ImageHandle> for FrameRect { impl From<ImageHandle> for RectFrame {
fn from(image: ImageHandle) -> Self { fn from(image: ImageHandle) -> Self {
Self::image(image) Self::image(image)
} }
} }
impl FrameRect { impl RectFrame {
/// Create a new [`FrameRect`] with the given color /// Create a new [`RectFrame`] with the given color
pub fn color(color: impl Into<FillColor>) -> Self { pub fn color(color: impl Into<FillColor>) -> Self {
Self { Self {
color: color.into(), color: color.into(),
@ -62,7 +62,7 @@ impl FrameRect {
} }
} }
/// Create a new [`FrameRect`] with the given image\ /// Create a new [`RectFrame`] with the given image\
/// ///
/// Color will be set to [`WHITE`](crate::color::WHITE) to ensure the image is visible /// Color will be set to [`WHITE`](crate::color::WHITE) to ensure the image is visible
pub fn image(image: ImageHandle) -> Self { pub fn image(image: ImageHandle) -> Self {
@ -73,7 +73,7 @@ impl FrameRect {
} }
} }
/// Create a new [`FrameRect`] with the given color and image /// Create a new [`RectFrame`] with the given color and image
pub fn color_image(color: impl Into<FillColor>, image: ImageHandle) -> Self { pub fn color_image(color: impl Into<FillColor>, image: ImageHandle) -> Self {
Self { Self {
color: color.into(), color: color.into(),
@ -82,7 +82,7 @@ impl FrameRect {
} }
} }
/// Set the corner radius of the [`FrameRect`] /// Set the corner radius of the [`RectFrame`]
pub fn with_corner_radius(self, radius: impl Into<Corners<f32>>) -> Self { pub fn with_corner_radius(self, radius: impl Into<Corners<f32>>) -> Self {
Self { Self {
corner_radius: radius.into(), corner_radius: radius.into(),
@ -102,7 +102,7 @@ impl FrameRect {
} }
} }
impl Default for FrameRect { impl Default for RectFrame {
fn default() -> Self { fn default() -> Self {
Self { Self {
color: FillColor::transparent(), color: FillColor::transparent(),
@ -114,13 +114,13 @@ impl Default for FrameRect {
} }
} }
impl Frame for FrameRect { impl Frame for RectFrame {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
//TODO: handle bottom_right < top_left //TODO: handle bottom_right < top_left
let top_left = self.top_left.resolve(parent_size); let top_left = self.top_left.resolve(rect.size);
let bottom_right = self.bottom_right.resolve(parent_size); let bottom_right = self.bottom_right.resolve(rect.size);
draw.add(UiDrawCommand::Rectangle { draw.add(UiDrawCommand::Rectangle {
position: position + top_left, position: rect.position + top_left,
size: bottom_right - top_left, size: bottom_right - top_left,
color: self.color.corners(), color: self.color.corners(),
texture: self.image, texture: self.image,
@ -132,14 +132,14 @@ impl Frame for FrameRect {
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
self.top_left.x.absolute <= 0. &&
self.top_left.x.relative <= 0. && self.top_left.x.relative <= 0. &&
self.top_left.y.absolute <= 0. && self.top_left.x.absolute <= 0. &&
self.top_left.y.relative <= 0. && self.top_left.y.relative <= 0. &&
self.bottom_right.x.absolute >= 0. && self.top_left.y.absolute <= 0. &&
self.bottom_right.x.relative >= 1. && self.bottom_right.x.relative >= 1. &&
self.bottom_right.y.absolute >= 0. && self.bottom_right.x.absolute >= 0. &&
self.bottom_right.y.relative >= 1. && self.bottom_right.y.relative >= 1. &&
self.bottom_right.y.absolute >= 0. &&
self.color.is_opaque() && self.color.is_opaque() &&
self.image.is_none() && self.image.is_none() &&
self.corner_radius.max_f32() == 0. self.corner_radius.max_f32() == 0.

View file

@ -1,16 +1,15 @@
//! allows stacking two frames on top of each other //! allows stacking two frames on top of each other
use glam::Vec2; use crate::{draw::UiDrawCommandList, rect::Rect};
use crate::draw::UiDrawCommandList;
use super::Frame; use super::Frame;
/// A frame that draws two frames on top of each other /// A frame that draws two frames on top of each other
pub struct FrameStack(pub Box<dyn Frame>, pub Box<dyn Frame>); pub struct FrameStack(pub Box<dyn Frame>, pub Box<dyn Frame>);
impl Frame for FrameStack { impl Frame for FrameStack {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
self.0.draw(draw, position, parent_size); self.0.draw(draw, rect);
self.1.draw(draw, position, parent_size); self.1.draw(draw, rect);
} }
fn covers_opaque(&self) -> bool { fn covers_opaque(&self) -> bool {
@ -22,10 +21,17 @@ impl Frame for FrameStack {
pub trait FrameStackExt: Frame { pub trait FrameStackExt: Frame {
/// Stack another frame on top of this one /// Stack another frame on top of this one
fn stack(self, other: impl Frame + 'static) -> FrameStack; fn stack(self, other: impl Frame + 'static) -> FrameStack;
/// Stack another frame below this one
fn stack_bottom(self, other: impl Frame + 'static) -> FrameStack;
} }
impl<T: Frame + 'static> FrameStackExt for T { impl<T: Frame + 'static> FrameStackExt for T {
fn stack(self, other: impl Frame + 'static) -> FrameStack { fn stack(self, other: impl Frame + 'static) -> FrameStack {
FrameStack(Box::new(self), Box::new(other)) FrameStack(Box::new(self), Box::new(other))
} }
fn stack_bottom(self, other: impl Frame + 'static) -> FrameStack {
FrameStack(Box::new(other), Box::new(self))
}
} }

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

@ -75,11 +75,11 @@ macro_rules! size {
}; };
} }
/// Helper macro for constructing a `FrameRect` /// Helper macro for constructing a `RectFrame`
/// ///
/// # Example: /// # Example:
/// ``` /// ```
/// frame_rect! { /// _frame! {
/// color: (0.2, 0.2, 0.3, 1.), /// color: (0.2, 0.2, 0.3, 1.),
/// corner_radius: 5., /// corner_radius: 5.,
/// }; /// };
@ -89,24 +89,24 @@ macro_rules! size {
/// - If the `image` field is set, but not `color`, the `color` field will default to [`WHITE`](crate::color::WHITE) (to ensure visibility) /// - If the `image` field is set, but not `color`, the `color` field will default to [`WHITE`](crate::color::WHITE) (to ensure visibility)
/// - If both `color` and `image` are not set, the `color` field will default to [`TRANSPARENT`](crate::color::TRANSPARENT) /// - If both `color` and `image` are not set, the `color` field will default to [`TRANSPARENT`](crate::color::TRANSPARENT)
#[macro_export] #[macro_export]
macro_rules! frame_rect { macro_rules! rect_frame {
{} => { {} => {
$crate::frame::FrameRect::default() $crate::frame::RectFrame::default()
}; };
// () => { // () => {
// $crate::frame::FrameRect::default() // $crate::frame::RectFrame::default()
// }; // };
($expr:expr) => { ($expr:expr) => {
{ {
let _frame_rect: $crate::frame::FrameRect = $crate::frame::FrameRect::from($expr); let __frame: $crate::frame::RectFrame = $crate::frame::RectFrame::from($expr);
_frame_rect __frame
} }
}; };
($image:expr, $color:expr) => { ($image:expr, $color:expr) => {
$crate::frame::FrameRect::color_image($color, $image) $crate::frame::RectFrame::color_image($color, $image)
}; };
{$($ident:ident : $expr:expr),+$(,)?} => { {$($ident:ident : $expr:expr),+$(,)?} => {
@ -115,58 +115,24 @@ macro_rules! frame_rect {
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
{$(const $ident: () = ();)+} {$(const $ident: () = ();)+}
// construct the FrameRect // construct the RectFrame
{ {
let mut frame_rect = $crate::frame::FrameRect::default(); let mut _frame = $crate::frame::RectFrame::default();
let mut _color_is_set = false; let mut _color_is_set = false;
let mut _image_is_set = false; let mut _image_is_set = false;
$( $(
{ {
frame_rect.$ident = ($expr).into(); _frame.$ident = ($expr).into();
if stringify!($ident) == "image" { _image_is_set |= stringify!($ident) == "image";
_image_is_set = true; _color_is_set |= stringify!($ident) == "color";
}
if stringify!($ident) == "color" {
_color_is_set = true;
}
} }
)+ )+
if frame_rect.image.is_some() && _image_is_set && !_color_is_set { // set color to white if image is explicitly set to Some(...) but color is left as the default
frame_rect.color = (1., 1., 1., 1.).into(); if _frame.image.is_some() && _image_is_set && !_color_is_set {
_frame.color = (1., 1., 1., 1.).into();
} }
frame_rect _frame
} }
} }
}; };
// {$from:expr, $($ident:ident : $expr:expr),+$(,)?} => {
// {
// // ensure all identifiers are unique
// #[allow(non_upper_case_globals)]
// {
// $(
// const $ident: () = ();
// )+
// }
// // construct the FrameRect
// {
// let mut _frame_rect: $crate::frame::FrameRect = ($from).into();
// $(
// let $ident = ($expr).into();
// _frame_rect.$ident = $ident;
// )+
// _frame_rect
// }
// }
// };
} }
// #[allow(unused)]
// fn test() {
// // let _ = frame_rect!(5, 6);
// let _ = frame_rect! {
// color: (0.2, 0.2, 0.3, 1.),
// corner_radius: 5.,
// };
// }

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,58 @@ 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)
}
}
impl From<(Vec2, Vec2)> for Rect {
/// Create a new `Rect` from a tuple of two `Vec2`s, where the first `Vec2` is the position and the second `Vec2` is the size.
fn from((position, size): (Vec2, Vec2)) -> Self {
Self { position, size }
}
}
impl From<(f32, f32, f32, f32)> for Rect {
/// Create a new `Rect` from a tuple of 4 `f32`s, where the first two `f32`s are the x and y positions of the top-left corner and the last two `f32`s are the width and height of the rect respectively.
fn from((x, y, width, height): (f32, f32, f32, f32)) -> Self {
Self {
position: Vec2::new(x, y),
size: Vec2::new(width, height),
}
}
}
impl From<[f32; 4]> for Rect {
/// Create a new `Rect` from an array of 4 `f32`s, where the first two `f32`s are the x and y positions of the top-left corner and the last two `f32`s are the width and height of the rect respectively.
fn from([x, y, width, height]: [f32; 4]) -> Self {
Self {
position: Vec2::new(x, y),
size: Vec2::new(width, height),
}
}
}
impl From<Rect> for (Vec2, Vec2) {
/// Convert a `Rect` into a tuple of two `Vec2`s, where the first `Vec2` is the position and the second `Vec2` is the size.
fn from(rect: Rect) -> Self {
(rect.position, rect.size)
}
}
impl From<Rect> for (f32, f32, f32, f32) {
/// Convert a `Rect` into a tuple of 4 `f32`s, where the first two `f32`s are the x and y positions of the top-left corner and the last two `f32`s are the width and height of the rect respectively.
fn from(rect: Rect) -> Self {
(rect.position.x, rect.position.y, rect.size.x, rect.size.y)
}
}
impl From<Rect> for [f32; 4] {
/// Convert a `Rect` into an array of 4 `f32`s, where the first two `f32`s are the x and y positions of the top-left corner and the last two `f32`s are the width and height of the rect respectively.
fn from(rect: Rect) -> Self {
[rect.position.x, rect.position.y, rect.size.x, rect.size.y]
}
}