mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-25 08:28:42 -06:00
idk a bunch of changes i forgor to commit
This commit is contained in:
parent
0377f1e46d
commit
c91e6ba50c
|
@ -1,6 +1,6 @@
|
||||||
<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">
|
<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>
|
||||||
Simple UI library for games and other interactive applications
|
Simple UI library for games and other interactive applications
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
</a><br><a href="./LICENSE.txt" align="right" float="right">
|
</a><br><a href="./LICENSE.txt" align="right" float="right">
|
||||||
<img alt="license" src="https://img.shields.io/github/license/griffi-gh/hui?style=flat-square" align="right" width="102" height="20">
|
<img alt="license" src="https://img.shields.io/github/license/griffi-gh/hui?style=flat-square" align="right" width="102" height="20">
|
||||||
</a><span>
|
</a><span>
|
||||||
Formerly kubi-ui
|
(Formerly <code>kubi-ui</code>)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p></p>
|
<p></p>
|
||||||
|
|
BIN
hui-examples/assets/blink/Blink-ynYZ.otf
Normal file
BIN
hui-examples/assets/blink/Blink-ynYZ.otf
Normal file
Binary file not shown.
94
hui-examples/assets/blink/SIL Open Font License.txt
Normal file
94
hui-examples/assets/blink/SIL Open Font License.txt
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
Copyright (c) 2015, Mew Too/Cannot Into Space Fonts (cannotintospacefonts@gmail.com),
|
||||||
|
with Reserved Font Name Blink.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
2
hui-examples/assets/blink/info.txt
Normal file
2
hui-examples/assets/blink/info.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
license: SIL Open Font License (OFL)
|
||||||
|
link: https://www.fontspace.com/blink-font-f21809
|
|
@ -9,46 +9,61 @@ use hui_glium::GliumUiRenderer;
|
||||||
|
|
||||||
/// Generates a `main` function that initializes glium renderer, `UiInstance`, and runs the event loop.
|
/// Generates a `main` function that initializes glium renderer, `UiInstance`, and runs the event loop.
|
||||||
macro_rules! ui_main {
|
macro_rules! ui_main {
|
||||||
|
(init: $closure0: expr, run: $closure1: expr) => {
|
||||||
|
fn main() {
|
||||||
|
$crate::boilerplate::ui($closure0, $closure1);
|
||||||
|
}
|
||||||
|
};
|
||||||
($closure: expr) => {
|
($closure: expr) => {
|
||||||
fn main() {
|
fn main() {
|
||||||
$crate::boilerplate::ui($closure);
|
$crate::boilerplate::ui(|_|(), $closure);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes glium renderer, `UiInstance`, and runs the event loop.
|
/// Initializes glium renderer, `UiInstance`, and runs the event loop.
|
||||||
pub fn ui(mut x: impl FnMut(&mut UiInstance, Vec2)) {
|
pub fn ui<T>(mut init: impl FnMut(&mut UiInstance) -> T, mut draw: impl FnMut(&mut UiInstance, Vec2, &T)) {
|
||||||
kubi_logging::init();
|
kubi_logging::init();
|
||||||
|
|
||||||
let event_loop = EventLoopBuilder::new().build().unwrap();
|
let event_loop = EventLoopBuilder::new().build().unwrap();
|
||||||
let (_window, display) = SimpleWindowBuilder::new().build(&event_loop);
|
let (window, display) = SimpleWindowBuilder::new().build(&event_loop);
|
||||||
|
|
||||||
let mut hui = UiInstance::new();
|
let mut hui = UiInstance::new();
|
||||||
let mut backend = GliumUiRenderer::new(&display);
|
let mut backend = GliumUiRenderer::new(&display);
|
||||||
|
|
||||||
|
let result = init(&mut hui);
|
||||||
|
|
||||||
event_loop.run(|event, window_target| {
|
event_loop.run(|event, window_target| {
|
||||||
|
window.request_redraw();
|
||||||
window_target.set_control_flow(ControlFlow::Poll);
|
window_target.set_control_flow(ControlFlow::Poll);
|
||||||
hui_winit::handle_winit_event(&mut hui, &event);
|
hui_winit::handle_winit_event(&mut hui, &event);
|
||||||
match event {
|
match event {
|
||||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
window_target.exit();
|
||||||
|
},
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
let mut frame = display.draw();
|
||||||
|
frame.clear_color_srgb(0.5, 0.5, 0.5, 0.);
|
||||||
|
|
||||||
|
hui.begin();
|
||||||
|
|
||||||
|
let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||||
|
draw(&mut hui, size, &result);
|
||||||
|
|
||||||
|
hui.end();
|
||||||
|
|
||||||
|
backend.update(&hui);
|
||||||
|
backend.draw(&mut frame, size);
|
||||||
|
|
||||||
|
frame.finish().unwrap();
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::Suspended => {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
window_target.exit();
|
window_target.exit();
|
||||||
},
|
},
|
||||||
Event::AboutToWait => {
|
|
||||||
let mut frame = display.draw();
|
|
||||||
frame.clear_color_srgb(0.5, 0.5, 0.5, 0.);
|
|
||||||
|
|
||||||
hui.begin();
|
|
||||||
|
|
||||||
let size = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
|
||||||
x(&mut hui, size);
|
|
||||||
|
|
||||||
hui.end();
|
|
||||||
|
|
||||||
backend.update(&hui);
|
|
||||||
backend.draw(&mut frame, size);
|
|
||||||
|
|
||||||
frame.finish().unwrap();
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
|
@ -69,7 +69,7 @@ fn main() {
|
||||||
if instant.elapsed().as_secs_f32() < 5. {
|
if instant.elapsed().as_secs_f32() < 5. {
|
||||||
el.push(Box::new(Text {
|
el.push(Box::new(Text {
|
||||||
text: "Downloading your mom...".into(),
|
text: "Downloading your mom...".into(),
|
||||||
font: font_handle,
|
font: Some(font_handle),
|
||||||
text_size: 24,
|
text_size: 24,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
|
@ -84,7 +84,7 @@ fn main() {
|
||||||
size: (Size::Fraction(1.), Size::Auto).into(),
|
size: (Size::Fraction(1.), Size::Auto).into(),
|
||||||
children: ElementList(vec![Box::new(Text {
|
children: ElementList(vec![Box::new(Text {
|
||||||
text: format!("{:.2}% ({:.1} GB)", mom_ratio * 100., mom_ratio * 10000.).into(),
|
text: format!("{:.2}% ({:.1} GB)", mom_ratio * 100., mom_ratio * 10000.).into(),
|
||||||
font: font_handle,
|
font: Some(font_handle),
|
||||||
text_size: 16,
|
text_size: 16,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})]),
|
})]),
|
||||||
|
@ -93,14 +93,14 @@ fn main() {
|
||||||
} else if instant.elapsed().as_secs() < 10 {
|
} else if instant.elapsed().as_secs() < 10 {
|
||||||
el.push(Box::new(Text {
|
el.push(Box::new(Text {
|
||||||
text: "Error 413: Request Entity Too Large".into(),
|
text: "Error 413: Request Entity Too Large".into(),
|
||||||
font: font_handle,
|
font: Some(font_handle),
|
||||||
color: vec4(1., 0.125, 0.125, 1.),
|
color: vec4(1., 0.125, 0.125, 1.),
|
||||||
text_size: 20,
|
text_size: 20,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
el.push(Box::new(Text {
|
el.push(Box::new(Text {
|
||||||
text: format!("Exiting in {}...", 10 - instant.elapsed().as_secs()).into(),
|
text: format!("Exiting in {}...", 10 - instant.elapsed().as_secs()).into(),
|
||||||
font: font_handle,
|
font: Some(font_handle),
|
||||||
text_size: 16,
|
text_size: 16,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -83,7 +83,7 @@ fn main() {
|
||||||
}));
|
}));
|
||||||
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(),
|
||||||
font: font_handle,
|
font: Some(font_handle),
|
||||||
text_size: 32,
|
text_size: 32,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -8,7 +8,7 @@ use hui::{
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod boilerplate;
|
mod boilerplate;
|
||||||
|
|
||||||
ui_main!(|ui, size| {
|
ui_main!(|ui, size, _| {
|
||||||
Container::default()
|
Container::default()
|
||||||
.with_size(size!(100%, 50%))
|
.with_size(size!(100%, 50%))
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Center)
|
||||||
|
|
119
hui-examples/examples/ui_test2.rs
Normal file
119
hui-examples/examples/ui_test2.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use glam::vec4;
|
||||||
|
use hui::{
|
||||||
|
color, size,
|
||||||
|
element::{container::Container, progress_bar::ProgressBar, text::Text, UiElementExt},
|
||||||
|
layout::Alignment,
|
||||||
|
rectangle::Corners,
|
||||||
|
text::FontHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "../boilerplate.rs"]
|
||||||
|
#[macro_use]
|
||||||
|
mod boilerplate;
|
||||||
|
|
||||||
|
ui_main!(
|
||||||
|
init: |ui| {
|
||||||
|
let font = ui.add_font(include_bytes!("../assets/blink/Blink-ynYZ.otf"));
|
||||||
|
ui.push_font(font);
|
||||||
|
(std::time::Instant::now(),)
|
||||||
|
},
|
||||||
|
run: |ui, size, (instant,)| {
|
||||||
|
// Background color (gradient)
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_background(Corners {
|
||||||
|
top_left: vec4(0.2, 0.2, 0.3, 1.),
|
||||||
|
top_right: vec4(0.3, 0.3, 0.4, 1.),
|
||||||
|
bottom_left: vec4(0.2, 0.3, 0.2, 1.),
|
||||||
|
bottom_right: vec4(0.5, 0.4, 0.4, 1.),
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
|
||||||
|
// Loading text in the bottom right corner
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align(Alignment::End)
|
||||||
|
.with_padding(20.)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Container::default()
|
||||||
|
.with_padding((10., 15.))
|
||||||
|
.with_corner_radius(8.)
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_children(|ui| {
|
||||||
|
let flash = 1. - 0.5 * (4. * instant.elapsed().as_secs_f32()).sin().powi(2);
|
||||||
|
Text::default()
|
||||||
|
.with_text("Loading...")
|
||||||
|
.with_color((1., 1., 1., flash))
|
||||||
|
.with_text_size(24)
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
|
||||||
|
// Did you know? box in the center
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Container::default()
|
||||||
|
.with_align((Alignment::Center, Alignment::Begin))
|
||||||
|
.with_padding(15.)
|
||||||
|
.with_gap(10.)
|
||||||
|
.with_corner_radius(8.)
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_children(|ui| {
|
||||||
|
Text::default()
|
||||||
|
.with_text("Did you know?")
|
||||||
|
.with_text_size(18)
|
||||||
|
.add_child(ui);
|
||||||
|
Text::default()
|
||||||
|
.with_text("You can die by jumping into the spike pit! :D\nCheck out the tutorial section for more tips.")
|
||||||
|
.with_text_size(24)
|
||||||
|
.with_font(FontHandle::default())
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
|
||||||
|
// Progress bar at the bottom
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align((Alignment::Center, Alignment::End))
|
||||||
|
.with_children(|ui| {
|
||||||
|
ProgressBar::default()
|
||||||
|
.with_value((instant.elapsed().as_secs_f32() * 0.1) % 1.)
|
||||||
|
.with_size(size!(100%, 5))
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_foreground(color::DARK_GREEN)
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
|
||||||
|
// Player XP and level (mock) in the top right corner
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%))
|
||||||
|
.with_align((Alignment::End, Alignment::Begin))
|
||||||
|
.with_padding(20.)
|
||||||
|
.with_children(|ui| {
|
||||||
|
Container::default()
|
||||||
|
.with_padding(10.)
|
||||||
|
.with_corner_radius(8.)
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_children(|ui| {
|
||||||
|
Text::default()
|
||||||
|
.with_text("Level 5")
|
||||||
|
.with_text_size(24)
|
||||||
|
.add_child(ui);
|
||||||
|
Text::default()
|
||||||
|
.with_text("XP: 1234 / 5000")
|
||||||
|
.with_text_size(18)
|
||||||
|
.with_font(FontHandle::default())
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
})
|
||||||
|
.add_root(ui, size);
|
||||||
|
}
|
||||||
|
);
|
|
@ -59,8 +59,8 @@ impl BufferPair {
|
||||||
Self {
|
Self {
|
||||||
vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(),
|
vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(),
|
||||||
index_buffer: IndexBuffer::dynamic(facade, PrimitiveType::TrianglesList, idx).unwrap(),
|
index_buffer: IndexBuffer::dynamic(facade, PrimitiveType::TrianglesList, idx).unwrap(),
|
||||||
vertex_count: 0,
|
vertex_count: vtx.len(),
|
||||||
index_count: 0,
|
index_count: idx.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ pub struct GliumUiRenderer {
|
||||||
|
|
||||||
impl GliumUiRenderer {
|
impl GliumUiRenderer {
|
||||||
pub fn new<F: Facade>(facade: &F) -> Self {
|
pub fn new<F: Facade>(facade: &F) -> Self {
|
||||||
log::info!("initializing hui glium backend");
|
log::info!("initializing hui-glium");
|
||||||
Self {
|
Self {
|
||||||
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
||||||
context: Rc::clone(facade.get_context()),
|
context: Rc::clone(facade.get_context()),
|
||||||
|
@ -131,7 +131,8 @@ impl GliumUiRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_buffers(&mut self, call: &UiDrawCall) {
|
fn update_buffers(&mut self, call: &UiDrawCall) {
|
||||||
|
log::trace!("updating ui buffers (i={})", call.indices.len());
|
||||||
let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..];
|
let data_vtx = &call.vertices.iter().copied().map(Vertex::from).collect::<Vec<_>>()[..];
|
||||||
let data_idx = &call.indices[..];
|
let data_idx = &call.indices[..];
|
||||||
if let Some(buffer) = &mut self.buffer_pair {
|
if let Some(buffer) = &mut self.buffer_pair {
|
||||||
|
@ -141,7 +142,7 @@ impl GliumUiRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_texture_atlas(&mut self, atlas: &TextureAtlasMeta) {
|
fn update_texture_atlas(&mut self, atlas: &TextureAtlasMeta) {
|
||||||
log::trace!("updating ui atlas texture");
|
log::trace!("updating ui atlas texture");
|
||||||
self.ui_texture = Some(SrgbTexture2d::new(
|
self.ui_texture = Some(SrgbTexture2d::new(
|
||||||
&self.context,
|
&self.context,
|
||||||
|
@ -152,16 +153,13 @@ impl GliumUiRenderer {
|
||||||
).unwrap());
|
).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, hui: &UiInstance) {
|
pub fn update(&mut self, instance: &UiInstance) {
|
||||||
if self.ui_texture.is_none() || hui.atlas().modified {
|
if self.ui_texture.is_none() || instance.atlas().modified {
|
||||||
self.update_texture_atlas(&hui.atlas());
|
self.update_texture_atlas(&instance.atlas());
|
||||||
|
}
|
||||||
|
if self.buffer_pair.is_none() || instance.draw_call().0 {
|
||||||
|
self.update_buffers(instance.draw_call().1);
|
||||||
}
|
}
|
||||||
//HACK: modified is incorrect, this is a hack
|
|
||||||
self.update_buffers(hui.draw_call().1);
|
|
||||||
//FIXME before release
|
|
||||||
// if (self.buffer_pair.is_none() && !hui.draw_call().1.indices.is_empty()) || hui.draw_call().0 {
|
|
||||||
// self.update_buffers(hui.draw_call().1);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
|
pub fn draw(&self, frame: &mut glium::Frame, resolution: Vec2) {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
//! element API, built-in elements like `Container`, `Button`, `Text`, etc.
|
//! element API, built-in elements like `Container`, `Button`, `Text`, etc.
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::UiDrawCommandList, layout::LayoutInfo, measure::Response, state::StateRepo, text::TextMeasure, UiInstance
|
draw::UiDrawCommandList,
|
||||||
|
layout::LayoutInfo,
|
||||||
|
measure::Response,
|
||||||
|
state::StateRepo,
|
||||||
|
text::{FontHandle, TextMeasure},
|
||||||
|
UiInstance
|
||||||
};
|
};
|
||||||
|
|
||||||
mod builtin;
|
mod builtin;
|
||||||
|
@ -12,6 +17,7 @@ pub struct MeasureContext<'a> {
|
||||||
pub state: &'a StateRepo,
|
pub state: &'a StateRepo,
|
||||||
pub layout: &'a LayoutInfo,
|
pub layout: &'a LayoutInfo,
|
||||||
pub text_measure: TextMeasure<'a>,
|
pub text_measure: TextMeasure<'a>,
|
||||||
|
pub current_font: FontHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context for the `Element::process` function
|
/// Context for the `Element::process` function
|
||||||
|
@ -21,6 +27,7 @@ pub struct ProcessContext<'a> {
|
||||||
pub layout: &'a LayoutInfo,
|
pub layout: &'a LayoutInfo,
|
||||||
pub draw: &'a mut UiDrawCommandList,
|
pub draw: &'a mut UiDrawCommandList,
|
||||||
pub text_measure: TextMeasure<'a>,
|
pub text_measure: TextMeasure<'a>,
|
||||||
|
pub current_font: FontHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UiElement {
|
pub trait UiElement {
|
||||||
|
@ -58,13 +65,18 @@ pub trait UiElement {
|
||||||
fn process(&self, ctx: ProcessContext);
|
fn process(&self, ctx: ProcessContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A list of elements\
|
||||||
|
/// Use the [`add`](`ElementList::add`) method to add elements to the list
|
||||||
pub struct ElementList(pub Vec<Box<dyn UiElement>>);
|
pub struct ElementList(pub Vec<Box<dyn UiElement>>);
|
||||||
|
|
||||||
impl ElementList {
|
impl ElementList {
|
||||||
|
/// Add an element to the list
|
||||||
pub fn add(&mut self, element: impl UiElement + 'static) {
|
pub fn add(&mut self, element: impl UiElement + 'static) {
|
||||||
self.0.push(Box::new(element))
|
self.0.push(Box::new(element))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `ElementList` from a callback\
|
||||||
|
/// The callback will be called with a reference to the newly list
|
||||||
pub(crate) fn from_callback(cb: impl FnOnce(&mut ElementList)) -> Self {
|
pub(crate) fn from_callback(cb: impl FnOnce(&mut ElementList)) -> Self {
|
||||||
let mut list = ElementList(Vec::new());
|
let mut list = ElementList(Vec::new());
|
||||||
cb(&mut list);
|
cb(&mut list);
|
||||||
|
@ -72,29 +84,10 @@ impl ElementList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl<T: FnOnce(&mut ElementList)> From<T> for ElementList {
|
|
||||||
// fn from(cb: T) -> Self {
|
|
||||||
// let mut list = ElementList(Vec::new());
|
|
||||||
// cb(&mut list);
|
|
||||||
// list
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl<T: UiElement + 'static> From<T> for ElementList {
|
|
||||||
// fn from(value: T) -> Self {
|
|
||||||
// ElementList(vec![Box::new(value)])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl From<Vec<Box<dyn UiElement>>> for ElementList {
|
|
||||||
// fn from(value: Vec<Box<dyn UiElement>>) -> Self {
|
|
||||||
// Self(value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub trait UiElementExt: UiElement {
|
pub trait UiElementExt: UiElement {
|
||||||
/// Add element as a child/nested element.
|
/// Add element as a child/nested element.
|
||||||
fn add_child(self, ui: &mut ElementList);
|
fn add_child(self, ui: &mut ElementList);
|
||||||
|
|
||||||
/// Add element as a ui root.
|
/// Add element as a ui root.
|
||||||
fn add_root(self, ui: &mut UiInstance, max_size: glam::Vec2);
|
fn add_root(self, ui: &mut UiInstance, max_size: glam::Vec2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ impl UiElement for Container {
|
||||||
direction: self.direction,
|
direction: self.direction,
|
||||||
},
|
},
|
||||||
text_measure: ctx.text_measure,
|
text_measure: ctx.text_measure,
|
||||||
|
current_font: ctx.current_font,
|
||||||
});
|
});
|
||||||
match self.direction {
|
match self.direction {
|
||||||
UiDirection::Horizontal => {
|
UiDirection::Horizontal => {
|
||||||
|
@ -160,7 +161,7 @@ impl UiElement for Container {
|
||||||
//.0 = primary, .1 = secondary
|
//.0 = primary, .1 = secondary
|
||||||
let pri_sec_align = match self.direction {
|
let pri_sec_align = match self.direction {
|
||||||
UiDirection::Horizontal => (self.align.horizontal, self.align.vertical),
|
UiDirection::Horizontal => (self.align.horizontal, self.align.vertical),
|
||||||
UiDirection::Vertical => (self.align.horizontal, self.align.vertical),
|
UiDirection::Vertical => (self.align.vertical, self.align.horizontal),
|
||||||
};
|
};
|
||||||
|
|
||||||
//alignment
|
//alignment
|
||||||
|
@ -194,6 +195,7 @@ impl UiElement for Container {
|
||||||
state: ctx.state,
|
state: ctx.state,
|
||||||
layout: &el_layout,
|
layout: &el_layout,
|
||||||
text_measure: ctx.text_measure,
|
text_measure: ctx.text_measure,
|
||||||
|
current_font: ctx.current_font,
|
||||||
});
|
});
|
||||||
|
|
||||||
//align (on sec. axis)
|
//align (on sec. axis)
|
||||||
|
@ -206,10 +208,10 @@ impl UiElement for Container {
|
||||||
el_layout.position.x += (ctx.measure.size.x - self.padding.left - self.padding.right - el_measure.size.x) / 2.;
|
el_layout.position.x += (ctx.measure.size.x - self.padding.left - self.padding.right - el_measure.size.x) / 2.;
|
||||||
},
|
},
|
||||||
(Alignment::End, UiDirection::Horizontal) => {
|
(Alignment::End, UiDirection::Horizontal) => {
|
||||||
el_layout.position.y += ctx.measure.size.y - el_measure.size.y - self.padding.bottom;
|
el_layout.position.y += ctx.measure.size.y - el_measure.size.y - self.padding.bottom - self.padding.top;
|
||||||
},
|
},
|
||||||
(Alignment::End, UiDirection::Vertical) => {
|
(Alignment::End, UiDirection::Vertical) => {
|
||||||
el_layout.position.x += ctx.measure.size.x - el_measure.size.x - self.padding.right;
|
el_layout.position.x += ctx.measure.size.x - el_measure.size.x - self.padding.right - self.padding.left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +222,7 @@ impl UiElement for Container {
|
||||||
layout: &el_layout,
|
layout: &el_layout,
|
||||||
draw: ctx.draw,
|
draw: ctx.draw,
|
||||||
text_measure: ctx.text_measure,
|
text_measure: ctx.text_measure,
|
||||||
|
current_font: ctx.current_font,
|
||||||
});
|
});
|
||||||
|
|
||||||
//layout
|
//layout
|
||||||
|
|
|
@ -4,7 +4,7 @@ use glam::{vec2, Vec4};
|
||||||
use crate::{
|
use crate::{
|
||||||
draw::UiDrawCommand,
|
draw::UiDrawCommand,
|
||||||
element::{MeasureContext, ProcessContext, UiElement},
|
element::{MeasureContext, ProcessContext, UiElement},
|
||||||
layout::Size,
|
layout::{Size, Size2d},
|
||||||
measure::Response,
|
measure::Response,
|
||||||
text::FontHandle,
|
text::FontHandle,
|
||||||
};
|
};
|
||||||
|
@ -22,9 +22,12 @@ use crate::{
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
#[setters(into)]
|
#[setters(into)]
|
||||||
pub text: Cow<'static, str>,
|
pub text: Cow<'static, str>,
|
||||||
pub size: (Size, Size),
|
#[setters(into)]
|
||||||
|
pub size: Size2d,
|
||||||
|
#[setters(into)]
|
||||||
pub color: Vec4,
|
pub color: Vec4,
|
||||||
pub font: FontHandle,
|
#[setters(into)]
|
||||||
|
pub font: Option<FontHandle>,
|
||||||
pub text_size: u16,
|
pub text_size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,30 +35,37 @@ impl Default for Text {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
text: "".into(),
|
text: "".into(),
|
||||||
size: (Size::Auto, Size::Auto),
|
size: (Size::Auto, Size::Auto).into(),
|
||||||
color: Vec4::new(1., 1., 1., 1.),
|
color: Vec4::new(1., 1., 1., 1.),
|
||||||
font: FontHandle::default(),
|
font: None,
|
||||||
text_size: 16,
|
text_size: 16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Text {
|
||||||
|
fn font(&self, f: FontHandle) -> FontHandle {
|
||||||
|
self.font.unwrap_or(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UiElement for Text {
|
impl UiElement for Text {
|
||||||
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.0, Size::Auto) || matches!(self.size.1, Size::Auto) {
|
if matches!(self.size.width, Size::Auto) || matches!(self.size.height, Size::Auto) {
|
||||||
let res = ctx.text_measure.measure(self.font, self.text_size, &self.text);
|
//TODO optimized measure if only one of the sizes is auto
|
||||||
|
let res = ctx.text_measure.measure(self.font(ctx.current_font), self.text_size, &self.text);
|
||||||
size.0 = res.max_width;
|
size.0 = res.max_width;
|
||||||
size.1 = res.height;
|
size.1 = res.height;
|
||||||
}
|
}
|
||||||
Response {
|
Response {
|
||||||
size: vec2(
|
size: vec2(
|
||||||
match self.size.0 {
|
match self.size.width {
|
||||||
Size::Auto => size.0,
|
Size::Auto => size.0,
|
||||||
Size::Fraction(percentage) => ctx.layout.max_size.x * percentage,
|
Size::Fraction(percentage) => ctx.layout.max_size.x * percentage,
|
||||||
Size::Static(pixels) => pixels,
|
Size::Static(pixels) => pixels,
|
||||||
},
|
},
|
||||||
match self.size.1 {
|
match self.size.height {
|
||||||
Size::Auto => size.1,
|
Size::Auto => size.1,
|
||||||
Size::Fraction(percentage) => ctx.layout.max_size.y * percentage,
|
Size::Fraction(percentage) => ctx.layout.max_size.y * percentage,
|
||||||
Size::Static(pixels) => pixels,
|
Size::Static(pixels) => pixels,
|
||||||
|
@ -67,12 +77,15 @@ impl UiElement for Text {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(&self, ctx: ProcessContext) {
|
fn process(&self, ctx: ProcessContext) {
|
||||||
|
if self.text.is_empty() || self.color.w == 0. {
|
||||||
|
return
|
||||||
|
}
|
||||||
ctx.draw.add(UiDrawCommand::Text {
|
ctx.draw.add(UiDrawCommand::Text {
|
||||||
text: self.text.clone(),
|
text: self.text.clone(),
|
||||||
position: ctx.layout.position,
|
position: ctx.layout.position,
|
||||||
size: self.text_size,
|
size: self.text_size,
|
||||||
color: self.color,
|
color: self.color,
|
||||||
font: self.font
|
font: self.font(ctx.current_font),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,28 @@ impl UiInstance {
|
||||||
self.text_renderer.add_font_from_bytes(font)
|
self.text_renderer.add_font_from_bytes(font)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push a font to the font stack\
|
||||||
|
/// The font will be used for all text rendering until it is popped
|
||||||
|
///
|
||||||
|
/// This function is useful for replacing the default font, use sparingly\
|
||||||
|
/// (This library attempts to be stateless, however passing the font to every text element is not very practical)
|
||||||
|
pub fn push_font(&mut self, font: FontHandle) {
|
||||||
|
self.text_renderer.push_font(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pop a font from the font stack\
|
||||||
|
///
|
||||||
|
/// ## Panics:
|
||||||
|
/// If the font stack is empty
|
||||||
|
pub fn pop_font(&mut self) {
|
||||||
|
self.text_renderer.pop_font();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current default font
|
||||||
|
pub fn current_font(&self) -> FontHandle {
|
||||||
|
self.text_renderer.current_font()
|
||||||
|
}
|
||||||
|
|
||||||
/// Add an element or an element tree to the UI
|
/// Add an element or an element tree to the UI
|
||||||
///
|
///
|
||||||
/// Use the `max_size` parameter to specify the maximum size of the element\
|
/// Use the `max_size` parameter to specify the maximum size of the element\
|
||||||
|
@ -77,6 +99,7 @@ impl UiInstance {
|
||||||
state: &self.stateful_state,
|
state: &self.stateful_state,
|
||||||
layout: &layout,
|
layout: &layout,
|
||||||
text_measure: self.text_renderer.to_measure(),
|
text_measure: self.text_renderer.to_measure(),
|
||||||
|
current_font: self.text_renderer.current_font(),
|
||||||
});
|
});
|
||||||
element.process(ProcessContext {
|
element.process(ProcessContext {
|
||||||
measure: &measure,
|
measure: &measure,
|
||||||
|
@ -84,6 +107,7 @@ impl UiInstance {
|
||||||
layout: &layout,
|
layout: &layout,
|
||||||
draw: &mut self.draw_commands,
|
draw: &mut self.draw_commands,
|
||||||
text_measure: self.text_renderer.to_measure(),
|
text_measure: self.text_renderer.to_measure(),
|
||||||
|
current_font: self.text_renderer.current_font(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,59 @@
|
||||||
//! text rendering, styling, measuring
|
//! text rendering, styling, measuring
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use fontdue::{Font, FontSettings};
|
||||||
|
use crate::draw::atlas::TextureAtlasManager;
|
||||||
|
|
||||||
mod font;
|
mod font;
|
||||||
mod ftm;
|
mod ftm;
|
||||||
|
mod stack;
|
||||||
|
|
||||||
use font::FontManager;
|
|
||||||
pub use font::FontHandle;
|
|
||||||
#[cfg(feature="builtin_font")]
|
#[cfg(feature="builtin_font")]
|
||||||
pub use font::BUILTIN_FONT;
|
pub use font::BUILTIN_FONT;
|
||||||
use fontdue::{Font, FontSettings};
|
pub use font::FontHandle;
|
||||||
|
|
||||||
|
use font::FontManager;
|
||||||
use ftm::FontTextureManager;
|
use ftm::FontTextureManager;
|
||||||
use ftm::GlyphCacheEntry;
|
use ftm::GlyphCacheEntry;
|
||||||
|
use stack::FontStack;
|
||||||
use crate::draw::atlas::TextureAtlasManager;
|
|
||||||
|
|
||||||
pub struct TextRenderer {
|
pub struct TextRenderer {
|
||||||
fm: FontManager,
|
manager: FontManager,
|
||||||
ftm: FontTextureManager,
|
ftm: FontTextureManager,
|
||||||
|
stack: FontStack,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextRenderer {
|
impl TextRenderer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fm: FontManager::new(),
|
manager: FontManager::new(),
|
||||||
ftm: FontTextureManager::default(),
|
ftm: FontTextureManager::default(),
|
||||||
|
stack: FontStack::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_font_from_bytes(&mut self, font: &[u8]) -> FontHandle {
|
pub fn add_font_from_bytes(&mut self, font: &[u8]) -> FontHandle {
|
||||||
self.fm.add_font(Font::from_bytes(font, FontSettings::default()).unwrap())
|
self.manager.add_font(Font::from_bytes(font, FontSettings::default()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
pub fn glyph(&mut self, atlas: &mut TextureAtlasManager, font_handle: FontHandle, character: char, size: u8) -> Arc<GlyphCacheEntry> {
|
||||||
self.ftm.glyph(atlas, &self.fm, font_handle, character, size)
|
self.ftm.glyph(atlas, &self.manager, font_handle, character, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_font(&mut self, font: FontHandle) {
|
||||||
|
self.stack.push(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_font(&mut self) {
|
||||||
|
self.stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_font(&self) -> FontHandle {
|
||||||
|
self.stack.current_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
pub(crate) fn internal_font(&self, handle: FontHandle) -> &Font {
|
||||||
self.fm.get(handle).unwrap()
|
self.manager.get(handle).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
hui/src/text/stack.rs
Normal file
32
hui/src/text/stack.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use super::FontHandle;
|
||||||
|
|
||||||
|
pub struct FontStack {
|
||||||
|
fonts: Vec<FontHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontStack {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(not(feature = "builtin_font"))]
|
||||||
|
fonts: Vec::new(),
|
||||||
|
#[cfg(feature = "builtin_font")]
|
||||||
|
fonts: vec![super::BUILTIN_FONT],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, font: FontHandle) {
|
||||||
|
self.fonts.push(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
assert!(self.fonts.pop().is_some())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current(&self) -> Option<FontHandle> {
|
||||||
|
self.fonts.last().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_or_default(&self) -> FontHandle {
|
||||||
|
self.current().unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue