mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-29 02:18:40 -06:00
Compare commits
15 commits
2a4af1aa35
...
2db8d2f056
Author | SHA1 | Date | |
---|---|---|---|
griffi-gh | 2db8d2f056 | ||
griffi-gh | 95b4c47fbd | ||
griffi-gh | 91c9af9fd5 | ||
griffi-gh | 3b7059d49f | ||
griffi-gh | dac0c7ac6d | ||
griffi-gh | 6ec12187ac | ||
griffi-gh | fc4bc83ba9 | ||
griffi-gh | deec244547 | ||
griffi-gh | 8e7e32671c | ||
griffi-gh | 4ea98db39a | ||
griffi-gh | 7bd93af63f | ||
griffi-gh | d4402756a3 | ||
griffi-gh | 0428af0f63 | ||
griffi-gh | 290157e8d9 | ||
griffi-gh | b8f9ace3da |
|
@ -12,7 +12,7 @@ hui-winit = { path = "../hui-winit" }
|
||||||
kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "c162893fd" }
|
kubi-logging = { git = "https://github.com/griffi-gh/kubi", rev = "c162893fd" }
|
||||||
glium = "0.34"
|
glium = "0.34"
|
||||||
winit = "0.29"
|
winit = "0.29"
|
||||||
glam = "0.25"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
#created as a workaround for rust-analyzer dependency cycle (which should be allowed)
|
#created as a workaround for rust-analyzer dependency cycle (which should be allowed)
|
||||||
|
|
|
@ -73,24 +73,21 @@ fn main() {
|
||||||
children: ElementList(vec![
|
children: ElementList(vec![
|
||||||
Box::new(FillRect {
|
Box::new(FillRect {
|
||||||
size: (Size::Relative(0.5), Size::Absolute(30.)).into(),
|
size: (Size::Relative(0.5), Size::Absolute(30.)).into(),
|
||||||
background: vec4(0.75, 0., 0., 1.).into(),
|
frame: Box::new(vec4(0.75, 0., 0., 1.)),
|
||||||
..Default::default()
|
|
||||||
}),
|
}),
|
||||||
Box::new(FillRect {
|
Box::new(FillRect {
|
||||||
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
|
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
|
||||||
background: Corners::left_right(
|
frame: Box::new(Corners::left_right(
|
||||||
vec4(1., 0., 0., 1.),
|
vec4(1., 0., 0., 1.),
|
||||||
vec4(0., 1., 0., 1.)
|
vec4(0., 1., 0., 1.)
|
||||||
).into(),
|
)),
|
||||||
..Default::default()
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
Box::new(FillRect {
|
Box::new(FillRect {
|
||||||
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
|
size: (Size::Relative(z / 2. + 0.5), Size::Absolute(30.)).into(),
|
||||||
background: vec4(0., 0.75, 0., 1.).into(),
|
frame: Box::new(vec4(0., 0.75, 0., 1.)),
|
||||||
..Default::default()
|
|
||||||
}),
|
}),
|
||||||
Box::new(Container {
|
Box::new(Container {
|
||||||
gap: 5.,
|
gap: 5.,
|
||||||
|
@ -102,12 +99,11 @@ fn main() {
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
x.push(Box::new(FillRect {
|
x.push(Box::new(FillRect {
|
||||||
size: (Size::Absolute(50.), Size::Absolute(50.)).into(),
|
size: (Size::Absolute(50.), Size::Absolute(50.)).into(),
|
||||||
background: if i == 1 {
|
frame: Box::new(if i == 1 {
|
||||||
vec4(0.75, 0.75, 0.75, 0.75).into()
|
vec4(0.75, 0.75, 0.75, 0.75)
|
||||||
} else {
|
} else {
|
||||||
vec4(0.5, 0.5, 0.5, 0.75).into()
|
vec4(0.5, 0.5, 0.5, 0.75)
|
||||||
},
|
}),
|
||||||
..Default::default()
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
ElementList(x)
|
ElementList(x)
|
||||||
|
@ -130,8 +126,7 @@ fn main() {
|
||||||
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(),
|
||||||
background: vec4(1., 1., 1., 0.75).into(),
|
frame: Box::new(vec4(1., 1., 1., 0.75)),
|
||||||
..Default::default()
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -73,13 +73,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
elem.push(Box::new(FillRect {
|
elem.push(Box::new(FillRect {
|
||||||
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
||||||
background: vec4(0., 0., 1., 1.).into(),
|
frame: Box::new(vec4(0., 0., 1., 1.)),
|
||||||
..Default::default()
|
|
||||||
}));
|
}));
|
||||||
elem.push(Box::new(FillRect {
|
elem.push(Box::new(FillRect {
|
||||||
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
||||||
background: vec4(1., 1., 0., 1.).into(),
|
frame: Box::new(vec4(1., 1., 0., 1.)),
|
||||||
..Default::default()
|
|
||||||
}));
|
}));
|
||||||
elem.push(Box::new(Text {
|
elem.push(Box::new(Text {
|
||||||
text: "Hello, world!\nżółty liść. życie nie ma sensu i wszyscy zginemy;\nтест кирилиці їїїїїїїїїїї\njapanese text: テスト".into(),
|
text: "Hello, world!\nżółty liść. życie nie ma sensu i wszyscy zginemy;\nтест кирилиці їїїїїїїїїїї\njapanese text: テスト".into(),
|
||||||
|
@ -90,13 +88,11 @@ fn main() {
|
||||||
if instant.elapsed().as_secs() & 1 != 0 {
|
if instant.elapsed().as_secs() & 1 != 0 {
|
||||||
elem.push(Box::new(FillRect {
|
elem.push(Box::new(FillRect {
|
||||||
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
||||||
background: vec4(1., 0., 0., 1.).into(),
|
frame: Box::new(vec4(1., 0., 0., 1.)),
|
||||||
..Default::default()
|
|
||||||
}));
|
}));
|
||||||
elem.push(Box::new(FillRect {
|
elem.push(Box::new(FillRect {
|
||||||
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
size: (Size::Relative(1.), Size::Absolute(10.)).into(),
|
||||||
background: vec4(0., 0., 0., 1.).into(),
|
frame: Box::new(vec4(0., 0., 0., 1.)),
|
||||||
..Default::default()
|
|
||||||
}));
|
}));
|
||||||
elem.push(Box::new(Spacer(100.)));
|
elem.push(Box::new(Spacer(100.)));
|
||||||
elem.push(Box::new(Text {
|
elem.push(Box::new(Text {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use hui::{
|
use hui::{
|
||||||
color, element::{container::Container, text::Text, UiElementExt}, frame::FrameRect, layout::Alignment, size
|
color, size, frame_rect,
|
||||||
|
element::{container::Container, text::Text, UiElementExt},
|
||||||
|
frame::FrameRect,
|
||||||
|
layout::Alignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
|
@ -12,10 +15,10 @@ 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(
|
.with_background(frame_rect! {
|
||||||
FrameRect::color(color::WHITE)
|
color: (0.5, 0.5, 0.5, 1.),
|
||||||
.with_corner_radius(10.)
|
corner_radius: 10.,
|
||||||
)
|
})
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Text::default()
|
Text::default()
|
||||||
.with_text("Hello, world")
|
.with_text("Hello, world")
|
||||||
|
@ -24,10 +27,10 @@ ui_main!(|ui, size, _| {
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_padding((10., 20.))
|
.with_padding((10., 20.))
|
||||||
.with_background(
|
.with_background(frame_rect! {
|
||||||
FrameRect::color(color::DARK_RED)
|
color: color::DARK_RED,
|
||||||
.with_corner_radius((2.5, 30., 2.5, 2.5))
|
corner_radius: (2.5, 30., 2.5, 2.5),
|
||||||
)
|
})
|
||||||
.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.")
|
||||||
|
|
|
@ -45,10 +45,10 @@ ui_main!(
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_padding((10., 15.))
|
.with_padding((10., 15.))
|
||||||
.with_background(
|
.with_background(frame_rect! {
|
||||||
FrameRect::color((0., 0., 0., 0.5))
|
color: (0., 0., 0., 0.5),
|
||||||
.with_corner_radius(8.)
|
corner_radius: 8.,
|
||||||
)
|
})
|
||||||
.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()
|
||||||
|
@ -111,10 +111,10 @@ ui_main!(
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_padding(10.)
|
.with_padding(10.)
|
||||||
.with_background(
|
.with_background(frame_rect!{
|
||||||
FrameRect::color((0., 0., 0., 0.5))
|
color: (0., 0., 0., 0.5),
|
||||||
.with_corner_radius(8.)
|
corner_radius: 8.,
|
||||||
)
|
})
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Text::default()
|
Text::default()
|
||||||
.with_text("Level 5")
|
.with_text("Level 5")
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use hui::{
|
use hui::{
|
||||||
color, size,
|
color, element::{
|
||||||
layout::{Alignment, Direction},
|
|
||||||
element::{
|
|
||||||
container::Container,
|
container::Container,
|
||||||
fill_rect::FillRect,
|
fill_rect::FillRect,
|
||||||
UiElementExt
|
UiElementExt
|
||||||
},
|
}, frame_rect, layout::{Alignment, Direction}, size
|
||||||
};
|
};
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
|
@ -32,8 +30,10 @@ ui_main!(
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
FillRect::default()
|
FillRect::default()
|
||||||
.with_size(size!((40 + i * 10)))
|
.with_size(size!((40 + i * 10)))
|
||||||
.with_corner_radius(8.)
|
.with_frame(frame_rect! {
|
||||||
.with_background(color::DARK_RED)
|
color: color::DARK_RED,
|
||||||
|
corner_radius: 8.
|
||||||
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,7 +8,6 @@ use hui::{
|
||||||
text::Text,
|
text::Text,
|
||||||
UiElementExt,
|
UiElementExt,
|
||||||
},
|
},
|
||||||
frame::FrameRect,
|
|
||||||
layout::{Alignment, Direction},
|
layout::{Alignment, Direction},
|
||||||
signal::Signal,
|
signal::Signal,
|
||||||
size,
|
size,
|
||||||
|
|
34
hui-examples/examples/ui_test_7_9patch.rs
Normal file
34
hui-examples/examples/ui_test_7_9patch.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
use hui::{
|
||||||
|
color, element::{
|
||||||
|
container::Container,
|
||||||
|
fill_rect::FillRect,
|
||||||
|
UiElementExt
|
||||||
|
}, frame_rect, layout::{Alignment, Direction}, size
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "../boilerplate.rs"]
|
||||||
|
#[macro_use]
|
||||||
|
mod boilerplate;
|
||||||
|
|
||||||
|
ui_main!(
|
||||||
|
"hUI: 9-Patch demo",
|
||||||
|
init: |_| {
|
||||||
|
|
||||||
|
},
|
||||||
|
run: |ui, size, _| {
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.with_background(color::WHITE)
|
||||||
|
.with_children(|ui| {
|
||||||
|
FillRect::default()
|
||||||
|
.with_size(size!(300, 100))
|
||||||
|
.with_frame(frame_rect! {
|
||||||
|
color: color::RED
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
}
|
||||||
|
);
|
|
@ -55,7 +55,7 @@ ui_main!(
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
FillRect::default()
|
FillRect::default()
|
||||||
.with_size(size!(100%, 1))
|
.with_size(size!(100%, 1))
|
||||||
.with_background(color::rgb_hex(0x2d2d30))
|
.with_frame(color::rgb_hex(0x2d2d30))
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%, 100%))
|
.with_size(size!(100%, 100%))
|
||||||
|
@ -67,7 +67,7 @@ ui_main!(
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
FillRect::default()
|
FillRect::default()
|
||||||
.with_size(size!(1, 100%))
|
.with_size(size!(1, 100%))
|
||||||
.with_background(color::rgb_hex(0x2d2d30))
|
.with_frame(color::rgb_hex(0x2d2d30))
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(200, 100%))
|
.with_size(size!(200, 100%))
|
||||||
|
|
|
@ -17,5 +17,5 @@ include = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui = { version = "=0.1.0-alpha.4", path = "../hui", default-features = false }
|
hui = { version = "=0.1.0-alpha.4", path = "../hui", default-features = false }
|
||||||
glium = { version = "0.34", default-features = false }
|
glium = { version = "0.34", default-features = false }
|
||||||
glam = "0.25"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -16,5 +16,5 @@ include = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui = { version = "=0.1.0-alpha.4", path = "../hui", default-features = false }
|
hui = { version = "=0.1.0-alpha.4", path = "../hui", default-features = false }
|
||||||
winit = { version = "0.29", default-features = false }
|
winit = { version = "0.29", default-features = false }
|
||||||
glam = "0.25"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -18,7 +18,7 @@ include = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
glam = "0.25"
|
glam = "0.27"
|
||||||
fontdue = "0.8"
|
fontdue = "0.8"
|
||||||
rect_packer = "0.2"
|
rect_packer = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
@ -26,21 +26,77 @@ 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"
|
image = { version = "0.25", default-features = false, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["builtin_elements", "builtin_font", "pixel_perfect_text"]
|
default = ["el_all", "image", "builtin_font", "pixel_perfect_text"]
|
||||||
## Enable the built-in font (ProggyTiny, adds 35kb to the executable)
|
|
||||||
|
#! Image loading support:
|
||||||
|
|
||||||
|
## Enable image loading support using the `image` crate
|
||||||
|
image = ["dep:image"]
|
||||||
|
|
||||||
|
#! #### Built-in font:
|
||||||
|
|
||||||
|
## Enable the built-in font (ProggyTiny, adds *35kb* to the executable)
|
||||||
builtin_font = []
|
builtin_font = []
|
||||||
## Enable the built-in elements (`Container`, `ProgressBar`, etc.)\
|
|
||||||
builtin_elements = ["builtin_container"]
|
#! #### Pixel-perfect rendering:
|
||||||
## Enable only the `Container` component (which is essential for laying out other components)
|
|
||||||
builtin_container = []
|
|
||||||
## Round all vertex positions to nearest integer coordinates (not recommended)
|
## Round all vertex positions to nearest integer coordinates (not recommended)
|
||||||
pixel_perfect = ["pixel_perfect_text"]
|
pixel_perfect = ["pixel_perfect_text"]
|
||||||
|
|
||||||
## Apply pixel-perfect rendering hack to text (fixes blurry text rendering)
|
## Apply pixel-perfect rendering hack to text (fixes blurry text rendering)
|
||||||
pixel_perfect_text = []
|
pixel_perfect_text = []
|
||||||
#! Make sure to disable the `pixel_perfect` feature if you are rendering UI in 3D space\
|
|
||||||
#! or using DPI (or any other form of) scaling while passing the virtual resolution to the ui
|
#! Make sure to disable both features if you are not rendering UI "as-is" at 1:1 scale\
|
||||||
|
#! For exmaple, you should disable them if using DPI (or any other form of) scaling while passing the virtual resolution to the ui or rendering it in 3d space
|
||||||
|
|
||||||
|
#! #### Built-in elements:
|
||||||
|
|
||||||
|
## Enable all built-in elements
|
||||||
|
el_all = [
|
||||||
|
"el_container",
|
||||||
|
"el_fill_rect",
|
||||||
|
"el_spacer",
|
||||||
|
"el_br",
|
||||||
|
"el_text",
|
||||||
|
"el_image",
|
||||||
|
"el_progress_bar",
|
||||||
|
"el_slider",
|
||||||
|
"el_transformer",
|
||||||
|
"el_interactable",
|
||||||
|
]
|
||||||
|
|
||||||
|
## Enable the built-in `Container` element
|
||||||
|
el_container = []
|
||||||
|
|
||||||
|
## Enable the built-in `FillRect` element
|
||||||
|
el_fill_rect = []
|
||||||
|
|
||||||
|
## Enable the built-in `Spacer` element
|
||||||
|
el_spacer = []
|
||||||
|
|
||||||
|
## Enable the built-in `Break` element
|
||||||
|
el_br = []
|
||||||
|
|
||||||
|
## Enable the built-in `Text` element
|
||||||
|
el_text = []
|
||||||
|
|
||||||
|
## Enable the built-in `Image` element
|
||||||
|
el_image = []
|
||||||
|
|
||||||
|
## Enable the built-in `ProgressBar` element
|
||||||
|
el_progress_bar = []
|
||||||
|
|
||||||
|
## Enable the built-in `Slider` element
|
||||||
|
el_slider = []
|
||||||
|
|
||||||
|
## Enable the built-in `Transformer` element
|
||||||
|
el_transformer = []
|
||||||
|
|
||||||
|
## Enable the built-in `Interactable` element
|
||||||
|
el_interactable = []
|
||||||
|
|
||||||
# ## Enable multi-threading support (currently only affects some 3rd-party libraries)
|
# ## Enable multi-threading support (currently only affects some 3rd-party libraries)
|
||||||
# parallel = ["fontdue/parallel"]
|
# parallel = ["fontdue/parallel"]
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
// Layout stuff:
|
// Layout stuff:
|
||||||
|
|
||||||
#[cfg(feature = "builtin_container")]
|
#[cfg(feature = "el_container")]
|
||||||
pub mod container;
|
pub mod container;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_fill_rect")]
|
||||||
pub mod fill_rect;
|
pub mod fill_rect;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_spacer")]
|
||||||
pub mod spacer;
|
pub mod spacer;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_br")]
|
||||||
pub mod br;
|
pub mod br;
|
||||||
|
|
||||||
// Basic elements:
|
// Basic elements:
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_text")]
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_image")]
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
|
||||||
// "Extras":
|
// "Extras":
|
||||||
// (meant to be replaced if needed)
|
// (meant to be replaced if needed)
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_progress_bar")]
|
||||||
pub mod progress_bar;
|
pub mod progress_bar;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_slider")]
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
|
|
||||||
// Wrappers:
|
// Wrappers:
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_transformer")]
|
||||||
pub mod transformer;
|
pub mod transformer;
|
||||||
|
|
||||||
#[cfg(feature = "builtin_elements")]
|
#[cfg(feature = "el_interactable")]
|
||||||
pub mod interactable;
|
pub mod interactable;
|
||||||
|
|
||||||
//TODO add: Image
|
//TODO add: Image
|
||||||
|
|
|
@ -1,39 +1,41 @@
|
||||||
//! Simple filled rectangle with the specified size, background and corner radius
|
//! Simple filled rectangle with the specified size, background and corner radius
|
||||||
|
|
||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use glam::{vec2, Vec4};
|
use glam::vec2;
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::{RoundedCorners, UiDrawCommand},
|
draw::{RoundedCorners, UiDrawCommand},
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
|
frame::{Frame, FrameRect},
|
||||||
layout::{Size, Size2d},
|
layout::{Size, Size2d},
|
||||||
measure::Response,
|
measure::Response,
|
||||||
rect::{Corners, FillColor},
|
size
|
||||||
size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Simple filled rectangle with the specified size, background, and corner radius
|
/// Simple filled rectangle with the specified size, background, and corner radius
|
||||||
#[derive(Debug, Clone, Copy, Setters)]
|
#[derive(Setters)]
|
||||||
#[setters(prefix = "with_")]
|
#[setters(prefix = "with_")]
|
||||||
pub struct FillRect {
|
pub struct FillRect {
|
||||||
/// Size of the rectangle
|
/// Size of the rectangle
|
||||||
#[setters(into)]
|
#[setters(into)]
|
||||||
pub size: Size2d,
|
pub size: Size2d,
|
||||||
|
|
||||||
/// Background color of the rectangle
|
/// Frame
|
||||||
#[setters(into)]
|
#[setters(skip)]
|
||||||
pub background: FillColor,
|
pub frame: Box<dyn Frame>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Corner radius of the rectangle
|
impl FillRect {
|
||||||
#[setters(into)]
|
pub fn with_frame(mut self, frame: impl Frame + 'static) -> Self {
|
||||||
pub corner_radius: Corners<f32>,
|
self.frame = Box::new(frame);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FillRect {
|
impl Default for FillRect {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
size: size!(10, 10),
|
size: size!(10, 10),
|
||||||
background: Vec4::new(0., 0., 0., 0.5).into(),
|
frame: Box::new(FrameRect::color((0., 0., 0., 0.5))),
|
||||||
corner_radius: Corners::all(0.),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,16 +64,17 @@ impl UiElement for FillRect {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&self, ctx: ProcessContext) {
|
fn process(&self, ctx: ProcessContext) {
|
||||||
if !self.background.is_transparent() {
|
// if !self.background.is_transparent() {
|
||||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
// ctx.draw.add(UiDrawCommand::Rectangle {
|
||||||
position: ctx.layout.position,
|
// position: ctx.layout.position,
|
||||||
size: ctx.measure.size,
|
// size: ctx.measure.size,
|
||||||
color: self.background.corners(),
|
// color: self.background.corners(),
|
||||||
texture: None,
|
// texture: None,
|
||||||
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.frame.draw(ctx.draw, ctx.layout.position, ctx.measure.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,7 @@ use derive_setters::Setters;
|
||||||
use glam::{Vec2, vec2};
|
use glam::{Vec2, vec2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::UiDrawCommand,
|
draw::UiDrawCommand, element::{MeasureContext, ProcessContext, UiElement}, frame::{Frame, FrameRect}, layout::{compute_size, Size2d}, measure::Response, rect::FillColor, signal::{trigger::SignalTriggerArg, Signal}
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
|
||||||
layout::{Size2d, compute_size},
|
|
||||||
measure::Response,
|
|
||||||
rect::FillColor,
|
|
||||||
signal::{trigger::SignalTriggerArg, Signal},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,22 +41,31 @@ pub struct Slider {
|
||||||
#[setters(into)]
|
#[setters(into)]
|
||||||
pub size: Size2d,
|
pub size: Size2d,
|
||||||
|
|
||||||
/// Color of the slider handle
|
/// Track frame
|
||||||
#[setters(into)]
|
#[setters(skip)]
|
||||||
pub handle_color: FillColor,
|
pub track: Box<dyn Frame>,
|
||||||
|
|
||||||
/// Color of the slider track
|
/// Track active frame
|
||||||
#[setters(into)]
|
#[setters(skip)]
|
||||||
pub track_color: FillColor,
|
pub track_active: Box<dyn Frame>,
|
||||||
|
|
||||||
/// Color of the "active" part of the slider
|
/// Handle frame
|
||||||
#[setters(into)]
|
#[setters(skip)]
|
||||||
pub track_active_color: FillColor,
|
pub handle: Box<dyn Frame>,
|
||||||
|
|
||||||
/// Track height relative to the slider height\
|
/// Track height *relative to the slider height*\
|
||||||
///
|
///
|
||||||
/// Range: 0.0..=1.0
|
/// Range: 0.0..=1.0
|
||||||
pub track_height_ratio: f32,
|
pub track_height: f32,
|
||||||
|
|
||||||
|
/// Handle size
|
||||||
|
///
|
||||||
|
/// Please be aware that:
|
||||||
|
///
|
||||||
|
/// - Width is *static* and specified in *pixels* (e.g. `15.0`)
|
||||||
|
/// - Height is *relative* to the slider height,\
|
||||||
|
/// ...and is specified as a *ratio* in range `0.0..=1.0`
|
||||||
|
pub handle_size: (f32, f32),
|
||||||
|
|
||||||
/// Follow mode
|
/// Follow mode
|
||||||
pub follow_mode: SliderFollowMode,
|
pub follow_mode: SliderFollowMode,
|
||||||
|
@ -75,10 +79,11 @@ impl Default for Slider {
|
||||||
Self {
|
Self {
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
size: Size2d::default(),
|
size: Size2d::default(),
|
||||||
handle_color: (0.0, 0.0, 1.).into(),
|
handle: Box::new(FrameRect::color((0.0, 0.0, 1.))),
|
||||||
track_color: (0.5, 0.5, 0.5).into(),
|
track: Box::new(FrameRect::color((0.5, 0.5, 0.5))),
|
||||||
track_active_color: (0.0, 0.0, 0.75).into(),
|
track_active: Box::new(FrameRect::color((0.0, 0.0, 0.75))),
|
||||||
track_height_ratio: 0.25,
|
track_height: 0.25,
|
||||||
|
handle_size: (15.0, 1.),
|
||||||
follow_mode: SliderFollowMode::default(),
|
follow_mode: SliderFollowMode::default(),
|
||||||
on_change: None
|
on_change: None
|
||||||
}
|
}
|
||||||
|
@ -101,6 +106,21 @@ impl Slider {
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_track(mut self, track: impl Frame + 'static) -> Self {
|
||||||
|
self.track = Box::new(track);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_track_active(mut self, track_active: impl Frame + 'static) -> Self {
|
||||||
|
self.track_active = Box::new(track_active);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_handle(mut self, handle: impl Frame + 'static) -> Self {
|
||||||
|
self.handle = Box::new(handle);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiElement for Slider {
|
impl UiElement for Slider {
|
||||||
|
@ -121,55 +141,65 @@ impl UiElement for Slider {
|
||||||
//Compute handle size:
|
//Compute handle size:
|
||||||
// This is kinda counter-intuitive, but if the handle is transparent, we treat it as completely disabled
|
// This is kinda counter-intuitive, but if the handle is transparent, we treat it as completely disabled
|
||||||
// To prevent confusing offset from the edge of the slider, we set the handle size to 0
|
// To prevent confusing offset from the edge of the slider, we set the handle size to 0
|
||||||
let handle_size = if self.handle_color.is_transparent() {
|
// let handle_size = if self.handle_color.is_transparent() {
|
||||||
Vec2::ZERO
|
// Vec2::ZERO
|
||||||
} else {
|
// } else {
|
||||||
vec2(15., ctx.measure.size.y)
|
// vec2(15., ctx.measure.size.y)
|
||||||
};
|
// };
|
||||||
|
let handle_size = vec2(self.handle_size.0, self.handle_size.1 * ctx.measure.size.y);
|
||||||
|
|
||||||
//Draw the track
|
//Draw the track
|
||||||
//If the active part is opaque and value >= 1., we don't need to draw the background as the active part will cover it
|
//If the active part is opaque and value >= 1., we don't need to draw the background as the active part will cover it
|
||||||
//However, if the handle is not opaque, we need to draw the background as the active part won't quite reach the end
|
//However, if the handle is not opaque, we need to draw the background as the active part won't quite reach the end
|
||||||
//Of corse, if it's fully transparent, we don't need to draw it either
|
//Of corse, if it's fully transparent, we don't need to draw it either
|
||||||
if !(self.track_color.is_transparent() || (self.track_active_color.is_opaque() && self.handle_color.is_opaque() && self.value >= 1.)) {
|
// if !(self.track_color.is_transparent() || (self.track_active_color.is_opaque() && self.handle_color.is_opaque() && self.value >= 1.)) {
|
||||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
if !(self.track_active.covers_opaque() && self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value >= 1.) {
|
||||||
position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.),
|
self.track.draw(
|
||||||
size: ctx.measure.size * vec2(1., self.track_height_ratio),
|
ctx.draw,
|
||||||
color: self.track_color.into(),
|
ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.),
|
||||||
texture: None,
|
ctx.measure.size * vec2(1., self.track_height),
|
||||||
rounded_corners: None,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//"Active" part of the track
|
//"Active" part of the track
|
||||||
//We can skip drawing it if it's fully transparent or value <= 0.
|
//We can skip drawing it if it's fully transparent or value <= 0.
|
||||||
//But if the handle is not opaque, it should be visible even if value is zero
|
//But if the handle is not opaque, it should be visible even if value is zero
|
||||||
if !(self.track_active_color.is_transparent() || (self.value <= 0. && self.handle_color.is_opaque())) {
|
// if !(self.track_active_color.is_transparent() || (self.value <= 0. && self.handle_color.is_opaque())) {
|
||||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
if !(self.handle.covers_opaque() && (self.handle_size.1 >= self.track_height) && self.value <= 0.) {
|
||||||
position: ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height_ratio / 2.),
|
self.track_active.draw(
|
||||||
size: (ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height_ratio) + handle_size * Vec2::X / 2.,
|
ctx.draw,
|
||||||
color: self.track_active_color.into(),
|
ctx.layout.position + ctx.measure.size * vec2(0., 0.5 - self.track_height / 2.),
|
||||||
texture: None,
|
(ctx.measure.size - handle_size * Vec2::X) * vec2(self.value, self.track_height) + handle_size * Vec2::X / 2.,
|
||||||
rounded_corners: None,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The handle
|
// The handle
|
||||||
if handle_size.x != 0. && !self.handle_color.is_transparent() {
|
// if handle_size.x != 0. && !self.handle_color.is_transparent() {
|
||||||
let value = self.value.clamp(0., 1.);
|
// let value = self.value.clamp(0., 1.);
|
||||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
// ctx.draw.add(UiDrawCommand::Rectangle {
|
||||||
position: ctx.layout.position + ((ctx.measure.size.x - handle_size.x) * value) * Vec2::X,
|
// position: ctx.layout.position + ((ctx.measure.size.x - handle_size.x) * value) * Vec2::X,
|
||||||
size: handle_size,
|
// size: handle_size,
|
||||||
color: self.handle_color.into(),
|
// color: self.handle_color.into(),
|
||||||
texture: None,
|
// texture: None,
|
||||||
rounded_corners: None,
|
// rounded_corners: None,
|
||||||
});
|
// });
|
||||||
|
// }
|
||||||
|
if (self.handle_size.0 > 0. && self.handle_size.1 > 0.) {
|
||||||
|
self.handle.draw(
|
||||||
|
ctx.draw,
|
||||||
|
ctx.layout.position +
|
||||||
|
((ctx.measure.size.x - handle_size.x) * self.value) * Vec2::X +
|
||||||
|
ctx.measure.size.y * ((1. - self.handle_size.1) * 0.5) * Vec2::Y,
|
||||||
|
handle_size,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//handle events
|
//handle events
|
||||||
if let Some(res) = ctx.input.check_active(ctx.measure.rect(ctx.layout.position)) {
|
if let Some(res) = ctx.input.check_active(ctx.measure.rect(ctx.layout.position)) {
|
||||||
let new_value = match self.follow_mode {
|
let new_value = match self.follow_mode {
|
||||||
SliderFollowMode::Absolute => ((res.position_in_rect.x - handle_size.x / 2.) / (ctx.measure.size.x - handle_size.x)).clamp(0., 1.),
|
SliderFollowMode::Absolute => {
|
||||||
|
((res.position_in_rect.x - handle_size.x / 2.) / (ctx.measure.size.x - handle_size.x)).clamp(0., 1.)
|
||||||
|
},
|
||||||
SliderFollowMode::Relative => {
|
SliderFollowMode::Relative => {
|
||||||
let delta = res.position_in_rect.x - res.last_position_in_rect.x;
|
let delta = res.position_in_rect.x - res.last_position_in_rect.x;
|
||||||
let delta_ratio = delta / (ctx.measure.size.x - handle_size.x);
|
let delta_ratio = delta / (ctx.measure.size.x - handle_size.x);
|
||||||
|
|
|
@ -9,5 +9,16 @@ mod impls;
|
||||||
pub use rect::FrameRect;
|
pub use rect::FrameRect;
|
||||||
|
|
||||||
pub trait Frame {
|
pub trait Frame {
|
||||||
|
/// Draw the frame at the given position and size
|
||||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2);
|
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2);
|
||||||
|
|
||||||
|
/// Check if the frame is guaranteed to be fully opaque and fully cover the parent frame regardless of it's size
|
||||||
|
///
|
||||||
|
/// Returns true if the frame:
|
||||||
|
/// - Is fully opaque (i.e. `alpha >= 1.0`)
|
||||||
|
/// - Completely covers (or exceeds the size of) the frame
|
||||||
|
///
|
||||||
|
/// False negatives are acceptable, but false positives ***are not***.\
|
||||||
|
/// May be used for optimization purposes
|
||||||
|
fn covers_opaque(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,10 @@ impl Frame for ImageHandle {
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame for FillColor {
|
impl Frame for FillColor {
|
||||||
|
@ -28,6 +32,10 @@ impl Frame for FillColor {
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
self.is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl for various types resembling colors
|
// impl for various types resembling colors
|
||||||
|
@ -38,24 +46,36 @@ impl Frame for Corners<Vec4> {
|
||||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Corners (RGB):
|
// Corners (RGB):
|
||||||
|
@ -64,24 +84,36 @@ impl Frame for Corners<Vec3> {
|
||||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RGBA:
|
// RGBA:
|
||||||
|
@ -90,18 +122,27 @@ impl Frame for Vec4 {
|
||||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RGB:
|
// RGB:
|
||||||
|
@ -110,16 +151,25 @@ impl Frame for Vec3 {
|
||||||
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
fn draw(&self, draw: &mut UiDrawCommandList, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, position: Vec2, parent_size: Vec2) {
|
||||||
FillColor::from(*self).draw(draw, position, parent_size)
|
FillColor::from(*self).draw(draw, position, parent_size)
|
||||||
}
|
}
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
FillColor::from(*self).is_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use super::{Frame, point::FramePoint2d};
|
use super::{Frame, point::FramePoint2d};
|
||||||
|
|
||||||
|
/// A rectangular frame
|
||||||
|
///
|
||||||
|
/// Can optionally be tinted, textured, and have rounded corners
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct FrameRect {
|
pub struct FrameRect {
|
||||||
/// Background color of the frame\
|
/// Background color of the frame\
|
||||||
|
@ -59,7 +62,9 @@ impl FrameRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`FrameRect`] with the given image
|
/// Create a new [`FrameRect`] with the given image\
|
||||||
|
///
|
||||||
|
/// 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 {
|
||||||
Self {
|
Self {
|
||||||
color: color::WHITE.into(),
|
color: color::WHITE.into(),
|
||||||
|
@ -124,4 +129,17 @@ impl Frame for FrameRect {
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
self.top_left.x.absolute <= 0. &&
|
||||||
|
self.top_left.x.relative <= 0. &&
|
||||||
|
self.top_left.y.absolute <= 0. &&
|
||||||
|
self.top_left.y.relative <= 0. &&
|
||||||
|
self.bottom_right.x.absolute >= 0. &&
|
||||||
|
self.bottom_right.x.relative >= 1. &&
|
||||||
|
self.bottom_right.y.absolute >= 0. &&
|
||||||
|
self.bottom_right.y.relative >= 1. &&
|
||||||
|
self.color.is_opaque() &&
|
||||||
|
self.image.is_none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,11 @@ impl Frame for FrameStack {
|
||||||
self.0.draw(draw, position, parent_size);
|
self.0.draw(draw, position, parent_size);
|
||||||
self.1.draw(draw, position, parent_size);
|
self.1.draw(draw, position, parent_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn covers_opaque(&self) -> bool {
|
||||||
|
self.0.covers_opaque() ||
|
||||||
|
self.1.covers_opaque()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FrameStackExt: Frame {
|
pub trait FrameStackExt: Frame {
|
||||||
|
|
|
@ -83,6 +83,44 @@ impl UiInstance {
|
||||||
self.atlas.add(width, data, format)
|
self.atlas.add(width, data, format)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO better error handling
|
||||||
|
|
||||||
|
/// Add an image from a file to the texture atlas\
|
||||||
|
/// (experimental, may be removed in the future)
|
||||||
|
///
|
||||||
|
/// Requires the `image` feature
|
||||||
|
///
|
||||||
|
/// # Panics:
|
||||||
|
/// - If the file exists but contains invalid image data\
|
||||||
|
/// (this will change to a soft error in the future)
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<ImageHandle, std::io::Error> {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
// Open the file (and wrap it in a bufreader)
|
||||||
|
let mut file = std::io::BufReader::new(std::fs::File::open(path)?);
|
||||||
|
|
||||||
|
//Guess the image format from the magic bytes
|
||||||
|
//Read like 64 bytes, which should be enough for magic byte detection
|
||||||
|
//well this would fail if the image is somehow smaller than 64 bytes, but who the fvck cares...
|
||||||
|
let mut magic = [0; 64];
|
||||||
|
file.read_exact(&mut magic)?;
|
||||||
|
let format = image::guess_format(&magic).expect("Invalid image data (FORMAT)");
|
||||||
|
|
||||||
|
//Parse the image and read the raw uncompressed rgba data
|
||||||
|
let image = image::load(file, format).expect("Invalid image data");
|
||||||
|
let image_rgba = image.as_rgba8().unwrap();
|
||||||
|
|
||||||
|
//Add the image to the atlas
|
||||||
|
let handle = self.add_image(
|
||||||
|
TextureFormat::Rgba,
|
||||||
|
image_rgba,
|
||||||
|
image.width() as usize
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
|
||||||
/// Push a font to the font stack\
|
/// Push a font to the font stack\
|
||||||
/// The font will be used for all text rendering until it is popped
|
/// The font will be used for all text rendering until it is popped
|
||||||
///
|
///
|
||||||
|
|
|
@ -62,6 +62,18 @@ macro_rules! size {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper macro for constructing a `FrameRect`
|
/// Helper macro for constructing a `FrameRect`
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
/// ```
|
||||||
|
/// frame_rect! {
|
||||||
|
/// color: (0.2, 0.2, 0.3, 1.),
|
||||||
|
/// corner_radius: 5.,
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Note:
|
||||||
|
/// - 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)
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! frame_rect {
|
macro_rules! frame_rect {
|
||||||
{} => {
|
{} => {
|
||||||
|
|
Loading…
Reference in a new issue