Compare commits

..

No commits in common. "2a4af1aa352f444edff4ae3e694d32055086faec" and "fb2f3c739e85e27a758e6372b6063d97ae101ef5" have entirely different histories.

20 changed files with 336 additions and 393 deletions

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, progress_bar::ProgressBar, fill_rect::FillRect, ElementList, UiElement
}, frame::FrameRect, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance }, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance
}; };
use hui_glium::GliumUiRenderer; use hui_glium::GliumUiRenderer;
@ -95,7 +95,7 @@ fn main() {
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: vec4(0., 0., 0., 0.5).into(),
direction: Direction::Horizontal, direction: Direction::Horizontal,
children: { children: {
let mut x: Vec<Box<dyn UiElement>> = vec![]; let mut x: Vec<Box<dyn UiElement>> = vec![];
@ -115,18 +115,19 @@ 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: vec4(1., 0., 0., 1.).into(),
top_left: 0.,
top_right: 30.,
bottom_left: 0.,
bottom_right: 0.,
})),
padding: Sides { padding: Sides {
top: 10., top: 10.,
bottom: 20., bottom: 20.,
left: 30., left: 30.,
right: 40., right: 40.,
}, },
corner_radius: Corners {
top_left: 0.,
top_right: 30.,
bottom_left: 0.,
bottom_right: 0.,
},
children: ElementList(vec![ children: ElementList(vec![
Box::new(FillRect { Box::new(FillRect {
size: (Size::Absolute(50.), Size::Absolute(50.)).into(), size: (Size::Absolute(50.), Size::Absolute(50.)).into(),

View file

@ -1,11 +1,13 @@
use std::time::Instant; use std::time::Instant;
use hui::{ use hui::{
size,
layout::{Alignment, Direction},
element::{ element::{
container::Container, container::Container,
progress_bar::ProgressBar, progress_bar::ProgressBar,
text::Text, text::Text,
UiElementExt, UiElementExt,
}, frame::FrameRect, frame_rect, layout::{Alignment, Direction}, size },
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -31,10 +33,8 @@ 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((0.2, 0.2, 0.5))
color: (0.2, 0.2, 0.5), .with_corner_radius(8.)
corner_radius: 8.
})
.with_children(|ui| { .with_children(|ui| {
if instant.elapsed().as_secs_f32() < 5. { if instant.elapsed().as_secs_f32() < 5. {
Text::default() Text::default()

View file

@ -0,0 +1,141 @@
//WARNING: THIS EXAMPLE IS EXTREMELY OUTDATED AND USES DEPRECATED API
use glam::{vec4, UVec2};
use glium::{backend::glutin::SimpleWindowBuilder, Surface};
use winit::{
event::{Event, WindowEvent},
event_loop::{EventLoopBuilder, ControlFlow}
};
use hui::{
element::{
container::Container,
text::Text, ElementList
},
layout::{Alignment, Direction, Size},
rect::{Corners, Sides},
UiInstance
};
use hui_glium::GliumUiRenderer;
fn main() {
kubi_logging::init();
let event_loop = EventLoopBuilder::new().build().unwrap();
let (_window, display) = SimpleWindowBuilder::new().build(&event_loop);
let mut hui = UiInstance::new();
let mut backend = GliumUiRenderer::new(&display);
event_loop.run(|event, window_target| {
window_target.set_control_flow(ControlFlow::Poll);
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
window_target.exit();
},
Event::AboutToWait => {
let mut frame = display.draw();
frame.clear_color_srgb(0.5, 0.5, 0.5, 0.);
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
hui.begin();
hui.add(Container {
gap: 10.,
align: Alignment::Center.into(),
size: (Size::Relative(1.), Size::Relative(1.)).into(),
children: ElementList(vec![
Box::new(Container {
align: Alignment::Center.into(),
size: (Size::Relative(0.5), Size::Relative(0.5)).into(),
background: vec4(1., 0., 0., 1.).into(),
corner_radius: Corners {
top_left: 10.,
top_right: 20.,
bottom_left: 50.,
bottom_right: 80.
},
children: ElementList(vec![
Box::new(Container {
padding: Sides::all(20.),
direction: Direction::Horizontal,
align: Alignment::Center.into(),
size: (Size::Auto, Size::Auto).into(),
background: vec4(0.1, 0.1, 0.1, 0.5).into(),
corner_radius: Corners::all(8.),
children: ElementList(vec![
Box::new(Text {
text: "Corners".into(),
text_size: 50,
color: vec4(1., 1., 1., 1.),
..Default::default()
}),
Box::new(Text {
text: "!".into(),
text_size: 50,
color: vec4(1., 1., 0., 1.),
..Default::default()
}),
]),
..Default::default()
}),
]),
..Default::default()
}),
Box::new(Container {
gap: 10.,
direction: Direction::Horizontal,
children: ElementList(vec![
Box::new(Container {
size: (Size::Absolute(100.), Size::Absolute(100.)).into(),
background: Corners::left_right(
vec4(1., 0., 0., 1.),
vec4(0., 1., 0., 1.)
).into(),
corner_radius: Corners::all(0.),
..Default::default()
}),
Box::new(Container {
size: (Size::Absolute(100.), Size::Absolute(100.)).into(),
background: Corners::left_right(
vec4(1., 0., 0., 1.),
vec4(0., 1., 0., 1.)
).into(),
corner_radius: Corners::all(10.),
..Default::default()
}),
Box::new(Container {
size: (Size::Absolute(100.), Size::Absolute(100.)).into(),
background: Corners::left_right(
vec4(1., 0., 0., 1.),
vec4(0., 1., 0., 1.)
).into(),
corner_radius: Corners::all(20.),
..Default::default()
}),
Box::new(Container {
size: (Size::Absolute(100.), Size::Absolute(100.)).into(),
background: Corners::left_right(
vec4(1., 0., 0., 1.),
vec4(0., 1., 0., 1.)
).into(),
corner_radius: Corners::all(30.),
..Default::default()
}),
]),
..Default::default()
}),
]),
..Default::default()
}, resolution);
hui.end();
backend.update(&hui);
backend.draw(&mut frame, resolution);
frame.finish().unwrap();
}
_ => (),
}
}).unwrap();
}

View file

@ -10,7 +10,7 @@ use winit::{
use hui::{ use hui::{
element::{ element::{
container::Container, fill_rect::FillRect, spacer::Spacer, text::Text, ElementList container::Container, fill_rect::FillRect, spacer::Spacer, text::Text, ElementList
}, frame::FrameRect, layout::Size, UiInstance }, 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: vec4(0.1, 0.1, 0.1, 1.).into(),
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(),

View file

@ -1,5 +1,7 @@
use hui::{ use hui::{
color, element::{container::Container, text::Text, UiElementExt}, frame::FrameRect, layout::Alignment, size color, size,
layout::Alignment,
element::{UiElementExt, container::Container, text::Text},
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -12,10 +14,8 @@ 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(
FrameRect::color(color::WHITE)
.with_corner_radius(10.) .with_corner_radius(10.)
) .with_background(color::WHITE)
.with_children(|ui| { .with_children(|ui| {
Text::default() Text::default()
.with_text("Hello, world") .with_text("Hello, world")
@ -24,10 +24,8 @@ ui_main!(|ui, size, _| {
.add_child(ui); .add_child(ui);
Container::default() Container::default()
.with_padding((10., 20.)) .with_padding((10., 20.))
.with_background(
FrameRect::color(color::DARK_RED)
.with_corner_radius((2.5, 30., 2.5, 2.5)) .with_corner_radius((2.5, 30., 2.5, 2.5))
) .with_background(color::DARK_RED)
.with_children(|ui| { .with_children(|ui| {
Text::default() Text::default()
.with_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.") .with_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")

View file

@ -1,17 +1,10 @@
use glam::vec4; use glam::vec4;
use hui::{ use hui::{
size, frame_rect, color, size,
color, element::{container::Container, progress_bar::ProgressBar, text::Text, UiElementExt},
element::{
container::Container,
progress_bar::ProgressBar,
text::Text,
UiElementExt
},
frame::FrameRect,
layout::Alignment, layout::Alignment,
rect::Corners, rect::Corners,
text::FontHandle text::FontHandle,
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -45,10 +38,8 @@ ui_main!(
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_padding((10., 15.)) .with_padding((10., 15.))
.with_background(
FrameRect::color((0., 0., 0., 0.5))
.with_corner_radius(8.) .with_corner_radius(8.)
) .with_background((0., 0., 0., 0.5))
.with_children(|ui| { .with_children(|ui| {
let flash = 1. - 0.5 * (4. * instant.elapsed().as_secs_f32()).sin().powi(2); let flash = 1. - 0.5 * (4. * instant.elapsed().as_secs_f32()).sin().powi(2);
Text::default() Text::default()
@ -70,10 +61,8 @@ 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_corner_radius(8.)
color: (0., 0., 0., 0.5), .with_background((0., 0., 0., 0.5))
corner_radius: 8.,
})
.with_children(|ui| { .with_children(|ui| {
Text::default() Text::default()
.with_text("Did you know?") .with_text("Did you know?")
@ -111,10 +100,8 @@ ui_main!(
.with_children(|ui| { .with_children(|ui| {
Container::default() Container::default()
.with_padding(10.) .with_padding(10.)
.with_background(
FrameRect::color((0., 0., 0., 0.5))
.with_corner_radius(8.) .with_corner_radius(8.)
) .with_background((0., 0., 0., 0.5))
.with_children(|ui| { .with_children(|ui| {
Text::default() Text::default()
.with_text("Level 5") .with_text("Level 5")

View file

@ -7,7 +7,11 @@ use hui::{
text::Text, text::Text,
transformer::ElementTransformExt, transformer::ElementTransformExt,
UiElementExt UiElementExt
}, frame::FrameRect, frame_rect, layout::Alignment, rect::Corners, size, text::FontHandle },
layout::Alignment,
rect::Corners,
text::FontHandle,
size,
}; };
#[path = "../boilerplate.rs"] #[path = "../boilerplate.rs"]
@ -37,10 +41,8 @@ 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_corner_radius(8.)
color: (0., 0., 0., 0.5), .with_background((0., 0., 0., 0.5))
corner_radius: 8.
})
.with_children(|ui| { .with_children(|ui| {
Text::default() Text::default()
.with_text("Did you know?") .with_text("Did you know?")

View file

@ -1,16 +1,15 @@
use hui::{ use hui::{
draw::TextureFormat, draw::TextureFormat,
signal::Signal,
layout::{Alignment, Direction},
element::{ element::{
br::Break,
container::Container, container::Container,
image::Image,
slider::Slider,
text::Text, text::Text,
image::Image,
br::Break,
slider::Slider,
UiElementExt, UiElementExt,
}, },
frame::FrameRect,
layout::{Alignment, Direction},
signal::Signal,
size, size,
}; };

View file

@ -26,7 +26,7 @@ document-features = "0.2"
derive_setters = "0.1" derive_setters = "0.1"
derive_more = "0.99" derive_more = "0.99"
tinyset = "0.4" tinyset = "0.4"
#enum_dispatch = "0.3" enum_dispatch = "0.3"
[features] [features]
default = ["builtin_elements", "builtin_font", "pixel_perfect_text"] default = ["builtin_elements", "builtin_font", "pixel_perfect_text"]

View file

@ -3,7 +3,6 @@ use crate::rect::Corners;
//TODO uneven corners (separate width/height for each corner) //TODO uneven corners (separate width/height for each corner)
/// Calculate the number of points based on the maximum corner radius
fn point_count(corners: Corners<f32>) -> NonZeroU16 { fn point_count(corners: Corners<f32>) -> NonZeroU16 {
//Increase for higher quality //Increase for higher quality
const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5; const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5;
@ -12,31 +11,19 @@ fn point_count(corners: Corners<f32>) -> NonZeroU16 {
).unwrap() ).unwrap()
} }
/// Low-level options for rendering rounded corners
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RoundedCorners { pub struct RoundedCorners {
/// Corner radius of each corner
pub radius: Corners<f32>, pub radius: Corners<f32>,
/// Number of points to use for each corner
///
/// This value affects all corners, regardless of their individual radius
pub point_count: NonZeroU16, pub point_count: NonZeroU16,
} }
impl From<Corners<f32>> for RoundedCorners { impl From<Corners<f32>> for RoundedCorners {
/// Create a new `RoundedCorners` from [`Corners<f32>`](crate::rect::Corners)
///
/// Point count will be calculated automatically based on the maximum radius
fn from(radius: Corners<f32>) -> Self { fn from(radius: Corners<f32>) -> Self {
Self::from_radius(radius) Self::from_radius(radius)
} }
} }
impl RoundedCorners { impl RoundedCorners {
/// Create a new `RoundedCorners` from [`Corners<f32>`](crate::rect::Corners)
///
/// Point count will be calculated automatically based on the maximum radius
pub fn from_radius(radius: Corners<f32>) -> Self { pub fn from_radius(radius: Corners<f32>) -> Self {
Self { Self {
radius, radius,

View file

@ -3,13 +3,18 @@
use derive_setters::Setters; use derive_setters::Setters;
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use crate::{ use crate::{
draw::{ImageHandle, RoundedCorners, UiDrawCommand},
element::{ElementList, MeasureContext, ProcessContext, UiElement}, element::{ElementList, MeasureContext, ProcessContext, UiElement},
layout::{Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d}, layout::{Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d},
frame::{Frame, FrameRect},
measure::{Hints, Response}, measure::{Hints, Response},
rect::{Sides, FillColor}, rect::{Corners, FillColor, Sides},
}; };
// pub struct Border {
// pub color: Vec4,
// pub width: f32,
// }
//XXX: add Order/Direction::Forward/Reverse or sth? //XXX: add Order/Direction::Forward/Reverse or sth?
//TODO: clip children flag //TODO: clip children flag
//TODO: borders //TODO: borders
@ -49,8 +54,26 @@ pub struct Container {
#[setters(into)] #[setters(into)]
pub align: Alignment2d, pub align: Alignment2d,
#[setters(skip)] /// Background color of the container\
pub background_frame: Box<dyn Frame>, ///
/// If the container has a background texture, it will be multiplied by this color
#[setters(into)]
pub background: FillColor,
/// Background texture of the container
///
/// Can be used in conjunction with the background color\
/// In this case, the texture will be shaded by the color
///
/// Please note that if the background color is NOT set (or set to transparent), the texture will NOT be visible\
/// This is because the texture is multiplied by the color, and if the color is transparent, the texture will be too\
//TODO: fix this flaw, if background_image is called for the first time, bg wasnt explicitly set and background is transparent, set it to white
#[setters(into)]
pub background_image: Option<ImageHandle>,
/// Corner radius of the background rectangle
#[setters(into)]
pub corner_radius: Corners<f32>,
/// Set this to `true` to allow the elements wrap automatically /// Set this to `true` to allow the elements wrap automatically
/// ///
@ -70,11 +93,6 @@ impl Container {
self.children.0.extend(ElementList::from_callback(ui).0); self.children.0.extend(ElementList::from_callback(ui).0);
self self
} }
pub fn with_background(mut self, frame: impl Frame + 'static) -> Self {
self.background_frame = Box::new(frame);
self
}
} }
impl Default for Container { impl Default for Container {
@ -85,9 +103,11 @@ 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: FillColor::transparent(),
wrap: false, background_image: None,
children: ElementList(Vec::new()), children: ElementList(Vec::new()),
wrap: false,
corner_radius: Corners::all(0.),
} }
} }
} }
@ -308,20 +328,18 @@ impl UiElement for Container {
let mut position = ctx.layout.position; let mut position = ctx.layout.position;
//background //background
// if !self.background.is_transparent() { if !self.background.is_transparent() {
// let corner_colors = self.background.corners(); let corner_colors = self.background.corners();
// ctx.draw.add(UiDrawCommand::Rectangle { ctx.draw.add(UiDrawCommand::Rectangle {
// position, position,
// size: ctx.measure.size, size: ctx.measure.size,
// color: corner_colors, color: corner_colors,
// texture: self.background_image, texture: self.background_image,
// rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({ rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
// RoundedCorners::from_radius(self.corner_radius) RoundedCorners::from_radius(self.corner_radius)
// }), }),
// }); });
// } }
self.background_frame.draw(ctx.draw, ctx.layout.position, ctx.measure.size);
//padding //padding
position += vec2(self.padding.left, self.padding.top); position += vec2(self.padding.left, self.padding.top);

View file

@ -16,8 +16,6 @@ use crate::{
//TODO: use state for slider? //TODO: use state for slider?
// ^ useful if the user only hanldes the drag end event or has large step sizes with relative mode // ^ useful if the user only hanldes the drag end event or has large step sizes with relative mode
//TODO: adopt frame api here
/// Follow mode for the slider /// Follow mode for the slider
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
pub enum SliderFollowMode { pub enum SliderFollowMode {

View file

@ -1,13 +1,67 @@
pub mod point;
pub mod layer;
use glam::Vec2; use glam::Vec2;
use layer::{FrameLayer, FrameLayerImpl};
use crate::draw::UiDrawCommandList; use crate::draw::UiDrawCommandList;
pub mod point; ///XXX: this is not used yet, and also kinda a mess, simplify?
mod rect; ///Maybe limit to a single layer? (aka `Frame` will be just one of the options)
pub mod stack; ///Because currently, this is just a duplicate of the dormal draw command system, but with a different name...
mod impls; ///Then, there's no need for the positioning stuff too, which is a bit overkill and is kinda code duplication too!
///aka Frame::Rectangle, Frame::NinePatch, ...
pub use rect::FrameRect; /// A frame, which can contain multiple layers
///
pub trait Frame { /// Use these to construct complex backgrounds
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2); #[derive(Default, Clone)]
pub struct Frame {
/// Layers of the frame
layers: Vec<FrameLayer>
}
impl<T: Into<FrameLayer>> From<T> for Frame {
fn from(layer: T) -> Self {
let mut frame = Self::default();
frame.add(layer.into());
frame
}
}
impl Frame {
/// Get the layer with the given index
#[inline]
pub fn layer(&self, index: usize) -> Option<&FrameLayer> {
self.layers.get(index)
}
/// Get a mutable reference to the layer with the given index
#[inline]
pub fn layer_mut(&mut self, index: usize) -> Option<&mut FrameLayer> {
self.layers.get_mut(index)
}
/// Add a layer to the frame
#[inline]
pub fn add(&mut self, layer: impl Into<FrameLayer>) -> &mut Self {
self.layers.push(layer.into());
self
}
/// Add a layer to the back of the frame
#[inline]
pub fn add_back(&mut self, layer: impl Into<FrameLayer>) -> &mut Self {
self.layers.insert(0, layer.into());
self
}
#[inline]
pub fn finish(&mut self) -> Self {
self.clone()
}
pub(crate) fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
for layer in &self.layers {
layer.draw(draw, position, parent_size);
}
}
} }

View file

@ -1,125 +0,0 @@
use glam::{Vec2, Vec3, Vec4};
use super::Frame;
use crate::{
color,
draw::{ImageHandle, UiDrawCommand, UiDrawCommandList},
rect::{Corners, FillColor},
};
impl Frame for ImageHandle {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
draw.add(UiDrawCommand::Rectangle {
position,
size: parent_size,
color: color::WHITE.into(),
texture: Some(*self),
rounded_corners: None,
})
}
}
impl Frame for FillColor {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
draw.add(UiDrawCommand::Rectangle {
position,
size: parent_size,
color: self.corners(),
texture: None,
rounded_corners: None,
})
}
}
// impl for various types resembling colors
// Corners (RGBA):
impl Frame for Corners<Vec4> {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for (Vec4, Vec4, Vec4, Vec4) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
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) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for [[f32; 4]; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
// Corners (RGB):
impl Frame for Corners<Vec3> {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for (Vec3, Vec3, Vec3, Vec3) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
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) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for [[f32; 3]; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
// RGBA:
impl Frame for Vec4 {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for (f32, f32, f32, f32) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for [f32; 4] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
// RGB:
impl Frame for Vec3 {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for (f32, f32, f32) {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}
impl Frame for [f32; 3] {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
FillColor::from(*self).draw(draw, position, parent_size)
}
}

View file

@ -1,13 +1,25 @@
use glam::Vec2; use glam::Vec2;
use enum_dispatch::enum_dispatch;
use crate::{ use crate::{
color, color,
draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList}, draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList},
rect::{Corners, FillColor}, rect::{Corners, FillColor},
}; };
use super::{Frame, point::FramePoint2d}; use super::point::FramePoint2d;
#[enum_dispatch]
pub(crate) trait FrameLayerImpl {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2);
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct FrameRect { #[enum_dispatch(FrameLayerImpl)]
pub enum FrameLayer {
Rect(RectFrame),
}
#[derive(Clone, Copy)]
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
@ -32,35 +44,29 @@ 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 {
fn from(color: FillColor) -> Self {
Self::color(color)
} }
} }
impl From<ImageHandle> for FrameRect { impl RectFrame {
fn from(image: ImageHandle) -> Self { pub fn from_color(color: impl Into<FillColor>) -> Self {
Self::image(image)
}
}
impl FrameRect {
/// Create a new [`FrameRect`] with the given color
pub fn color(color: impl Into<FillColor>) -> Self {
Self { Self {
color: color.into(), color: color.into(),
..Self::default() ..Self::default()
} }
} }
/// Create a new [`FrameRect`] with the given image pub fn from_color_rounded(color: impl Into<FillColor>, corner_radius: impl Into<Corners<f32>>) -> Self {
pub fn image(image: ImageHandle) -> Self { Self {
color: color.into(),
corner_radius: corner_radius.into(),
..Self::default()
}
}
pub fn from_image(image: ImageHandle) -> Self {
Self { Self {
color: color::WHITE.into(), color: color::WHITE.into(),
image: Some(image), image: Some(image),
@ -68,8 +74,7 @@ impl FrameRect {
} }
} }
/// Create a new [`FrameRect`] with the given color and image pub fn from_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(),
image: Some(image), image: Some(image),
@ -77,18 +82,17 @@ impl FrameRect {
} }
} }
/// Set the corner radius of the [`FrameRect`] pub fn from_color_image_rounded(color: impl Into<FillColor>, image: ImageHandle, corner_radius: impl Into<Corners<f32>>) -> Self {
pub fn with_corner_radius(self, radius: impl Into<Corners<f32>>) -> Self {
Self { Self {
corner_radius: radius.into(), color: color.into(),
..self image: Some(image),
corner_radius: corner_radius.into(),
..Self::default()
} }
} }
//TODO: deprecate and replace /// Inset the rectangle by the given amount
pub fn inset(self, inset: f32) -> Self {
/// Inset the rectangle by the given amount in pixels
pub fn with_inset(self, inset: f32) -> Self {
Self { Self {
top_left: self.top_left + Vec2::splat(inset).into(), top_left: self.top_left + Vec2::splat(inset).into(),
bottom_right: self.bottom_right - Vec2::splat(inset).into(), bottom_right: self.bottom_right - Vec2::splat(inset).into(),
@ -97,10 +101,10 @@ impl FrameRect {
} }
} }
impl Default for FrameRect { impl Default for RectFrame {
fn default() -> Self { fn default() -> Self {
Self { Self {
color: FillColor::transparent(), color: FillColor::default(),
image: None, image: None,
top_left: FramePoint2d::TOP_LEFT, top_left: FramePoint2d::TOP_LEFT,
bottom_right: FramePoint2d::BOTTOM_RIGHT, bottom_right: FramePoint2d::BOTTOM_RIGHT,
@ -109,7 +113,7 @@ impl Default for FrameRect {
} }
} }
impl Frame for FrameRect { impl FrameLayerImpl for RectFrame {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) { fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
//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(parent_size);

View file

@ -43,14 +43,14 @@ impl FramePoint {
/// Center of the frame axis /// Center of the frame axis
pub const CENTER: Self = Self { pub const CENTER: Self = Self {
absolute: 0.0, absolute: 0.5,
relative: 0.5, relative: 0.0,
}; };
/// End of the frame axis /// End of the frame axis
pub const END: Self = Self { pub const END: Self = Self {
absolute: 0.0, absolute: 1.0,
relative: 1.0, relative: 0.0,
}; };
/// Create a new absolutely positioned `FramePoint` /// Create a new absolutely positioned `FramePoint`

View file

@ -1,22 +0,0 @@
use glam::Vec2;
use crate::draw::UiDrawCommandList;
use super::Frame;
pub struct FrameStack(pub Box<dyn Frame>, pub Box<dyn Frame>);
impl Frame for FrameStack {
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
self.0.draw(draw, position, parent_size);
self.1.draw(draw, position, parent_size);
}
}
pub trait FrameStackExt: Frame {
fn stack(self, other: impl Frame + 'static) -> FrameStack;
}
impl<T: Frame + 'static> FrameStackExt for T {
fn stack(self, other: impl Frame + 'static) -> FrameStack {
FrameStack(Box::new(self), Box::new(other))
}
}

View file

@ -60,87 +60,3 @@ macro_rules! size {
} }
}; };
} }
/// Helper macro for constructing a `FrameRect`
#[macro_export]
macro_rules! frame_rect {
{} => {
$crate::frame::FrameRect::default()
};
// () => {
// $crate::frame::FrameRect::default()
// };
($expr:expr) => {
{
let _frame_rect: $crate::frame::FrameRect = $crate::frame::FrameRect::from($expr);
_frame_rect
}
};
($image:expr, $color:expr) => {
$crate::frame::FrameRect::color_image($color, $image)
};
{$($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::default();
let mut _color_is_set = false;
let mut _image_is_set = false;
$(
{
frame_rect.$ident = ($expr).into();
if stringify!($ident) == "image" {
_image_is_set = true;
}
if stringify!($ident) == "color" {
_color_is_set = true;
}
}
)+
if frame_rect.image.is_some() && _image_is_set && !_color_is_set {
frame_rect.color = (1., 1., 1., 1.).into();
}
frame_rect
}
}
};
// {$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

@ -153,17 +153,6 @@ impl From<[[f32; 4]; 4]> for FillColor {
} }
} }
impl From<Corners<Vec3>> for FillColor {
fn from(corners: Corners<Vec3>) -> Self {
Self(Corners {
top_left: corners.top_left.extend(1.),
top_right: corners.top_right.extend(1.),
bottom_left: corners.bottom_left.extend(1.),
bottom_right: corners.bottom_right.extend(1.),
})
}
}
impl From<(Vec3, Vec3, Vec3, Vec3)> for FillColor { impl From<(Vec3, Vec3, Vec3, Vec3)> for FillColor {
fn from((top_left, top_right, bottom_left, bottom_right): (Vec3, Vec3, Vec3, Vec3)) -> Self { fn from((top_left, top_right, bottom_left, bottom_right): (Vec3, Vec3, Vec3, Vec3)) -> Self {
Self(Corners { Self(Corners {

View file

@ -8,7 +8,6 @@ mod font;
mod ftm; mod ftm;
mod stack; mod stack;
/// Built-in font handle
#[cfg(feature="builtin_font")] #[cfg(feature="builtin_font")]
pub use font::BUILTIN_FONT; pub use font::BUILTIN_FONT;
pub use font::FontHandle; pub use font::FontHandle;
@ -18,7 +17,7 @@ use ftm::FontTextureManager;
use ftm::GlyphCacheEntry; use ftm::GlyphCacheEntry;
use stack::FontStack; use stack::FontStack;
pub(crate) struct TextRenderer { pub struct TextRenderer {
manager: FontManager, manager: FontManager,
ftm: FontTextureManager, ftm: FontTextureManager,
stack: FontStack, stack: FontStack,
@ -64,18 +63,15 @@ impl Default for TextRenderer {
} }
} }
/// Size of measured text
pub struct TextMeasureResponse { pub struct TextMeasureResponse {
pub max_width: f32, pub max_width: f32,
pub height: f32, pub height: f32,
} }
/// Context for measuring text
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct TextMeasure<'a>(&'a TextRenderer); pub struct TextMeasure<'a>(&'a TextRenderer);
impl<'a> TextMeasure<'a> { impl<'a> TextMeasure<'a> {
/// Measure the given string of text with the given font and size
pub fn measure(&self, font: FontHandle, size: u16, text: &str) -> TextMeasureResponse { pub fn measure(&self, font: FontHandle, size: u16, text: &str) -> TextMeasureResponse {
use fontdue::layout::{Layout, CoordinateSystem, TextStyle}; use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
let mut layout = Layout::new(CoordinateSystem::PositiveYDown); let mut layout = Layout::new(CoordinateSystem::PositiveYDown);