mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-21 22:58:42 -06:00
Compare commits
14 commits
fb2f3c739e
...
2a4af1aa35
Author | SHA1 | Date | |
---|---|---|---|
griffi-gh | 2a4af1aa35 | ||
griffi-gh | 8a52fe1d67 | ||
griffi-gh | 7a64a6b750 | ||
griffi-gh | 9ba0a7e762 | ||
griffi-gh | 91668e575c | ||
griffi-gh | b89a277aa9 | ||
griffi-gh | f5f3dd9ad3 | ||
griffi-gh | 03a49791fa | ||
griffi-gh | d2598f8a20 | ||
griffi-gh | d44a2bae53 | ||
griffi-gh | 3c680ea294 | ||
griffi-gh | 899774a7e1 | ||
griffi-gh | 9acdacaa32 | ||
griffi-gh | f8b80040f3 |
|
@ -9,8 +9,8 @@ use winit::{
|
|||
};
|
||||
use hui::{
|
||||
element::{
|
||||
container::Container, progress_bar::ProgressBar, fill_rect::FillRect, ElementList, UiElement
|
||||
}, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance
|
||||
container::Container, fill_rect::FillRect, progress_bar::ProgressBar, ElementList, UiElement
|
||||
}, frame::FrameRect, layout::{Alignment, Direction, Size}, rect::{Corners, Sides}, UiInstance
|
||||
};
|
||||
use hui_glium::GliumUiRenderer;
|
||||
|
||||
|
@ -95,7 +95,7 @@ fn main() {
|
|||
Box::new(Container {
|
||||
gap: 5.,
|
||||
padding: Sides::all(5.),
|
||||
background: vec4(0., 0., 0., 0.5).into(),
|
||||
background_frame: Box::new(FrameRect::color(vec4(0., 0., 0., 0.5))),
|
||||
direction: Direction::Horizontal,
|
||||
children: {
|
||||
let mut x: Vec<Box<dyn UiElement>> = vec![];
|
||||
|
@ -115,19 +115,18 @@ fn main() {
|
|||
..Default::default()
|
||||
}),
|
||||
Box::new(Container {
|
||||
background: vec4(1., 0., 0., 1.).into(),
|
||||
background_frame: Box::new(FrameRect::color((1., 0., 0.)).with_corner_radius(Corners {
|
||||
top_left: 0.,
|
||||
top_right: 30.,
|
||||
bottom_left: 0.,
|
||||
bottom_right: 0.,
|
||||
})),
|
||||
padding: Sides {
|
||||
top: 10.,
|
||||
bottom: 20.,
|
||||
left: 30.,
|
||||
right: 40.,
|
||||
},
|
||||
corner_radius: Corners {
|
||||
top_left: 0.,
|
||||
top_right: 30.,
|
||||
bottom_left: 0.,
|
||||
bottom_right: 0.,
|
||||
},
|
||||
children: ElementList(vec![
|
||||
Box::new(FillRect {
|
||||
size: (Size::Absolute(50.), Size::Absolute(50.)).into(),
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use std::time::Instant;
|
||||
use hui::{
|
||||
size,
|
||||
layout::{Alignment, Direction},
|
||||
element::{
|
||||
container::Container,
|
||||
progress_bar::ProgressBar,
|
||||
text::Text,
|
||||
UiElementExt,
|
||||
},
|
||||
}, frame::FrameRect, frame_rect, layout::{Alignment, Direction}, size
|
||||
};
|
||||
|
||||
#[path = "../boilerplate.rs"]
|
||||
|
@ -33,8 +31,10 @@ ui_main!{
|
|||
.with_gap(5.)
|
||||
.with_padding(10.)
|
||||
.with_size(size!(450, auto))
|
||||
.with_background((0.2, 0.2, 0.5))
|
||||
.with_corner_radius(8.)
|
||||
.with_background(frame_rect! {
|
||||
color: (0.2, 0.2, 0.5),
|
||||
corner_radius: 8.
|
||||
})
|
||||
.with_children(|ui| {
|
||||
if instant.elapsed().as_secs_f32() < 5. {
|
||||
Text::default()
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
//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();
|
||||
}
|
|
@ -10,7 +10,7 @@ use winit::{
|
|||
use hui::{
|
||||
element::{
|
||||
container::Container, fill_rect::FillRect, spacer::Spacer, text::Text, ElementList
|
||||
}, layout::Size, UiInstance
|
||||
}, frame::FrameRect, layout::Size, UiInstance
|
||||
};
|
||||
use hui_glium::GliumUiRenderer;
|
||||
|
||||
|
@ -49,7 +49,7 @@ fn main() {
|
|||
|
||||
hui.add(Container {
|
||||
size: (Size::Relative(1.), Size::Relative(1.)).into(),
|
||||
background: vec4(0.1, 0.1, 0.1, 1.).into(),
|
||||
background_frame: Box::new(FrameRect::color((0.1, 0.1, 0.1, 1.))),
|
||||
children: elements(|elem| {
|
||||
elem.push(Box::new(Text {
|
||||
text: "THIS LINE SHOULD BE SHARP!".into(),
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use hui::{
|
||||
color, size,
|
||||
layout::Alignment,
|
||||
element::{UiElementExt, container::Container, text::Text},
|
||||
color, element::{container::Container, text::Text, UiElementExt}, frame::FrameRect, layout::Alignment, size
|
||||
};
|
||||
|
||||
#[path = "../boilerplate.rs"]
|
||||
|
@ -14,8 +12,10 @@ ui_main!(|ui, size, _| {
|
|||
.with_align(Alignment::Center)
|
||||
.with_padding(5.)
|
||||
.with_gap(10.)
|
||||
.with_corner_radius(10.)
|
||||
.with_background(color::WHITE)
|
||||
.with_background(
|
||||
FrameRect::color(color::WHITE)
|
||||
.with_corner_radius(10.)
|
||||
)
|
||||
.with_children(|ui| {
|
||||
Text::default()
|
||||
.with_text("Hello, world")
|
||||
|
@ -24,8 +24,10 @@ ui_main!(|ui, size, _| {
|
|||
.add_child(ui);
|
||||
Container::default()
|
||||
.with_padding((10., 20.))
|
||||
.with_corner_radius((2.5, 30., 2.5, 2.5))
|
||||
.with_background(color::DARK_RED)
|
||||
.with_background(
|
||||
FrameRect::color(color::DARK_RED)
|
||||
.with_corner_radius((2.5, 30., 2.5, 2.5))
|
||||
)
|
||||
.with_children(|ui| {
|
||||
Text::default()
|
||||
.with_text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
use glam::vec4;
|
||||
use hui::{
|
||||
color, size,
|
||||
element::{container::Container, progress_bar::ProgressBar, text::Text, UiElementExt},
|
||||
size, frame_rect,
|
||||
color,
|
||||
element::{
|
||||
container::Container,
|
||||
progress_bar::ProgressBar,
|
||||
text::Text,
|
||||
UiElementExt
|
||||
},
|
||||
frame::FrameRect,
|
||||
layout::Alignment,
|
||||
rect::Corners,
|
||||
text::FontHandle,
|
||||
text::FontHandle
|
||||
};
|
||||
|
||||
#[path = "../boilerplate.rs"]
|
||||
|
@ -38,8 +45,10 @@ ui_main!(
|
|||
.with_children(|ui| {
|
||||
Container::default()
|
||||
.with_padding((10., 15.))
|
||||
.with_corner_radius(8.)
|
||||
.with_background((0., 0., 0., 0.5))
|
||||
.with_background(
|
||||
FrameRect::color((0., 0., 0., 0.5))
|
||||
.with_corner_radius(8.)
|
||||
)
|
||||
.with_children(|ui| {
|
||||
let flash = 1. - 0.5 * (4. * instant.elapsed().as_secs_f32()).sin().powi(2);
|
||||
Text::default()
|
||||
|
@ -61,8 +70,10 @@ ui_main!(
|
|||
.with_align((Alignment::Center, Alignment::Begin))
|
||||
.with_padding(15.)
|
||||
.with_gap(10.)
|
||||
.with_corner_radius(8.)
|
||||
.with_background((0., 0., 0., 0.5))
|
||||
.with_background(frame_rect! {
|
||||
color: (0., 0., 0., 0.5),
|
||||
corner_radius: 8.,
|
||||
})
|
||||
.with_children(|ui| {
|
||||
Text::default()
|
||||
.with_text("Did you know?")
|
||||
|
@ -100,8 +111,10 @@ ui_main!(
|
|||
.with_children(|ui| {
|
||||
Container::default()
|
||||
.with_padding(10.)
|
||||
.with_corner_radius(8.)
|
||||
.with_background((0., 0., 0., 0.5))
|
||||
.with_background(
|
||||
FrameRect::color((0., 0., 0., 0.5))
|
||||
.with_corner_radius(8.)
|
||||
)
|
||||
.with_children(|ui| {
|
||||
Text::default()
|
||||
.with_text("Level 5")
|
||||
|
|
|
@ -7,11 +7,7 @@ use hui::{
|
|||
text::Text,
|
||||
transformer::ElementTransformExt,
|
||||
UiElementExt
|
||||
},
|
||||
layout::Alignment,
|
||||
rect::Corners,
|
||||
text::FontHandle,
|
||||
size,
|
||||
}, frame::FrameRect, frame_rect, layout::Alignment, rect::Corners, size, text::FontHandle
|
||||
};
|
||||
|
||||
#[path = "../boilerplate.rs"]
|
||||
|
@ -41,8 +37,10 @@ ui_main!(
|
|||
.with_align((Alignment::Center, Alignment::Begin))
|
||||
.with_padding(15.)
|
||||
.with_gap(10.)
|
||||
.with_corner_radius(8.)
|
||||
.with_background((0., 0., 0., 0.5))
|
||||
.with_background(frame_rect! {
|
||||
color: (0., 0., 0., 0.5),
|
||||
corner_radius: 8.
|
||||
})
|
||||
.with_children(|ui| {
|
||||
Text::default()
|
||||
.with_text("Did you know?")
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use hui::{
|
||||
draw::TextureFormat,
|
||||
signal::Signal,
|
||||
layout::{Alignment, Direction},
|
||||
element::{
|
||||
container::Container,
|
||||
text::Text,
|
||||
image::Image,
|
||||
br::Break,
|
||||
container::Container,
|
||||
image::Image,
|
||||
slider::Slider,
|
||||
text::Text,
|
||||
UiElementExt,
|
||||
},
|
||||
frame::FrameRect,
|
||||
layout::{Alignment, Direction},
|
||||
signal::Signal,
|
||||
size,
|
||||
};
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ document-features = "0.2"
|
|||
derive_setters = "0.1"
|
||||
derive_more = "0.99"
|
||||
tinyset = "0.4"
|
||||
enum_dispatch = "0.3"
|
||||
#enum_dispatch = "0.3"
|
||||
|
||||
[features]
|
||||
default = ["builtin_elements", "builtin_font", "pixel_perfect_text"]
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::rect::Corners;
|
|||
|
||||
//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 {
|
||||
//Increase for higher quality
|
||||
const VTX_PER_CORER_RADIUS_PIXEL: f32 = 0.5;
|
||||
|
@ -11,19 +12,31 @@ fn point_count(corners: Corners<f32>) -> NonZeroU16 {
|
|||
).unwrap()
|
||||
}
|
||||
|
||||
/// Low-level options for rendering rounded corners
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RoundedCorners {
|
||||
/// Corner radius of each corner
|
||||
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,
|
||||
}
|
||||
|
||||
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 {
|
||||
Self::from_radius(radius)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Self {
|
||||
radius,
|
||||
|
|
|
@ -3,18 +3,13 @@
|
|||
use derive_setters::Setters;
|
||||
use glam::{Vec2, vec2};
|
||||
use crate::{
|
||||
draw::{ImageHandle, RoundedCorners, UiDrawCommand},
|
||||
element::{ElementList, MeasureContext, ProcessContext, UiElement},
|
||||
layout::{Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d},
|
||||
frame::{Frame, FrameRect},
|
||||
measure::{Hints, Response},
|
||||
rect::{Corners, FillColor, Sides},
|
||||
rect::{Sides, FillColor},
|
||||
};
|
||||
|
||||
// pub struct Border {
|
||||
// pub color: Vec4,
|
||||
// pub width: f32,
|
||||
// }
|
||||
|
||||
//XXX: add Order/Direction::Forward/Reverse or sth?
|
||||
//TODO: clip children flag
|
||||
//TODO: borders
|
||||
|
@ -54,26 +49,8 @@ pub struct Container {
|
|||
#[setters(into)]
|
||||
pub align: Alignment2d,
|
||||
|
||||
/// Background color of the container\
|
||||
///
|
||||
/// 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>,
|
||||
#[setters(skip)]
|
||||
pub background_frame: Box<dyn Frame>,
|
||||
|
||||
/// Set this to `true` to allow the elements wrap automatically
|
||||
///
|
||||
|
@ -93,6 +70,11 @@ impl Container {
|
|||
self.children.0.extend(ElementList::from_callback(ui).0);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_background(mut self, frame: impl Frame + 'static) -> Self {
|
||||
self.background_frame = Box::new(frame);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Container {
|
||||
|
@ -103,11 +85,9 @@ impl Default for Container {
|
|||
gap: 0.,
|
||||
padding: Sides::all(0.),
|
||||
align: Alignment2d::default(),
|
||||
background: FillColor::transparent(),
|
||||
background_image: None,
|
||||
children: ElementList(Vec::new()),
|
||||
background_frame: Box::<FrameRect>::default(),
|
||||
wrap: false,
|
||||
corner_radius: Corners::all(0.),
|
||||
children: ElementList(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -328,18 +308,20 @@ impl UiElement for Container {
|
|||
let mut position = ctx.layout.position;
|
||||
|
||||
//background
|
||||
if !self.background.is_transparent() {
|
||||
let corner_colors = self.background.corners();
|
||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
position,
|
||||
size: ctx.measure.size,
|
||||
color: corner_colors,
|
||||
texture: self.background_image,
|
||||
rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
|
||||
RoundedCorners::from_radius(self.corner_radius)
|
||||
}),
|
||||
});
|
||||
}
|
||||
// if !self.background.is_transparent() {
|
||||
// let corner_colors = self.background.corners();
|
||||
// ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
// position,
|
||||
// size: ctx.measure.size,
|
||||
// color: corner_colors,
|
||||
// texture: self.background_image,
|
||||
// rounded_corners: (self.corner_radius.max_f32() > 0.).then_some({
|
||||
// RoundedCorners::from_radius(self.corner_radius)
|
||||
// }),
|
||||
// });
|
||||
// }
|
||||
|
||||
self.background_frame.draw(ctx.draw, ctx.layout.position, ctx.measure.size);
|
||||
|
||||
//padding
|
||||
position += vec2(self.padding.left, self.padding.top);
|
||||
|
|
|
@ -16,6 +16,8 @@ use crate::{
|
|||
//TODO: use state for slider?
|
||||
// ^ 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
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
||||
pub enum SliderFollowMode {
|
||||
|
|
|
@ -1,67 +1,13 @@
|
|||
pub mod point;
|
||||
pub mod layer;
|
||||
use glam::Vec2;
|
||||
use layer::{FrameLayer, FrameLayerImpl};
|
||||
use crate::draw::UiDrawCommandList;
|
||||
|
||||
///XXX: this is not used yet, and also kinda a mess, simplify?
|
||||
///Maybe limit to a single layer? (aka `Frame` will be just one of the options)
|
||||
///Because currently, this is just a duplicate of the dormal draw command system, but with a different name...
|
||||
///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 mod point;
|
||||
mod rect;
|
||||
pub mod stack;
|
||||
mod impls;
|
||||
|
||||
/// A frame, which can contain multiple layers
|
||||
///
|
||||
/// Use these to construct complex backgrounds
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
pub use rect::FrameRect;
|
||||
|
||||
pub trait Frame {
|
||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2);
|
||||
}
|
||||
|
|
125
hui/src/frame/impls.rs
Normal file
125
hui/src/frame/impls.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -43,14 +43,14 @@ impl FramePoint {
|
|||
|
||||
/// Center of the frame axis
|
||||
pub const CENTER: Self = Self {
|
||||
absolute: 0.5,
|
||||
relative: 0.0,
|
||||
absolute: 0.0,
|
||||
relative: 0.5,
|
||||
};
|
||||
|
||||
/// End of the frame axis
|
||||
pub const END: Self = Self {
|
||||
absolute: 1.0,
|
||||
relative: 0.0,
|
||||
absolute: 0.0,
|
||||
relative: 1.0,
|
||||
};
|
||||
|
||||
/// Create a new absolutely positioned `FramePoint`
|
||||
|
|
|
@ -1,25 +1,13 @@
|
|||
use glam::Vec2;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use crate::{
|
||||
color,
|
||||
draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList},
|
||||
rect::{Corners, FillColor},
|
||||
};
|
||||
use super::point::FramePoint2d;
|
||||
|
||||
#[enum_dispatch]
|
||||
pub(crate) trait FrameLayerImpl {
|
||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2);
|
||||
}
|
||||
use super::{Frame, point::FramePoint2d};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[enum_dispatch(FrameLayerImpl)]
|
||||
pub enum FrameLayer {
|
||||
Rect(RectFrame),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RectFrame {
|
||||
pub struct FrameRect {
|
||||
/// Background color of the frame\
|
||||
///
|
||||
/// If the container has a background texture, it will be multiplied by this color
|
||||
|
@ -44,29 +32,35 @@ pub struct RectFrame {
|
|||
pub corner_radius: Corners<f32>,
|
||||
}
|
||||
|
||||
impl<T: Into<FillColor>> From<T> for RectFrame {
|
||||
fn from(color: T) -> Self {
|
||||
Self::from_color(color)
|
||||
// impl<T: Into<FillColor>> From<T> for FrameRect {
|
||||
// fn from(color: T) -> Self {
|
||||
// Self::from_color(color)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl From<FillColor> for FrameRect {
|
||||
fn from(color: FillColor) -> Self {
|
||||
Self::color(color)
|
||||
}
|
||||
}
|
||||
|
||||
impl RectFrame {
|
||||
pub fn from_color(color: impl Into<FillColor>) -> Self {
|
||||
impl From<ImageHandle> for FrameRect {
|
||||
fn from(image: ImageHandle) -> Self {
|
||||
Self::image(image)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameRect {
|
||||
/// Create a new [`FrameRect`] with the given color
|
||||
pub fn color(color: impl Into<FillColor>) -> Self {
|
||||
Self {
|
||||
color: color.into(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_color_rounded(color: impl Into<FillColor>, corner_radius: impl Into<Corners<f32>>) -> Self {
|
||||
Self {
|
||||
color: color.into(),
|
||||
corner_radius: corner_radius.into(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_image(image: ImageHandle) -> Self {
|
||||
/// Create a new [`FrameRect`] with the given image
|
||||
pub fn image(image: ImageHandle) -> Self {
|
||||
Self {
|
||||
color: color::WHITE.into(),
|
||||
image: Some(image),
|
||||
|
@ -74,7 +68,8 @@ impl RectFrame {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_color_image(color: impl Into<FillColor>, image: ImageHandle) -> Self {
|
||||
/// Create a new [`FrameRect`] with the given color and image
|
||||
pub fn color_image(color: impl Into<FillColor>, image: ImageHandle) -> Self {
|
||||
Self {
|
||||
color: color.into(),
|
||||
image: Some(image),
|
||||
|
@ -82,17 +77,18 @@ impl RectFrame {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_color_image_rounded(color: impl Into<FillColor>, image: ImageHandle, corner_radius: impl Into<Corners<f32>>) -> Self {
|
||||
/// Set the corner radius of the [`FrameRect`]
|
||||
pub fn with_corner_radius(self, radius: impl Into<Corners<f32>>) -> Self {
|
||||
Self {
|
||||
color: color.into(),
|
||||
image: Some(image),
|
||||
corner_radius: corner_radius.into(),
|
||||
..Self::default()
|
||||
corner_radius: radius.into(),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Inset the rectangle by the given amount
|
||||
pub fn inset(self, inset: f32) -> Self {
|
||||
//TODO: deprecate and replace
|
||||
|
||||
/// Inset the rectangle by the given amount in pixels
|
||||
pub fn with_inset(self, inset: f32) -> Self {
|
||||
Self {
|
||||
top_left: self.top_left + Vec2::splat(inset).into(),
|
||||
bottom_right: self.bottom_right - Vec2::splat(inset).into(),
|
||||
|
@ -101,10 +97,10 @@ impl RectFrame {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for RectFrame {
|
||||
impl Default for FrameRect {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
color: FillColor::default(),
|
||||
color: FillColor::transparent(),
|
||||
image: None,
|
||||
top_left: FramePoint2d::TOP_LEFT,
|
||||
bottom_right: FramePoint2d::BOTTOM_RIGHT,
|
||||
|
@ -113,7 +109,7 @@ impl Default for RectFrame {
|
|||
}
|
||||
}
|
||||
|
||||
impl FrameLayerImpl for RectFrame {
|
||||
impl Frame for FrameRect {
|
||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
||||
//TODO: handle bottom_right < top_left
|
||||
let top_left = self.top_left.resolve(parent_size);
|
22
hui/src/frame/stack.rs
Normal file
22
hui/src/frame/stack.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
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))
|
||||
}
|
||||
}
|
|
@ -60,3 +60,87 @@ 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.,
|
||||
// };
|
||||
// }
|
||||
|
|
|
@ -153,6 +153,17 @@ 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 {
|
||||
fn from((top_left, top_right, bottom_left, bottom_right): (Vec3, Vec3, Vec3, Vec3)) -> Self {
|
||||
Self(Corners {
|
||||
|
|
|
@ -8,6 +8,7 @@ mod font;
|
|||
mod ftm;
|
||||
mod stack;
|
||||
|
||||
/// Built-in font handle
|
||||
#[cfg(feature="builtin_font")]
|
||||
pub use font::BUILTIN_FONT;
|
||||
pub use font::FontHandle;
|
||||
|
@ -17,7 +18,7 @@ use ftm::FontTextureManager;
|
|||
use ftm::GlyphCacheEntry;
|
||||
use stack::FontStack;
|
||||
|
||||
pub struct TextRenderer {
|
||||
pub(crate) struct TextRenderer {
|
||||
manager: FontManager,
|
||||
ftm: FontTextureManager,
|
||||
stack: FontStack,
|
||||
|
@ -63,15 +64,18 @@ impl Default for TextRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Size of measured text
|
||||
pub struct TextMeasureResponse {
|
||||
pub max_width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
/// Context for measuring text
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TextMeasure<'a>(&'a TextRenderer);
|
||||
|
||||
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 {
|
||||
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||
|
|
Loading…
Reference in a new issue