mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-29 10:18:56 -06:00
Compare commits
No commits in common. "ec4404b26cfe16705bdb873f7ac7b8df6f2c4126" and "2db8d2f056802e04e726b754efc236b50d5ee093" have entirely different histories.
ec4404b26c
...
2db8d2f056
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/universal:2",
|
|
||||||
"features": {
|
|
||||||
"ghcr.io/devcontainers/features/rust:1": {},
|
|
||||||
"ghcr.io/devcontainers/features/desktop-lite:1": {}
|
|
||||||
},
|
|
||||||
"forwardPorts": [6080, 5901],
|
|
||||||
"portsAttributes": {
|
|
||||||
"6080": {
|
|
||||||
"label": "desktop"
|
|
||||||
},
|
|
||||||
"5901": {
|
|
||||||
"label": "desktop"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"MD041": false, //first-line-heading
|
|
||||||
"MD013": false, //line-length
|
|
||||||
"MD033": false //inline-html
|
|
||||||
}
|
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"editor.detectIndentation": false,
|
"editor.detectIndentation": false,
|
||||||
"editor.tabSize": 2,
|
"editor.tabSize": 2,
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true
|
||||||
"editor.wordWrap": "off"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["hui", "hui-derive", "hui-examples", "hui-glium", "hui-winit"]
|
members = ["hui", "hui-examples", "hui-glium", "hui-winit"]
|
||||||
|
|
40
README.md
40
README.md
|
@ -1,5 +1,5 @@
|
||||||
<p></p><p></p>
|
<p></p><p></p>
|
||||||
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/hui.svg" width="120" align="left" alt="logo">
|
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/hui.svg" width="120" align="left">
|
||||||
<h1>hUI</h1>
|
<h1>hUI</h1>
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
|
@ -17,25 +17,22 @@
|
||||||
|
|
||||||
<table align="center">
|
<table align="center">
|
||||||
<td>
|
<td>
|
||||||
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/demo0.gif" width="300" alt="example: mom_downloader">
|
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/demo0.gif" width="300">
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/demo1.gif" width="300" alt="example: align_test">
|
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/demo1.gif" width="300">
|
||||||
</td>
|
</td>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Example</h2>
|
<h2>Example</h2>
|
||||||
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/exemplaris.png"
|
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/exemplaris.png" height="175" align="right" float="right">
|
||||||
height="175" align="right" float="right" alt="code result">
|
|
||||||
<pre lang="rust">Container::default()
|
<pre lang="rust">Container::default()
|
||||||
.with_size(size!(100%, 50%))
|
.with_size(size!(100%, 50%))
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
.with_padding(5.)
|
.with_padding(5.)
|
||||||
.with_gap(10.)
|
.with_gap(10.)
|
||||||
.with_background(frame_rect! {
|
.with_corner_radius(10.)
|
||||||
color: (0.5, 0.5, 0.5, 1.),
|
.with_background(color::WHITE)
|
||||||
corner_radius: 10.,
|
|
||||||
})
|
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Text::default()
|
Text::default()
|
||||||
.with_text("Hello, world")
|
.with_text("Hello, world")
|
||||||
|
@ -44,10 +41,8 @@
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_padding((10., 20.))
|
.with_padding((10., 20.))
|
||||||
.with_background(frame_rect! {
|
.with_corner_radius((2.5, 30., 2.5, 2.5))
|
||||||
color: color::DARK_RED,
|
.with_background(color::DARK_RED)
|
||||||
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.")
|
||||||
|
@ -56,7 +51,7 @@
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_root(ui, size);</pre>
|
.add_root(&mut hui, resolution);</pre>
|
||||||
|
|
||||||
<h2>Backends</h2>
|
<h2>Backends</h2>
|
||||||
<p>
|
<p>
|
||||||
|
@ -94,20 +89,6 @@
|
||||||
</td>
|
</td>
|
||||||
<td align="center">(support planned)</td>
|
<td align="center">(support planned)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
<code>0.1.0-alpha.4</code>
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<code>hui-glium = "0.1.0-alpha.4"</code><br>
|
|
||||||
<code>glium = "0.34"</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<code>hui-winit = "0.1.0-alpha.4"</code><br>
|
|
||||||
<code>winit = "0.29"</code>
|
|
||||||
</td>
|
|
||||||
<td align="center">N/A</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<code>0.1.0-alpha.3</code>
|
<code>0.1.0-alpha.3</code>
|
||||||
|
@ -159,6 +140,3 @@
|
||||||
<td align="center">-</td>
|
<td align="center">-</td>
|
||||||
</tr> -->
|
</tr> -->
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>MSRV</h2>
|
|
||||||
1.75
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hui-derive"
|
|
||||||
description = "Derive macros for hUI"
|
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
|
||||||
readme = "../README.md"
|
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
|
||||||
rust-version = "1.75"
|
|
||||||
version = "0.1.0-alpha.5"
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
publish = true
|
|
||||||
include = [
|
|
||||||
"assets/**/*",
|
|
||||||
"src/**/*.rs",
|
|
||||||
"Cargo.toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
quote = "1.0"
|
|
||||||
syn = "2.0"
|
|
|
@ -1,13 +0,0 @@
|
||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
|
||||||
|
|
||||||
/// Implements `Signal` trait for the given type
|
|
||||||
#[proc_macro_derive(Signal)]
|
|
||||||
pub fn signal(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let name = input.ident;
|
|
||||||
quote!(impl ::hui::signal::Signal for #name {}).into()
|
|
||||||
}
|
|
|
@ -14,6 +14,5 @@ glium = "0.34"
|
||||||
winit = "0.29"
|
winit = "0.29"
|
||||||
glam = "0.27"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
image = { version = "0.25", features = ["jpeg", "png"] }
|
|
||||||
|
|
||||||
#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)
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 638 B |
|
@ -62,7 +62,7 @@ fn main() {
|
||||||
children: ElementList(vec![
|
children: ElementList(vec![
|
||||||
Box::new(ProgressBar {
|
Box::new(ProgressBar {
|
||||||
value: z,
|
value: z,
|
||||||
// corner_radius: Corners::all(0.25 * ProgressBar::DEFAULT_HEIGHT),
|
corner_radius: Corners::all(0.25 * ProgressBar::DEFAULT_HEIGHT),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
Box::new(Container {
|
Box::new(Container {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use hui::{
|
use hui::{
|
||||||
color, element::{
|
element::{
|
||||||
container::Container,
|
container::Container,
|
||||||
progress_bar::ProgressBar,
|
progress_bar::ProgressBar,
|
||||||
text::Text,
|
text::Text,
|
||||||
|
@ -43,14 +43,7 @@ ui_main!{
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
ProgressBar::default()
|
ProgressBar::default()
|
||||||
.with_value(mom_ratio)
|
.with_value(mom_ratio)
|
||||||
.with_background(frame_rect! {
|
.with_corner_radius(0.125 * ProgressBar::DEFAULT_HEIGHT)
|
||||||
color: color::BLACK,
|
|
||||||
corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT
|
|
||||||
})
|
|
||||||
.with_foreground(frame_rect! {
|
|
||||||
color: color::BLUE,
|
|
||||||
corner_radius: 0.125 * ProgressBar::DEFAULT_HEIGHT
|
|
||||||
})
|
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_direction(Direction::Horizontal)
|
.with_direction(Direction::Horizontal)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use hui::{
|
use hui::{
|
||||||
color, size,
|
color, size,
|
||||||
signal::Signal,
|
|
||||||
draw::TextureFormat,
|
draw::TextureFormat,
|
||||||
|
signal::Signal,
|
||||||
layout::{Alignment, Direction},
|
layout::{Alignment, Direction},
|
||||||
element::{
|
element::{
|
||||||
container::Container,
|
container::Container,
|
||||||
|
@ -13,11 +13,11 @@ use hui::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Signal)]
|
|
||||||
enum CounterSignal {
|
enum CounterSignal {
|
||||||
Increment,
|
Increment,
|
||||||
Decrement,
|
Decrement,
|
||||||
}
|
}
|
||||||
|
impl Signal for CounterSignal {}
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -8,15 +8,15 @@ use hui::{
|
||||||
text::Text,
|
text::Text,
|
||||||
UiElementExt,
|
UiElementExt,
|
||||||
},
|
},
|
||||||
signal::Signal,
|
|
||||||
layout::{Alignment, Direction},
|
layout::{Alignment, Direction},
|
||||||
|
signal::Signal,
|
||||||
size,
|
size,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Signal)]
|
|
||||||
enum CounterSignal {
|
enum CounterSignal {
|
||||||
ChangeValue(u32)
|
ChangeValue(u32)
|
||||||
}
|
}
|
||||||
|
impl Signal for CounterSignal {}
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
|
|
@ -1,92 +1,34 @@
|
||||||
use glam::vec2;
|
use std::time::Instant;
|
||||||
use hui::{
|
use hui::{
|
||||||
color,
|
color, element::{
|
||||||
element::{
|
|
||||||
container::Container,
|
container::Container,
|
||||||
fill_rect::FillRect,
|
fill_rect::FillRect,
|
||||||
slider::Slider,
|
|
||||||
text::Text,
|
|
||||||
UiElementExt
|
UiElementExt
|
||||||
},
|
}, frame_rect, layout::{Alignment, Direction}, size
|
||||||
frame::nine_patch::{NinePatchAsset, NinePatchFrame},
|
|
||||||
layout::Alignment,
|
|
||||||
rect::Rect,
|
|
||||||
signal::Signal,
|
|
||||||
size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[path = "../boilerplate.rs"]
|
#[path = "../boilerplate.rs"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod boilerplate;
|
mod boilerplate;
|
||||||
|
|
||||||
#[derive(Signal)]
|
|
||||||
struct SetValue(f32);
|
|
||||||
|
|
||||||
ui_main!(
|
ui_main!(
|
||||||
"hUI: 9-Patch demo",
|
"hUI: 9-Patch demo",
|
||||||
init: |ui| {
|
init: |_| {
|
||||||
(
|
|
||||||
NinePatchAsset {
|
|
||||||
image: ui.add_image_file_path("./hui-examples/assets/ninepatch_button.png").unwrap(),
|
|
||||||
size: (190, 49),
|
|
||||||
scalable_region: Rect {
|
|
||||||
position: vec2(8. / 190., 8. / 49.),
|
|
||||||
size: vec2(1. - 16. / 190., 1. - 18. / 49.),
|
|
||||||
},
|
},
|
||||||
},
|
run: |ui, size, _| {
|
||||||
0.33,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
run: |ui, size, (asset, value)| {
|
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%))
|
.with_size(size!(100%))
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
.with_gap(5.)
|
|
||||||
.with_background(color::WHITE)
|
.with_background(color::WHITE)
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
Container::default()
|
FillRect::default()
|
||||||
.with_size(size!(300, 100))
|
.with_size(size!(300, 100))
|
||||||
.with_background(NinePatchFrame::from_asset(*asset).with_color(color::RED))
|
.with_frame(frame_rect! {
|
||||||
.with_padding(10.)
|
color: color::RED
|
||||||
.with_children(|ui| {
|
|
||||||
Text::new("Hello, world!\nThis is a 9-patch frame used as a background \nfor Container with a Text element.\nIt's scalable and looks great!\nBelow, there are two FillRects with the same \n9-patch frame used as the background.")
|
|
||||||
.with_text_size(16)
|
|
||||||
.add_child(ui);
|
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
FillRect::default()
|
|
||||||
.with_size(size!(600, 75))
|
|
||||||
.with_frame(NinePatchFrame::from_asset(*asset).with_color(color::GREEN))
|
|
||||||
.add_child(ui);
|
|
||||||
Text::new("This one's fancy:")
|
|
||||||
.with_color(color::BLACK)
|
|
||||||
.with_text_size(32)
|
|
||||||
.add_child(ui);
|
|
||||||
FillRect::default()
|
|
||||||
.with_size(size!(700, 50))
|
|
||||||
.with_frame(NinePatchFrame::from_asset(*asset).with_color((
|
|
||||||
(1., 0., 1.),
|
|
||||||
(0., 1., 1.),
|
|
||||||
(1., 1., 0.),
|
|
||||||
(0., 0., 1.),
|
|
||||||
)))
|
|
||||||
.add_child(ui);
|
|
||||||
Text::new("Slider customized with `NinePatchFrame`s:")
|
|
||||||
.with_color(color::BLACK)
|
|
||||||
.with_text_size(32)
|
|
||||||
.add_child(ui);
|
|
||||||
Slider::new(*value)
|
|
||||||
.with_size(size!(50%, 30))
|
|
||||||
.with_track_height(1.)
|
|
||||||
.with_handle_size((20., 1.))
|
|
||||||
.with_handle(NinePatchFrame::from_asset(*asset).with_color(color::CYAN))
|
|
||||||
.with_track(NinePatchFrame::from_asset(*asset))
|
|
||||||
.with_track_active(NinePatchFrame::from_asset(*asset).with_color(color::SKY_BLUE))
|
|
||||||
.on_change(SetValue)
|
|
||||||
.add_child(ui);
|
|
||||||
})
|
})
|
||||||
.add_root(ui, size);
|
.add_root(ui, size);
|
||||||
|
|
||||||
ui.process_signals::<SetValue>(|signal| *value = signal.0);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,7 +38,7 @@ ui_main!(
|
||||||
.with_size(size!(100%, auto))
|
.with_size(size!(100%, auto))
|
||||||
.with_direction(Direction::Horizontal)
|
.with_direction(Direction::Horizontal)
|
||||||
.with_align((Alignment::Begin, Alignment::Center))
|
.with_align((Alignment::Begin, Alignment::Center))
|
||||||
.with_padding(5.)
|
.with_padding(8.)
|
||||||
.with_gap(15.)
|
.with_gap(15.)
|
||||||
.with_background(color::rgb_hex(0x3d3c3e))
|
.with_background(color::rgb_hex(0x3d3c3e))
|
||||||
.with_wrap(true) //XXX: not authentic but great for demostration
|
.with_wrap(true) //XXX: not authentic but great for demostration
|
||||||
|
@ -51,15 +51,6 @@ ui_main!(
|
||||||
.with_text_size(15)
|
.with_text_size(15)
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
}
|
}
|
||||||
Container::default()
|
|
||||||
.with_size(size!(100%=, 100%))
|
|
||||||
.with_align((Alignment::End, Alignment::Center))
|
|
||||||
.with_children(|ui| {
|
|
||||||
Text::new("- ×")
|
|
||||||
.with_text_size(32)
|
|
||||||
.add_child(ui);
|
|
||||||
})
|
|
||||||
.add_child(ui);
|
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
FillRect::default()
|
FillRect::default()
|
||||||
|
@ -67,10 +58,9 @@ ui_main!(
|
||||||
.with_frame(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%))
|
||||||
.with_direction(Direction::Horizontal)
|
.with_direction(Direction::Horizontal)
|
||||||
.with_children(|ui| {
|
.with_children(|ui| {
|
||||||
// Sidebar:
|
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(54, 100%))
|
.with_size(size!(54, 100%))
|
||||||
.with_background(color::rgb_hex(0x343334))
|
.with_background(color::rgb_hex(0x343334))
|
||||||
|
@ -79,8 +69,6 @@ ui_main!(
|
||||||
.with_size(size!(1, 100%))
|
.with_size(size!(1, 100%))
|
||||||
.with_frame(color::rgb_hex(0x2d2d30))
|
.with_frame(color::rgb_hex(0x2d2d30))
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
|
|
||||||
// Explorer pane:
|
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(200, 100%))
|
.with_size(size!(200, 100%))
|
||||||
.with_padding((15., 8.))
|
.with_padding((15., 8.))
|
||||||
|
@ -90,16 +78,20 @@ ui_main!(
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
|
|
||||||
// "Code" pane
|
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%=, 100%))
|
.with_size(size!(100%, 100%))
|
||||||
.with_background(color::rgb_hex(0x1f1e1f))
|
.with_background(color::rgb_hex(0x1f1e1f))
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
})
|
})
|
||||||
.add_child(ui);
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
|
||||||
//Status bar
|
//Bottom bar (yeah, it's basically fake/overlay)
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align((Alignment::Begin, Alignment::End))
|
||||||
|
.with_children(|ui| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%, auto))
|
.with_size(size!(100%, auto))
|
||||||
.with_background(color::rgb_hex(0x0079cc))
|
.with_background(color::rgb_hex(0x0079cc))
|
||||||
|
|
|
@ -4,8 +4,7 @@ description = "glium render backend for `hui`"
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.4"
|
||||||
rust-version = "1.75"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
|
@ -16,7 +15,7 @@ include = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui = { version = "=0.1.0-alpha.5", 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.27"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
#version 150 core
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
out vec4 out_color;
|
|
||||||
in vec4 vtx_color;
|
|
||||||
in vec2 vtx_uv;
|
|
||||||
uniform sampler2D tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
out_color = texture(tex, vtx_uv) * vtx_color;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#version 150 core
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
|
|
||||||
uniform vec2 resolution;
|
|
||||||
in vec2 uv;
|
|
||||||
in vec4 color;
|
|
||||||
in vec2 position;
|
|
||||||
out vec4 vtx_color;
|
|
||||||
out vec2 vtx_uv;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vtx_color = color;
|
|
||||||
vtx_uv = uv;
|
|
||||||
vec2 pos2d = (vec2(2., -2.) * (position / resolution)) + vec2(-1, 1);
|
|
||||||
gl_Position = vec4(pos2d, 0., 1.);
|
|
||||||
}
|
|
|
@ -1,17 +1,19 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use glium::{
|
use glium::{
|
||||||
backend::{Context, Facade}, implement_vertex, index::PrimitiveType, texture::{RawImage2d, Texture2d}, uniform, uniforms::{MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction}, Api, Blend, DrawParameters, IndexBuffer, Program, Surface, VertexBuffer
|
Blend, DrawParameters, IndexBuffer, Program, Surface, VertexBuffer,
|
||||||
|
implement_vertex, uniform,
|
||||||
|
backend::{Context, Facade},
|
||||||
|
index::PrimitiveType,
|
||||||
|
texture::{RawImage2d, Texture2d},
|
||||||
|
uniforms::{MagnifySamplerFilter, MinifySamplerFilter, Sampler, SamplerBehavior, SamplerWrapFunction},
|
||||||
};
|
};
|
||||||
use hui::{
|
use hui::{
|
||||||
draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
|
draw::{TextureAtlasMeta, UiDrawCall, UiVertex}, UiInstance
|
||||||
};
|
};
|
||||||
|
|
||||||
const VERTEX_SHADER_GLES3: &str = include_str!("../shaders/vertex.es.vert");
|
const VERTEX_SHADER: &str = include_str!("../shaders/vertex.vert");
|
||||||
const FRAGMENT_SHADER_GLES3: &str = include_str!("../shaders/fragment.es.frag");
|
const FRAGMENT_SHADER: &str = include_str!("../shaders/fragment.frag");
|
||||||
|
|
||||||
const VERTEX_SHADER_150: &str = include_str!("../shaders/vertex.150.vert");
|
|
||||||
const FRAGMENT_SHADER_150: &str = include_str!("../shaders/fragment.150.frag");
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -120,10 +122,7 @@ impl GliumUiRenderer {
|
||||||
pub fn new<F: Facade>(facade: &F) -> Self {
|
pub fn new<F: Facade>(facade: &F) -> Self {
|
||||||
log::info!("initializing hui-glium");
|
log::info!("initializing hui-glium");
|
||||||
Self {
|
Self {
|
||||||
program: match facade.get_context().get_supported_glsl_version().0 {
|
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
||||||
Api::Gl => Program::from_source(facade, VERTEX_SHADER_150, FRAGMENT_SHADER_150, None).unwrap(),
|
|
||||||
Api::GlEs => Program::from_source(facade, VERTEX_SHADER_GLES3, FRAGMENT_SHADER_GLES3, None).unwrap(),
|
|
||||||
},
|
|
||||||
context: Rc::clone(facade.get_context()),
|
context: Rc::clone(facade.get_context()),
|
||||||
ui_texture: None,
|
ui_texture: None,
|
||||||
buffer_pair: None,
|
buffer_pair: None,
|
||||||
|
@ -184,7 +183,7 @@ impl GliumUiRenderer {
|
||||||
tex: Sampler(self.ui_texture.as_ref().unwrap(), SamplerBehavior {
|
tex: Sampler(self.ui_texture.as_ref().unwrap(), SamplerBehavior {
|
||||||
max_anisotropy: 1,
|
max_anisotropy: 1,
|
||||||
magnify_filter: MagnifySamplerFilter::Nearest,
|
magnify_filter: MagnifySamplerFilter::Nearest,
|
||||||
minify_filter: MinifySamplerFilter::Linear,
|
minify_filter: MinifySamplerFilter::NearestMipmapNearest,
|
||||||
wrap_function: (SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp),
|
wrap_function: (SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp, SamplerWrapFunction::Clamp),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -4,7 +4,7 @@ description = "winit platform backend for `hui`"
|
||||||
repository = "https://github.com/griffi-gh/hui"
|
repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
|
@ -14,7 +14,7 @@ include = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui = { version = "=0.1.0-alpha.5", 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.27"
|
glam = "0.27"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -5,7 +5,7 @@ repository = "https://github.com/griffi-gh/hui"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||||
rust-version = "1.75"
|
rust-version = "1.75"
|
||||||
version = "0.1.0-alpha.5"
|
version = "0.1.0-alpha.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
publish = true
|
publish = true
|
||||||
|
@ -16,7 +16,6 @@ include = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hui-derive = { version = "0.1.0-alpha.5", path = "../hui-derive", optional = true }
|
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
glam = "0.27"
|
glam = "0.27"
|
||||||
|
@ -30,14 +29,15 @@ tinyset = "0.4"
|
||||||
image = { version = "0.25", default-features = false, optional = true }
|
image = { version = "0.25", default-features = false, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["el_all", "image", "builtin_font", "pixel_perfect_text", "derive"]
|
default = ["el_all", "image", "builtin_font", "pixel_perfect_text"]
|
||||||
|
|
||||||
## Enable derive macros
|
#! Image loading support:
|
||||||
derive = ["dep:hui-derive"]
|
|
||||||
|
|
||||||
## Enable image loading support using the `image` crate
|
## Enable image loading support using the `image` crate
|
||||||
image = ["dep:image"]
|
image = ["dep:image"]
|
||||||
|
|
||||||
|
#! #### Built-in font:
|
||||||
|
|
||||||
## Enable the built-in font (ProggyTiny, adds *35kb* to the executable)
|
## Enable the built-in font (ProggyTiny, adds *35kb* to the executable)
|
||||||
builtin_font = []
|
builtin_font = []
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
//! draw commands, tesselation and UI rendering.
|
//! draw commands, tesselation and UI rendering.
|
||||||
|
|
||||||
|
//TODO: 9-slice draw command
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
rect::Corners,
|
rect::Corners,
|
||||||
text::{FontHandle, TextRenderer}
|
text::{FontHandle, TextRenderer}
|
||||||
|
@ -33,8 +35,6 @@ pub enum UiDrawCommand {
|
||||||
color: Corners<Vec4>,
|
color: Corners<Vec4>,
|
||||||
///Texture
|
///Texture
|
||||||
texture: Option<ImageHandle>,
|
texture: Option<ImageHandle>,
|
||||||
///Sub-UV coordinates for the texture
|
|
||||||
texture_uv: Option<Corners<Vec2>>,
|
|
||||||
///Rounded corners
|
///Rounded corners
|
||||||
rounded_corners: Option<RoundedCorners>,
|
rounded_corners: Option<RoundedCorners>,
|
||||||
},
|
},
|
||||||
|
@ -161,36 +161,10 @@ impl UiDrawCall {
|
||||||
v.position += center;
|
v.position += center;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UiDrawCommand::Rectangle { position, size, color, texture, texture_uv, rounded_corners } => {
|
UiDrawCommand::Rectangle { position, size, color, texture, rounded_corners } => {
|
||||||
let uvs = texture
|
let uvs = texture
|
||||||
.map(|x| atlas.get_uv(x))
|
.map(|x| atlas.get_uv(x))
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|guv| {
|
|
||||||
if let Some(texture_uv) = texture_uv {
|
|
||||||
//XXX: this may not work if the texture is rotated
|
|
||||||
//also is this slow?
|
|
||||||
|
|
||||||
let top = guv.top_left.lerp(guv.top_right, texture_uv.top_left.x);
|
|
||||||
let bottom = guv.bottom_left.lerp(guv.bottom_right, texture_uv.top_left.x);
|
|
||||||
let top_left = top.lerp(bottom, texture_uv.top_left.y);
|
|
||||||
|
|
||||||
let top = guv.top_left.lerp(guv.top_right, texture_uv.top_right.x);
|
|
||||||
let bottom = guv.bottom_left.lerp(guv.bottom_right, texture_uv.top_right.x);
|
|
||||||
let top_right = top.lerp(bottom, texture_uv.top_right.y);
|
|
||||||
|
|
||||||
let top = guv.top_left.lerp(guv.top_right, texture_uv.bottom_left.x);
|
|
||||||
let bottom = guv.bottom_left.lerp(guv.bottom_right, texture_uv.bottom_left.x);
|
|
||||||
let bottom_left = top.lerp(bottom, texture_uv.bottom_left.y);
|
|
||||||
|
|
||||||
let top = guv.top_left.lerp(guv.top_right, texture_uv.bottom_right.x);
|
|
||||||
let bottom = guv.bottom_left.lerp(guv.bottom_right, texture_uv.bottom_right.x);
|
|
||||||
let bottom_right = top.lerp(bottom, texture_uv.bottom_right.y);
|
|
||||||
|
|
||||||
Corners { top_left, top_right, bottom_left, bottom_right }
|
|
||||||
} else {
|
|
||||||
guv
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or(Corners::all(Vec2::ZERO));
|
.unwrap_or(Corners::all(Vec2::ZERO));
|
||||||
|
|
||||||
let vidx = draw_call.vertices.len() as u32;
|
let vidx = draw_call.vertices.len() as u32;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::any::Any;
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::{atlas::ImageCtx, UiDrawCommandList},
|
draw::{atlas::ImageCtx, UiDrawCommandList},
|
||||||
input::InputCtx,
|
input::InputCtx,
|
||||||
layout::{LayoutInfo, Size2d},
|
layout::LayoutInfo,
|
||||||
measure::Response,
|
measure::Response,
|
||||||
signal::SignalStore,
|
signal::SignalStore,
|
||||||
state::StateRepo,
|
state::StateRepo,
|
||||||
|
@ -45,11 +45,6 @@ pub trait UiElement {
|
||||||
/// For example, "button" or "progress_bar"
|
/// For example, "button" or "progress_bar"
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Get the requested UiElement size
|
|
||||||
///
|
|
||||||
/// You should implement this function whenever possible, otherwise some features may not work at all, such as the `Remaining` size
|
|
||||||
fn size(&self) -> Option<Size2d> { None }
|
|
||||||
|
|
||||||
/// Get the unique id used for internal state management\
|
/// Get the unique id used for internal state management\
|
||||||
/// This value must be unique for each instance of the element
|
/// This value must be unique for each instance of the element
|
||||||
///
|
///
|
||||||
|
|
|
@ -4,10 +4,10 @@ use derive_setters::Setters;
|
||||||
use glam::{Vec2, vec2};
|
use glam::{Vec2, vec2};
|
||||||
use crate::{
|
use crate::{
|
||||||
element::{ElementList, MeasureContext, ProcessContext, UiElement},
|
element::{ElementList, MeasureContext, ProcessContext, UiElement},
|
||||||
|
layout::{Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d},
|
||||||
frame::{Frame, FrameRect},
|
frame::{Frame, FrameRect},
|
||||||
layout::{compute_size, Alignment, Alignment2d, Direction, LayoutInfo, Size, Size2d, WrapBehavior},
|
|
||||||
measure::{Hints, Response},
|
measure::{Hints, Response},
|
||||||
rect::Sides,
|
rect::{Sides, FillColor},
|
||||||
};
|
};
|
||||||
|
|
||||||
//XXX: add Order/Direction::Forward/Reverse or sth?
|
//XXX: add Order/Direction::Forward/Reverse or sth?
|
||||||
|
@ -19,7 +19,6 @@ use crate::{
|
||||||
struct CudLine {
|
struct CudLine {
|
||||||
start_idx: usize,
|
start_idx: usize,
|
||||||
content_size: Vec2,
|
content_size: Vec2,
|
||||||
remaining_space: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContainerUserData {
|
struct ContainerUserData {
|
||||||
|
@ -53,9 +52,13 @@ pub struct Container {
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
pub background_frame: Box<dyn Frame>,
|
pub background_frame: Box<dyn Frame>,
|
||||||
|
|
||||||
/// Controls if wrapping is enabled
|
/// Set this to `true` to allow the elements wrap automatically
|
||||||
#[setters(into)]
|
///
|
||||||
pub wrap: WrapBehavior,
|
/// Disabling/enabling this does not affect explicit wrapping\
|
||||||
|
/// (for example, `Br`, or any other element with `should_wrap` set to `true`)
|
||||||
|
///
|
||||||
|
/// This is an experimental feature and may not work as expected
|
||||||
|
pub wrap: bool,
|
||||||
|
|
||||||
/// List of children elements
|
/// List of children elements
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
|
@ -83,7 +86,7 @@ impl Default for Container {
|
||||||
padding: Sides::all(0.),
|
padding: Sides::all(0.),
|
||||||
align: Alignment2d::default(),
|
align: Alignment2d::default(),
|
||||||
background_frame: Box::<FrameRect>::default(),
|
background_frame: Box::<FrameRect>::default(),
|
||||||
wrap: WrapBehavior::Allow,
|
wrap: false,
|
||||||
children: ElementList(Vec::new()),
|
children: ElementList(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,24 +94,19 @@ impl Default for Container {
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
pub fn measure_max_inner_size(&self, layout: &LayoutInfo) -> Vec2 {
|
pub fn measure_max_inner_size(&self, layout: &LayoutInfo) -> Vec2 {
|
||||||
// let outer_size_x = match self.size.width {
|
let outer_size_x = match self.size.width {
|
||||||
// Size::Auto => layout.max_size.x,
|
Size::Auto => layout.max_size.x,
|
||||||
// Size::Relative(p) => layout.max_size.x * p,
|
Size::Relative(p) => layout.max_size.x * p,
|
||||||
// Size::Absolute(p) => p,
|
Size::Absolute(p) => p,
|
||||||
// Size::Remaining(p) => match layout.direction {
|
};
|
||||||
// Direction::Horizontal => layout.remaining_space.unwrap_or(layout.max_size.x) * p,
|
let outer_size_y = match self.size.height {
|
||||||
// Direction::Vertical => layout.max_size.x,
|
Size::Auto => layout.max_size.y,
|
||||||
// }
|
Size::Relative(p) => layout.max_size.y * p,
|
||||||
// };
|
Size::Absolute(p) => p,
|
||||||
// let outer_size_y = match self.size.height {
|
};
|
||||||
// Size::Auto => layout.max_size.y,
|
|
||||||
// Size::Relative(p) => layout.max_size.y * p,
|
|
||||||
// Size::Absolute(p) => p,
|
|
||||||
// };
|
|
||||||
let outer_size = compute_size(layout, self.size, layout.max_size);
|
|
||||||
vec2(
|
vec2(
|
||||||
outer_size.x - (self.padding.left + self.padding.right),
|
outer_size_x - (self.padding.left + self.padding.right),
|
||||||
outer_size.y - (self.padding.top + self.padding.bottom),
|
outer_size_y - (self.padding.top + self.padding.bottom),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,10 +116,6 @@ impl UiElement for Container {
|
||||||
"container"
|
"container"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Option<Size2d> {
|
|
||||||
Some(self.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||||
// XXX: If both axes are NOT set to auto, we should be able quickly return the size
|
// XXX: If both axes are NOT set to auto, we should be able quickly return the size
|
||||||
// ... but we can't, because we need to measure the children to get the inner_content_size and user_data values
|
// ... but we can't, because we need to measure the children to get the inner_content_size and user_data values
|
||||||
|
@ -136,13 +130,11 @@ impl UiElement for Container {
|
||||||
Size::Auto => ctx.layout.max_size.x,
|
Size::Auto => ctx.layout.max_size.x,
|
||||||
Size::Relative(p) => ctx.layout.max_size.x * p,
|
Size::Relative(p) => ctx.layout.max_size.x * p,
|
||||||
Size::Absolute(p) => p,
|
Size::Absolute(p) => p,
|
||||||
Size::Remaining(p) => ctx.layout.remaining_space.unwrap_or(ctx.layout.max_size.x) * p,
|
|
||||||
},
|
},
|
||||||
Direction::Vertical => match self.size.height {
|
Direction::Vertical => match self.size.height {
|
||||||
Size::Auto => ctx.layout.max_size.y,
|
Size::Auto => ctx.layout.max_size.y,
|
||||||
Size::Relative(p) => ctx.layout.max_size.y * p,
|
Size::Relative(p) => ctx.layout.max_size.y * p,
|
||||||
Size::Absolute(p) => p,
|
Size::Absolute(p) => p,
|
||||||
Size::Remaining(p) => ctx.layout.remaining_space.unwrap_or(ctx.layout.max_size.y) * p,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,25 +160,10 @@ impl UiElement for Container {
|
||||||
CudLine {
|
CudLine {
|
||||||
start_idx: 0,
|
start_idx: 0,
|
||||||
content_size: Vec2::ZERO,
|
content_size: Vec2::ZERO,
|
||||||
remaining_space: 0.,
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
//set to true if in the current line there is an element with Remaining size (line will have to be wrapped)
|
|
||||||
// let mut has_remaining = false;
|
|
||||||
|
|
||||||
for (idx, element) in self.children.0.iter().enumerate() {
|
for (idx, element) in self.children.0.iter().enumerate() {
|
||||||
if let Some(esize) = element.size() {
|
|
||||||
let pri_size = match self.direction {
|
|
||||||
Direction::Horizontal => esize.width,
|
|
||||||
Direction::Vertical => esize.height,
|
|
||||||
};
|
|
||||||
if matches!(pri_size, Size::Remaining(_)) {
|
|
||||||
//XXX: kinda a hack?
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let measure = element.measure(MeasureContext{
|
let measure = element.measure(MeasureContext{
|
||||||
state: ctx.state,
|
state: ctx.state,
|
||||||
layout: &LayoutInfo {
|
layout: &LayoutInfo {
|
||||||
|
@ -199,7 +176,6 @@ impl UiElement for Container {
|
||||||
//TODO: subtract size already taken by previous children
|
//TODO: subtract size already taken by previous children
|
||||||
max_size: self.measure_max_inner_size(ctx.layout),
|
max_size: self.measure_max_inner_size(ctx.layout),
|
||||||
direction: self.direction,
|
direction: self.direction,
|
||||||
remaining_space: None,
|
|
||||||
},
|
},
|
||||||
text_measure: ctx.text_measure,
|
text_measure: ctx.text_measure,
|
||||||
current_font: ctx.current_font,
|
current_font: ctx.current_font,
|
||||||
|
@ -213,30 +189,19 @@ impl UiElement for Container {
|
||||||
};
|
};
|
||||||
|
|
||||||
//Wrap the element if it exceeds container's size and is not the first element in the line
|
//Wrap the element if it exceeds container's size and is not the first element in the line
|
||||||
let should_wrap_overflow = self.wrap.is_enabled() && (end_pos_pri > max_line_pri);
|
if ((self.wrap && (end_pos_pri > max_line_pri)) || measure.should_wrap) && (line_element_count > 0) {
|
||||||
if self.wrap.is_allowed() && line_element_count > 0 && (measure.should_wrap || should_wrap_overflow) {
|
|
||||||
// >>>>>>> WRAP THAT B*TCH!
|
// >>>>>>> WRAP THAT B*TCH!
|
||||||
|
|
||||||
//Negate the leftover gap from the previous element
|
//Negate the leftover gap from the previous element
|
||||||
line_size -= leftover_gap;
|
line_size -= leftover_gap;
|
||||||
|
|
||||||
//update the previous line metadata
|
//update the previous line metadata
|
||||||
{
|
lines.last_mut().unwrap().content_size = line_size;
|
||||||
let last_line = lines.last_mut().unwrap();
|
|
||||||
last_line.content_size = line_size;
|
|
||||||
//HACK: why? - self.gap, may be different for the last element or if it's the only element in the line
|
|
||||||
let will_produce_gap = if line_element_count > 1 { self.gap } else { 0. };
|
|
||||||
last_line.remaining_space = max_line_pri - will_produce_gap - match self.direction {
|
|
||||||
Direction::Horizontal => line_size.x + self.padding.left + self.padding.right,
|
|
||||||
Direction::Vertical => line_size.y + self.padding.top + self.padding.bottom,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//push the line metadata
|
//push the line metadata
|
||||||
lines.push(CudLine {
|
lines.push(CudLine {
|
||||||
start_idx: idx,
|
start_idx: idx,
|
||||||
content_size: Vec2::ZERO,
|
content_size: Vec2::ZERO,
|
||||||
remaining_space: 0.,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//Update the total size accordingly
|
//Update the total size accordingly
|
||||||
|
@ -287,16 +252,7 @@ impl UiElement for Container {
|
||||||
line_size -= leftover_gap;
|
line_size -= leftover_gap;
|
||||||
|
|
||||||
//Update the content size of the last line
|
//Update the content size of the last line
|
||||||
{
|
lines.last_mut().unwrap().content_size = line_size;
|
||||||
//HACK: why? - self.gap, may be different for the last element or if it's the only element in the line
|
|
||||||
let cur_line = lines.last_mut().unwrap();
|
|
||||||
cur_line.content_size = line_size;
|
|
||||||
let will_produce_gap = if line_element_count > 1 { self.gap } else { 0. };
|
|
||||||
cur_line.remaining_space = max_line_pri - will_produce_gap - match self.direction {
|
|
||||||
Direction::Horizontal => line_size.x + self.padding.left + self.padding.right,
|
|
||||||
Direction::Vertical => line_size.y + self.padding.top + self.padding.bottom,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the total size according to the size of the last line
|
//Update the total size according to the size of the last line
|
||||||
match self.direction {
|
match self.direction {
|
||||||
|
@ -322,27 +278,17 @@ impl UiElement for Container {
|
||||||
self.padding.top + self.padding.bottom,
|
self.padding.top + self.padding.bottom,
|
||||||
);
|
);
|
||||||
|
|
||||||
let computed_size = compute_size(ctx.layout, self.size, total_size);
|
|
||||||
match self.size.width {
|
match self.size.width {
|
||||||
Size::Auto => (),
|
Size::Auto => (),
|
||||||
_ => total_size.x = computed_size.x,
|
Size::Relative(percentage) => total_size.x = ctx.layout.max_size.x * percentage,
|
||||||
|
Size::Absolute(pixels) => total_size.x = pixels,
|
||||||
}
|
}
|
||||||
match self.size.height {
|
match self.size.height {
|
||||||
Size::Auto => (),
|
Size::Auto => (),
|
||||||
_ => total_size.y = computed_size.y,
|
Size::Relative(percentage) => total_size.y = ctx.layout.max_size.y * percentage,
|
||||||
|
Size::Absolute(pixels) => total_size.y = pixels,
|
||||||
}
|
}
|
||||||
|
|
||||||
// match self.size.width {
|
|
||||||
// Size::Auto => (),
|
|
||||||
// Size::Relative(percentage) => total_size.x = ctx.layout.max_size.x * percentage,
|
|
||||||
// Size::Absolute(pixels) => total_size.x = pixels,
|
|
||||||
// }
|
|
||||||
// match self.size.height {
|
|
||||||
// Size::Auto => (),
|
|
||||||
// Size::Relative(percentage) => total_size.y = ctx.layout.max_size.y * percentage,
|
|
||||||
// Size::Absolute(pixels) => total_size.y = pixels,
|
|
||||||
// }
|
|
||||||
|
|
||||||
Response {
|
Response {
|
||||||
size: total_size,
|
size: total_size,
|
||||||
hints: Hints {
|
hints: Hints {
|
||||||
|
@ -439,7 +385,6 @@ impl UiElement for Container {
|
||||||
position: local_position,
|
position: local_position,
|
||||||
max_size: self.measure_max_inner_size(ctx.layout),
|
max_size: self.measure_max_inner_size(ctx.layout),
|
||||||
direction: self.direction,
|
direction: self.direction,
|
||||||
remaining_space: Some(cur_line.remaining_space),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//measure
|
//measure
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
draw::{RoundedCorners, UiDrawCommand},
|
draw::{RoundedCorners, UiDrawCommand},
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
frame::{Frame, FrameRect},
|
frame::{Frame, FrameRect},
|
||||||
layout::{compute_size, Size, Size2d},
|
layout::{Size, Size2d},
|
||||||
measure::Response,
|
measure::Response,
|
||||||
size
|
size
|
||||||
};
|
};
|
||||||
|
@ -45,13 +45,20 @@ impl UiElement for FillRect {
|
||||||
"fill_rect"
|
"fill_rect"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Option<Size2d> {
|
|
||||||
Some(self.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||||
Response {
|
Response {
|
||||||
size: compute_size(ctx.layout, self.size, ctx.layout.max_size),
|
size: vec2(
|
||||||
|
match self.size.width {
|
||||||
|
Size::Auto => ctx.layout.max_size.x,
|
||||||
|
Size::Relative(percentage) => ctx.layout.max_size.x * percentage,
|
||||||
|
Size::Absolute(pixels) => pixels,
|
||||||
|
},
|
||||||
|
match self.size.height {
|
||||||
|
Size::Auto => ctx.layout.max_size.y,
|
||||||
|
Size::Relative(percentage) => ctx.layout.max_size.y * percentage,
|
||||||
|
Size::Absolute(pixels) => pixels,
|
||||||
|
},
|
||||||
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,6 @@ impl UiElement for Image {
|
||||||
"image"
|
"image"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Option<Size2d> {
|
|
||||||
Some(self.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||||
let dim = ctx.images.get_size(self.image).expect("invalid image handle");
|
let dim = ctx.images.get_size(self.image).expect("invalid image handle");
|
||||||
let pre_size = compute_size(ctx.layout, self.size, dim.as_vec2());
|
let pre_size = compute_size(ctx.layout, self.size, dim.as_vec2());
|
||||||
|
@ -83,7 +79,6 @@ impl UiElement for Image {
|
||||||
size: ctx.measure.size,
|
size: ctx.measure.size,
|
||||||
color: self.color.corners(),
|
color: self.color.corners(),
|
||||||
texture: Some(self.image),
|
texture: Some(self.image),
|
||||||
texture_uv: 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)
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -48,10 +48,6 @@ impl UiElement for Interactable {
|
||||||
"interactable"
|
"interactable"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Option<crate::layout::Size2d> {
|
|
||||||
self.element.size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure(&self, ctx: MeasureContext) -> crate::measure::Response {
|
fn measure(&self, ctx: MeasureContext) -> crate::measure::Response {
|
||||||
self.element.measure(ctx)
|
self.element.measure(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use glam::vec2;
|
use glam::{vec2, vec4};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
draw::{RoundedCorners, UiDrawCommand},
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
frame::{Frame, FrameRect},
|
|
||||||
layout::{compute_size, Size, Size2d},
|
layout::{compute_size, Size, Size2d},
|
||||||
measure::Response,
|
measure::Response,
|
||||||
|
rect::{Corners, FillColor}
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: Use Frames here instead of FillColor
|
#[derive(Debug, Clone, Copy, Setters)]
|
||||||
|
|
||||||
#[derive(Setters)]
|
|
||||||
#[setters(prefix = "with_")]
|
#[setters(prefix = "with_")]
|
||||||
pub struct ProgressBar {
|
pub struct ProgressBar {
|
||||||
/// Current progress, should be in the range 0.0..=1.0
|
/// Current progress, should be in the range 0.0..=1.0
|
||||||
|
@ -20,26 +19,20 @@ pub struct ProgressBar {
|
||||||
pub size: Size2d,
|
pub size: Size2d,
|
||||||
|
|
||||||
/// Foreground (bar) color
|
/// Foreground (bar) color
|
||||||
#[setters(skip)]
|
#[setters(into)]
|
||||||
pub foreground: Box<dyn Frame>,
|
pub foreground: FillColor,
|
||||||
|
|
||||||
/// Background color
|
/// Background color
|
||||||
#[setters(skip)]
|
#[setters(into)]
|
||||||
pub background: Box<dyn Frame>,
|
pub background: FillColor,
|
||||||
|
|
||||||
|
/// Corner radius of the progress bar
|
||||||
|
#[setters(into)]
|
||||||
|
pub corner_radius: Corners<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgressBar {
|
impl ProgressBar {
|
||||||
pub const DEFAULT_HEIGHT: f32 = 20.0;
|
pub const DEFAULT_HEIGHT: f32 = 20.0;
|
||||||
|
|
||||||
pub fn with_background(mut self, frame: impl Frame + 'static) -> Self {
|
|
||||||
self.background = Box::new(frame);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_foreground(mut self, frame: impl Frame + 'static) -> Self {
|
|
||||||
self.foreground = Box::new(frame);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProgressBar {
|
impl Default for ProgressBar {
|
||||||
|
@ -47,8 +40,9 @@ impl Default for ProgressBar {
|
||||||
Self {
|
Self {
|
||||||
value: 0.,
|
value: 0.,
|
||||||
size: Size::Auto.into(),
|
size: Size::Auto.into(),
|
||||||
foreground: Box::new(FrameRect::color((0.0, 0.0, 1.0, 1.0))),
|
foreground: vec4(0.0, 0.0, 1.0, 1.0).into(),
|
||||||
background: Box::new(FrameRect::color((0.0, 0.0, 0.0, 1.0))),
|
background: vec4(0.0, 0.0, 0.0, 1.0).into(),
|
||||||
|
corner_radius: Corners::all(0.),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +55,7 @@ impl UiElement for ProgressBar {
|
||||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||||
Response {
|
Response {
|
||||||
size: compute_size(ctx.layout, self.size, vec2(
|
size: compute_size(ctx.layout, self.size, vec2(
|
||||||
ctx.layout.max_size.x.max(300.), //XXX: remove .max(300)?
|
ctx.layout.max_size.x.max(300.),
|
||||||
Self::DEFAULT_HEIGHT,
|
Self::DEFAULT_HEIGHT,
|
||||||
)),
|
)),
|
||||||
hints: Default::default(),
|
hints: Default::default(),
|
||||||
|
@ -72,49 +66,38 @@ impl UiElement for ProgressBar {
|
||||||
|
|
||||||
fn process(&self, ctx: ProcessContext) {
|
fn process(&self, ctx: ProcessContext) {
|
||||||
let value = self.value.clamp(0., 1.);
|
let value = self.value.clamp(0., 1.);
|
||||||
|
let rounded_corners =
|
||||||
//FIXME: these optimizations may not be valid
|
(self.corner_radius.max_f32() > 0.).then_some({
|
||||||
if value < 1. || !self.foreground.covers_opaque() {
|
//HACK: fix clipping issues; //todo: get rid of this
|
||||||
self.background.draw(ctx.draw, ctx.layout.position, ctx.measure.size);
|
let mut radii = self.corner_radius;
|
||||||
|
let width = ctx.measure.size.x * value;
|
||||||
|
if width <= radii.max_f32() * 2. {
|
||||||
|
radii.bottom_right = 0.;
|
||||||
|
radii.top_right = 0.;
|
||||||
|
}
|
||||||
|
if width <= radii.max_f32() {
|
||||||
|
radii.bottom_left = 0.;
|
||||||
|
radii.top_left = 0.;
|
||||||
|
}
|
||||||
|
RoundedCorners::from_radius(radii)
|
||||||
|
});
|
||||||
|
if value < 1. {
|
||||||
|
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||||
|
position: ctx.layout.position,
|
||||||
|
size: ctx.measure.size,
|
||||||
|
color: self.background.corners(),
|
||||||
|
texture: None,
|
||||||
|
rounded_corners
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if value > 0. {
|
if value > 0. {
|
||||||
self.foreground.draw(ctx.draw, ctx.layout.position, ctx.measure.size * vec2(value, 1.));
|
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||||
}
|
position: ctx.layout.position,
|
||||||
|
size: ctx.measure.size * vec2(value, 1.0),
|
||||||
// let rounded_corners =
|
color: self.foreground.corners(),
|
||||||
// (self.corner_radius.max_f32() > 0.).then_some({
|
texture: None,
|
||||||
// //HACK: fix clipping issues; //todo: get rid of this
|
rounded_corners,
|
||||||
// let mut radii = self.corner_radius;
|
});
|
||||||
// let width = ctx.measure.size.x * value;
|
}
|
||||||
// if width <= radii.max_f32() * 2. {
|
|
||||||
// radii.bottom_right = 0.;
|
|
||||||
// radii.top_right = 0.;
|
|
||||||
// }
|
|
||||||
// if width <= radii.max_f32() {
|
|
||||||
// radii.bottom_left = 0.;
|
|
||||||
// radii.top_left = 0.;
|
|
||||||
// }
|
|
||||||
// RoundedCorners::from_radius(radii)
|
|
||||||
// });
|
|
||||||
// if value < 1. {
|
|
||||||
// ctx.draw.add(UiDrawCommand::Rectangle {
|
|
||||||
// position: ctx.layout.position,
|
|
||||||
// size: ctx.measure.size,
|
|
||||||
// color: self.background.corners(),
|
|
||||||
// texture: None,
|
|
||||||
// texture_uv: None,
|
|
||||||
// rounded_corners
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// if value > 0. {
|
|
||||||
// ctx.draw.add(UiDrawCommand::Rectangle {
|
|
||||||
// position: ctx.layout.position,
|
|
||||||
// size: ctx.measure.size * vec2(value, 1.0),
|
|
||||||
// color: self.foreground.corners(),
|
|
||||||
// texture: None,
|
|
||||||
// texture_uv: None,
|
|
||||||
// rounded_corners,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use glam::{vec2, Vec4};
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::UiDrawCommand,
|
draw::UiDrawCommand,
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
layout::{compute_size, Size, Size2d},
|
layout::{Size, Size2d},
|
||||||
measure::Response,
|
measure::Response,
|
||||||
text::FontHandle,
|
text::FontHandle,
|
||||||
};
|
};
|
||||||
|
@ -74,10 +74,6 @@ impl UiElement for Text {
|
||||||
"text"
|
"text"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Option<Size2d> {
|
|
||||||
Some(self.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||||
let mut size = (0., 0.);
|
let mut size = (0., 0.);
|
||||||
if matches!(self.size.width, Size::Auto) || matches!(self.size.height, Size::Auto) {
|
if matches!(self.size.width, Size::Auto) || matches!(self.size.height, Size::Auto) {
|
||||||
|
@ -87,7 +83,18 @@ impl UiElement for Text {
|
||||||
size.1 = res.height;
|
size.1 = res.height;
|
||||||
}
|
}
|
||||||
Response {
|
Response {
|
||||||
size: compute_size(ctx.layout, self.size, size.into()),
|
size: vec2(
|
||||||
|
match self.size.width {
|
||||||
|
Size::Auto => size.0,
|
||||||
|
Size::Relative(percentage) => ctx.layout.max_size.x * percentage,
|
||||||
|
Size::Absolute(pixels) => pixels,
|
||||||
|
},
|
||||||
|
match self.size.height {
|
||||||
|
Size::Auto => size.1,
|
||||||
|
Size::Relative(percentage) => ctx.layout.max_size.y * percentage,
|
||||||
|
Size::Absolute(pixels) => pixels,
|
||||||
|
},
|
||||||
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
//! modular procedural background system
|
|
||||||
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use crate::draw::UiDrawCommandList;
|
use crate::draw::UiDrawCommandList;
|
||||||
|
|
||||||
pub mod point;
|
pub mod point;
|
||||||
mod rect;
|
mod rect;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
pub mod nine_patch;
|
|
||||||
mod impls;
|
mod impls;
|
||||||
|
|
||||||
pub use rect::FrameRect;
|
pub use rect::FrameRect;
|
||||||
|
|
||||||
/// Trait for a drawable frame
|
|
||||||
pub trait Frame {
|
pub trait Frame {
|
||||||
/// Draw the frame at the given position and size
|
/// Draw the frame at the given 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);
|
||||||
|
|
|
@ -13,7 +13,6 @@ impl Frame for ImageHandle {
|
||||||
size: parent_size,
|
size: parent_size,
|
||||||
color: color::WHITE.into(),
|
color: color::WHITE.into(),
|
||||||
texture: Some(*self),
|
texture: Some(*self),
|
||||||
texture_uv: None,
|
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -30,7 +29,6 @@ impl Frame for FillColor {
|
||||||
size: parent_size,
|
size: parent_size,
|
||||||
color: self.corners(),
|
color: self.corners(),
|
||||||
texture: None,
|
texture: None,
|
||||||
texture_uv: None,
|
|
||||||
rounded_corners: None,
|
rounded_corners: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,234 +0,0 @@
|
||||||
//! nine-patch frame implementation
|
|
||||||
//!
|
|
||||||
//! A 9-patch image is an image that can be scaled in a way that preserves the corners and edges of the image while scaling the center.
|
|
||||||
//! This is useful for creating scalable UI elements like buttons, windows, etc.
|
|
||||||
|
|
||||||
use glam::{vec2, UVec2, Vec2};
|
|
||||||
use crate::{color, draw::{ImageHandle, UiDrawCommand}, rect::{Corners, FillColor, Rect}};
|
|
||||||
use super::Frame;
|
|
||||||
|
|
||||||
/// Represents a 9-patch image asset
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct NinePatchAsset {
|
|
||||||
pub image: ImageHandle,
|
|
||||||
//TODO: remove this:
|
|
||||||
pub size: (u32, u32),
|
|
||||||
pub scalable_region: Rect,
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO allow scaling/moving corners
|
|
||||||
|
|
||||||
/// A 9-patch frame
|
|
||||||
///
|
|
||||||
/// Can optionally be tinted with a color (works well with grayscale assets)
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct NinePatchFrame {
|
|
||||||
pub asset: NinePatchAsset,
|
|
||||||
pub color: FillColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NinePatchFrame {
|
|
||||||
pub fn from_asset(asset: NinePatchAsset) -> Self {
|
|
||||||
Self { asset, ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_color(mut self, color: impl Into<FillColor>) -> Self {
|
|
||||||
self.color = color.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for NinePatchFrame {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
//This is not supposed to be left out as the default, so just set it to whatever :p
|
|
||||||
asset: NinePatchAsset { image: ImageHandle::default(), size: (0, 0), scalable_region: Rect::default() },
|
|
||||||
color: color::WHITE.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frame for NinePatchFrame {
|
|
||||||
fn draw(&self, draw: &mut crate::draw::UiDrawCommandList, position: glam::Vec2, parent_size: glam::Vec2) {
|
|
||||||
// without this, shїt gets messed up when the position is not a whole number
|
|
||||||
//XXX: should we round the size as well?
|
|
||||||
let position = position.round();
|
|
||||||
|
|
||||||
let img_sz = UVec2::from(self.asset.size).as_vec2();
|
|
||||||
|
|
||||||
//Color stuff
|
|
||||||
let interpolate_color_rect = |uvs: Corners<Vec2>| {
|
|
||||||
Corners {
|
|
||||||
top_left: self.color.interpolate(uvs.top_left),
|
|
||||||
top_right: self.color.interpolate(uvs.top_right),
|
|
||||||
bottom_left: self.color.interpolate(uvs.bottom_left),
|
|
||||||
bottom_right: self.color.interpolate(uvs.bottom_right),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inset coords, in UV space
|
|
||||||
let region_uv = self.asset.scalable_region.corners();
|
|
||||||
|
|
||||||
// Inset coords, in image (px) space
|
|
||||||
let corners_image_px = Corners {
|
|
||||||
top_left: img_sz * region_uv.top_left,
|
|
||||||
top_right: img_sz * region_uv.top_right,
|
|
||||||
bottom_left: img_sz * region_uv.bottom_left,
|
|
||||||
bottom_right: img_sz * region_uv.bottom_right,
|
|
||||||
};
|
|
||||||
|
|
||||||
let size_h = (
|
|
||||||
corners_image_px.top_left.x,
|
|
||||||
parent_size.x - corners_image_px.top_left.x - (img_sz.x - corners_image_px.top_right.x),
|
|
||||||
img_sz.x - corners_image_px.top_right.x,
|
|
||||||
);
|
|
||||||
|
|
||||||
let size_v = (
|
|
||||||
corners_image_px.top_left.y,
|
|
||||||
parent_size.y - corners_image_px.top_left.y - (img_sz.y - corners_image_px.bottom_left.y),
|
|
||||||
img_sz.y - corners_image_px.bottom_left.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
//Top-left patch
|
|
||||||
let top_left_patch_uv = Corners {
|
|
||||||
top_left: vec2(0., 0.),
|
|
||||||
top_right: vec2(region_uv.top_left.x, 0.),
|
|
||||||
bottom_left: vec2(0., region_uv.top_left.y),
|
|
||||||
bottom_right: region_uv.top_left,
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position,
|
|
||||||
size: vec2(size_h.0, size_v.0),
|
|
||||||
color: interpolate_color_rect(top_left_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(top_left_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Top patch
|
|
||||||
let top_patch_uv = Corners {
|
|
||||||
top_left: vec2(region_uv.top_left.x, 0.),
|
|
||||||
top_right: vec2(region_uv.top_right.x, 0.),
|
|
||||||
bottom_left: region_uv.top_left,
|
|
||||||
bottom_right: region_uv.top_right,
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0, 0.),
|
|
||||||
size: vec2(size_h.1, size_v.0),
|
|
||||||
color: interpolate_color_rect(top_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(top_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Top-right patch
|
|
||||||
let top_right_patch_uv = Corners {
|
|
||||||
top_left: vec2(region_uv.top_right.x, 0.),
|
|
||||||
top_right: vec2(1., 0.),
|
|
||||||
bottom_left: region_uv.top_right,
|
|
||||||
bottom_right: vec2(1., region_uv.top_right.y),
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0 + size_h.1, 0.),
|
|
||||||
size: vec2(size_h.2, size_v.0),
|
|
||||||
color: interpolate_color_rect(top_right_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(top_right_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Left patch
|
|
||||||
let left_patch_uv = Corners {
|
|
||||||
top_left: vec2(0., region_uv.top_left.y),
|
|
||||||
top_right: region_uv.top_left,
|
|
||||||
bottom_left: vec2(0., region_uv.bottom_left.y),
|
|
||||||
bottom_right: region_uv.bottom_left,
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(0., size_v.0),
|
|
||||||
size: vec2(size_h.0, size_v.1),
|
|
||||||
color: interpolate_color_rect(left_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(left_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
// Center patch
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0, size_v.0),
|
|
||||||
size: vec2(size_h.1, size_v.1),
|
|
||||||
color: interpolate_color_rect(region_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(region_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Right patch
|
|
||||||
let right_patch_uv = Corners {
|
|
||||||
top_left: region_uv.top_right,
|
|
||||||
top_right: vec2(1., region_uv.top_right.y),
|
|
||||||
bottom_left: region_uv.bottom_right,
|
|
||||||
bottom_right: vec2(1., region_uv.bottom_right.y),
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0 + size_h.1, size_v.0),
|
|
||||||
size: vec2(size_h.2, size_v.1),
|
|
||||||
color: interpolate_color_rect(right_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(right_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Bottom-left patch
|
|
||||||
let bottom_left_patch_uv = Corners {
|
|
||||||
top_left: vec2(0., region_uv.bottom_left.y),
|
|
||||||
top_right: region_uv.bottom_left,
|
|
||||||
bottom_left: vec2(0., 1.),
|
|
||||||
bottom_right: vec2(region_uv.bottom_left.x, 1.),
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(0., size_v.0 + size_v.1),
|
|
||||||
size: vec2(size_h.0, size_v.2),
|
|
||||||
color: interpolate_color_rect(bottom_left_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(bottom_left_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Bottom patch
|
|
||||||
let bottom_patch_uv = Corners {
|
|
||||||
top_left: region_uv.bottom_left,
|
|
||||||
top_right: region_uv.bottom_right,
|
|
||||||
bottom_left: vec2(region_uv.bottom_left.x, 1.),
|
|
||||||
bottom_right: vec2(region_uv.bottom_right.x, 1.),
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0, size_v.0 + size_v.1),
|
|
||||||
size: vec2(size_h.1, size_v.2),
|
|
||||||
color: interpolate_color_rect(bottom_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(bottom_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
|
|
||||||
//Bottom-right patch
|
|
||||||
let bottom_right_patch_uv = Corners {
|
|
||||||
top_left: region_uv.bottom_right,
|
|
||||||
top_right: vec2(1., region_uv.bottom_right.y),
|
|
||||||
bottom_left: vec2(region_uv.bottom_right.x, 1.),
|
|
||||||
bottom_right: vec2(1., 1.),
|
|
||||||
};
|
|
||||||
draw.add(UiDrawCommand::Rectangle {
|
|
||||||
position: position + vec2(size_h.0 + size_h.1, size_v.0 + size_v.1),
|
|
||||||
size: vec2(size_h.2, size_v.2),
|
|
||||||
color: interpolate_color_rect(bottom_right_patch_uv),
|
|
||||||
texture: Some(self.asset.image),
|
|
||||||
texture_uv: Some(bottom_right_patch_uv),
|
|
||||||
rounded_corners: None
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn covers_opaque(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
//! frame-relative positioning/size
|
|
||||||
|
|
||||||
use glam::{Vec2, vec2};
|
use glam::{Vec2, vec2};
|
||||||
use derive_more::{Add, AddAssign, Sub, SubAssign};
|
use derive_more::{Add, AddAssign, Sub, SubAssign};
|
||||||
use crate::layout::{Size, Size2d};
|
use crate::layout::{Size, Size2d};
|
||||||
|
@ -26,13 +24,11 @@ impl From<f32> for FramePoint {
|
||||||
impl From<Size> for FramePoint {
|
impl From<Size> for FramePoint {
|
||||||
/// Convert a `Size` into a `FramePoint`
|
/// Convert a `Size` into a `FramePoint`
|
||||||
///
|
///
|
||||||
/// This function behaves just as you would expect, but:
|
/// This function behaves just as you would expect, but `Auto` is always treated as `BEGIN`
|
||||||
/// - `Auto` is always treated as `BEGIN`
|
|
||||||
/// - `Remaining` is treated as `Relative`
|
|
||||||
fn from(size: Size) -> Self {
|
fn from(size: Size) -> Self {
|
||||||
match size {
|
match size {
|
||||||
Size::Auto => Self::BEGIN,
|
Size::Auto => Self::BEGIN,
|
||||||
Size::Relative(value) | Size::Remaining(value) => Self::relative(value),
|
Size::Relative(value) => Self::relative(value),
|
||||||
Size::Absolute(value) => Self::absolute(value),
|
Size::Absolute(value) => Self::absolute(value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,6 @@ impl Frame for FrameRect {
|
||||||
size: bottom_right - top_left,
|
size: bottom_right - top_left,
|
||||||
color: self.color.corners(),
|
color: self.color.corners(),
|
||||||
texture: self.image,
|
texture: self.image,
|
||||||
texture_uv: 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)
|
||||||
),
|
),
|
||||||
|
@ -141,7 +140,6 @@ impl Frame for FrameRect {
|
||||||
self.bottom_right.y.absolute >= 0. &&
|
self.bottom_right.y.absolute >= 0. &&
|
||||||
self.bottom_right.y.relative >= 1. &&
|
self.bottom_right.y.relative >= 1. &&
|
||||||
self.color.is_opaque() &&
|
self.color.is_opaque() &&
|
||||||
self.image.is_none() &&
|
self.image.is_none()
|
||||||
self.corner_radius.max_f32() == 0.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
//! allows stacking two frames on top of each other
|
|
||||||
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use crate::draw::UiDrawCommandList;
|
use crate::draw::UiDrawCommandList;
|
||||||
use super::Frame;
|
use super::Frame;
|
||||||
|
|
||||||
/// A frame that draws two frames on top of each other
|
|
||||||
pub struct FrameStack(pub Box<dyn Frame>, pub Box<dyn Frame>);
|
pub struct FrameStack(pub Box<dyn Frame>, pub Box<dyn Frame>);
|
||||||
|
|
||||||
impl Frame for FrameStack {
|
impl Frame for FrameStack {
|
||||||
|
@ -20,7 +17,6 @@ impl Frame for FrameStack {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FrameStackExt: Frame {
|
pub trait FrameStackExt: Frame {
|
||||||
/// Stack another frame on top of this one
|
|
||||||
fn stack(self, other: impl Frame + 'static) -> FrameStack;
|
fn stack(self, other: impl Frame + 'static) -> FrameStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,8 +101,6 @@ pub enum KeyboardKey {
|
||||||
macro_rules! impl_fits64_for_keyboard_key {
|
macro_rules! impl_fits64_for_keyboard_key {
|
||||||
($($i:ident = $v:literal),*) => {
|
($($i:ident = $v:literal),*) => {
|
||||||
impl Fits64 for KeyboardKey {
|
impl Fits64 for KeyboardKey {
|
||||||
// SAFETY: not actually doing anything unsafe
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe fn from_u64(x: u64) -> Self {
|
unsafe fn from_u64(x: u64) -> Self {
|
||||||
match x {
|
match x {
|
||||||
$( $v => KeyboardKey::$i, )*
|
$( $v => KeyboardKey::$i, )*
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl UiInstance {
|
||||||
/// (this will change to a soft error in the future)
|
/// (this will change to a soft error in the future)
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<ImageHandle, std::io::Error> {
|
pub fn add_image_file_path(&mut self, path: impl AsRef<std::path::Path>) -> Result<ImageHandle, std::io::Error> {
|
||||||
use std::io::{Read, Seek};
|
use std::io::Read;
|
||||||
|
|
||||||
// Open the file (and wrap it in a bufreader)
|
// Open the file (and wrap it in a bufreader)
|
||||||
let mut file = std::io::BufReader::new(std::fs::File::open(path)?);
|
let mut file = std::io::BufReader::new(std::fs::File::open(path)?);
|
||||||
|
@ -106,7 +106,6 @@ impl UiInstance {
|
||||||
let mut magic = [0; 64];
|
let mut magic = [0; 64];
|
||||||
file.read_exact(&mut magic)?;
|
file.read_exact(&mut magic)?;
|
||||||
let format = image::guess_format(&magic).expect("Invalid image data (FORMAT)");
|
let format = image::guess_format(&magic).expect("Invalid image data (FORMAT)");
|
||||||
file.seek(std::io::SeekFrom::Start(0))?;
|
|
||||||
|
|
||||||
//Parse the image and read the raw uncompressed rgba data
|
//Parse the image and read the raw uncompressed rgba data
|
||||||
let image = image::load(file, format).expect("Invalid image data");
|
let image = image::load(file, format).expect("Invalid image data");
|
||||||
|
@ -157,7 +156,6 @@ impl UiInstance {
|
||||||
position: Vec2::ZERO,
|
position: Vec2::ZERO,
|
||||||
max_size,
|
max_size,
|
||||||
direction: Direction::Vertical,
|
direction: Direction::Vertical,
|
||||||
remaining_space: None,
|
|
||||||
};
|
};
|
||||||
let measure = element.measure(MeasureContext {
|
let measure = element.measure(MeasureContext {
|
||||||
state: &self.stateful_state,
|
state: &self.stateful_state,
|
||||||
|
|
|
@ -2,46 +2,6 @@
|
||||||
|
|
||||||
use glam::{vec2, Vec2};
|
use glam::{vec2, Vec2};
|
||||||
|
|
||||||
/// Controls wrapping behavior of elements
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord, Default)]
|
|
||||||
pub enum WrapBehavior {
|
|
||||||
/// No wrapping is allowed, even if explicit line breaks is requested by the element
|
|
||||||
Disable = 0,
|
|
||||||
|
|
||||||
/// Allow wrapping if the element explicitly requests it (default behavior)
|
|
||||||
#[default]
|
|
||||||
Allow = 1,
|
|
||||||
|
|
||||||
/// Elements will be wrapped automatically when they reach the maximum width/height of the container
|
|
||||||
Enable = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for WrapBehavior {
|
|
||||||
#[inline]
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
match value {
|
|
||||||
true => Self::Enable,
|
|
||||||
false => Self::Disable,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WrapBehavior {
|
|
||||||
/// Check if wrapping is allowed for the element
|
|
||||||
#[inline]
|
|
||||||
pub fn is_allowed(&self) -> bool {
|
|
||||||
*self != Self::Disable
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if wrapping is enabled for the element
|
|
||||||
///
|
|
||||||
/// (Wrapping will be done automatically when the element reaches the maximum width/height)
|
|
||||||
#[inline]
|
|
||||||
pub fn is_enabled(&self) -> bool {
|
|
||||||
*self == Self::Enable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alignment along a single axis
|
/// Alignment along a single axis
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, PartialOrd, Ord)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, PartialOrd, Ord)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
|
@ -126,24 +86,17 @@ pub enum Size {
|
||||||
#[default]
|
#[default]
|
||||||
Auto,
|
Auto,
|
||||||
|
|
||||||
/// Static size in pixels
|
/// Size as a ratio of parent size\
|
||||||
Absolute(f32),
|
/// Valid range: 0.0-1.0 (0-100%)
|
||||||
|
|
||||||
/// Size as a ratio of parent element size
|
|
||||||
///
|
///
|
||||||
/// Expected range: `0.0..=1.0`
|
/// Out of range values are allowed, but are not guaranteed to work as expected\
|
||||||
|
/// (especially with negative values)
|
||||||
Relative(f32),
|
Relative(f32),
|
||||||
|
|
||||||
/// Size as a ratio of remaining space after all other elements have been laid out
|
//TODO FractionRemaining(f32),
|
||||||
///
|
|
||||||
/// Expected range: `0.0..=1.0`
|
/// Static size in pixels
|
||||||
///
|
Absolute(f32),
|
||||||
/// - This feature is experimental and may not work as expected;\
|
|
||||||
/// Current `Container` implementation:
|
|
||||||
/// - Assumes that he line is fully filled if any element uses `Remaining` size, even if sum of remaining sizes is less than 1.0
|
|
||||||
/// - Does not support `Remaining` size in the secondary axis, it will be treated as `Relative`
|
|
||||||
/// - In cases where it's not applicable or not supported, it's defined to behave as `Relative`
|
|
||||||
Remaining(f32),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<f32> for Size {
|
impl From<f32> for Size {
|
||||||
|
@ -206,13 +159,6 @@ pub struct LayoutInfo {
|
||||||
/// Current direction of the layout\
|
/// Current direction of the layout\
|
||||||
/// (Usually matches direction of the parent container)
|
/// (Usually matches direction of the parent container)
|
||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
|
|
||||||
/// Remaining space in the primary axis\
|
|
||||||
///
|
|
||||||
/// This value is only available during the layout step and is only likely to be present if the element uses `Size::Remaining`
|
|
||||||
///
|
|
||||||
/// (Make sure that LayoutInfo::direction is set to the correct direction!)
|
|
||||||
pub remaining_space: Option<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to calculate the size of an element based on its layout and size information\
|
/// Helper function to calculate the size of an element based on its layout and size information\
|
||||||
|
@ -222,19 +168,11 @@ pub fn compute_size(layout: &LayoutInfo, size: Size2d, comfy_size: Vec2) -> Vec2
|
||||||
Size::Auto => comfy_size.x,
|
Size::Auto => comfy_size.x,
|
||||||
Size::Relative(fraction) => layout.max_size.x * fraction,
|
Size::Relative(fraction) => layout.max_size.x * fraction,
|
||||||
Size::Absolute(size) => size,
|
Size::Absolute(size) => size,
|
||||||
Size::Remaining(fraction) => match layout.direction {
|
|
||||||
Direction::Horizontal => layout.remaining_space.unwrap_or(layout.max_size.x) * fraction,
|
|
||||||
Direction::Vertical => layout.max_size.x * fraction,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let height = match size.height {
|
let height = match size.height {
|
||||||
Size::Auto => comfy_size.y,
|
Size::Auto => comfy_size.y,
|
||||||
Size::Relative(fraction) => layout.max_size.y * fraction,
|
Size::Relative(fraction) => layout.max_size.y * fraction,
|
||||||
Size::Absolute(size) => size,
|
Size::Absolute(size) => size,
|
||||||
Size::Remaining(fraction) => match layout.direction {
|
|
||||||
Direction::Horizontal => layout.max_size.y * fraction,
|
|
||||||
Direction::Vertical => layout.remaining_space.unwrap_or(layout.max_size.y) * fraction,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
vec2(width, height)
|
vec2(width, height)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,9 @@
|
||||||
//! # Features
|
//! # Features
|
||||||
#![doc = document_features::document_features!()]
|
#![doc = document_features::document_features!()]
|
||||||
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
||||||
|
|
||||||
#![deny(unsafe_code)]
|
|
||||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![allow(unused_parens)]
|
#![allow(unused_parens)]
|
||||||
|
//#![forbid(unsafe_code)]
|
||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
mod instance;
|
mod instance;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
/// - `x` - `Size::Absolute(x)`
|
/// - `x` - `Size::Absolute(x)`
|
||||||
/// - `x%` - `Size::Relative(x / 100.)` *(literal only)*
|
/// - `x%` - `Size::Relative(x / 100.)` *(literal only)*
|
||||||
/// - `x/` - `Size::Relative(x)`
|
/// - `x/` - `Size::Relative(x)`
|
||||||
/// - `x%=` - `Size::Remaining(x / 100.)` *(literal only)*
|
|
||||||
/// - `x/=` - `Size::Remaining(x)`
|
|
||||||
///
|
///
|
||||||
/// ...where `x` is a literal, identifier or an expression wrapped in parentheses
|
/// ...where `x` is a literal, identifier or an expression wrapped in parentheses
|
||||||
///
|
///
|
||||||
|
@ -34,12 +32,6 @@ macro_rules! size {
|
||||||
($x:literal /) => {
|
($x:literal /) => {
|
||||||
$crate::layout::Size::Relative($x as f32)
|
$crate::layout::Size::Relative($x as f32)
|
||||||
};
|
};
|
||||||
($x:literal %=) => {
|
|
||||||
$crate::layout::Size::Remaining($x as f32 / 100.)
|
|
||||||
};
|
|
||||||
($x:literal /=) => {
|
|
||||||
$crate::layout::Size::Remaining($x as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
($x:ident) => {
|
($x:ident) => {
|
||||||
$crate::layout::Size::Absolute($x as f32)
|
$crate::layout::Size::Absolute($x as f32)
|
||||||
|
@ -47,9 +39,6 @@ macro_rules! size {
|
||||||
($x:ident /) => {
|
($x:ident /) => {
|
||||||
$crate::layout::Size::Relative($x as f32)
|
$crate::layout::Size::Relative($x as f32)
|
||||||
};
|
};
|
||||||
($x:ident /=) => {
|
|
||||||
$crate::layout::Size::Remaining($x as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
(($x:expr)) => {
|
(($x:expr)) => {
|
||||||
$crate::layout::Size::Absolute(($x) as f32)
|
$crate::layout::Size::Absolute(($x) as f32)
|
||||||
|
@ -57,9 +46,6 @@ macro_rules! size {
|
||||||
(($x:expr) /) => {
|
(($x:expr) /) => {
|
||||||
$crate::layout::Size::Relative(($x) as f32)
|
$crate::layout::Size::Relative(($x) as f32)
|
||||||
};
|
};
|
||||||
(($x:expr) /=) => {
|
|
||||||
$crate::layout::Size::Remaining(($x) as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
($x:tt , $y:tt $($ys:tt)?) => {
|
($x:tt , $y:tt $($ys:tt)?) => {
|
||||||
$crate::layout::Size2d {
|
$crate::layout::Size2d {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::Corners;
|
use super::Corners;
|
||||||
use glam::{Vec2, Vec3, Vec4, vec4};
|
use glam::{Vec3, Vec4, vec4};
|
||||||
|
|
||||||
/// Represents the fill color of a rectangle
|
/// Represents the fill color of a rectangle
|
||||||
///
|
///
|
||||||
|
@ -69,14 +69,6 @@ impl FillColor {
|
||||||
pub const fn corners(&self) -> Corners<Vec4> {
|
pub const fn corners(&self) -> Corners<Vec4> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpolate color on position, assuming a linear gradient
|
|
||||||
pub fn interpolate(&self, uv: Vec2) -> Vec4 {
|
|
||||||
let c = self.corners();
|
|
||||||
let top = c.top_left.lerp(c.top_right, uv.x);
|
|
||||||
let bottom = c.bottom_left.lerp(c.bottom_right, uv.x);
|
|
||||||
top.lerp(bottom, uv.y)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FillColor {
|
impl Default for FillColor {
|
||||||
|
|
|
@ -6,9 +6,6 @@ use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
pub mod trigger;
|
pub mod trigger;
|
||||||
|
|
||||||
#[cfg(feature = "derive")]
|
|
||||||
pub use hui_derive::Signal;
|
|
||||||
|
|
||||||
/// A marker trait for UI Signals
|
/// A marker trait for UI Signals
|
||||||
pub trait Signal: Any {}
|
pub trait Signal: Any {}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue