mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-22 07:08:42 -06:00
idk a bunch of changes i forgor to commit
This commit is contained in:
parent
91367d54e0
commit
a09a25ba73
|
@ -1,6 +1,6 @@
|
|||
<p></p><p></p>
|
||||
<img src="https://raw.githubusercontent.com/griffi-gh/hui/master/.assets/hui.svg" width="120" align="left">
|
||||
<h1>hui</h1>
|
||||
<h1>hUI</h1>
|
||||
<div>
|
||||
<span>
|
||||
Simple UI library for games and other interactive applications
|
||||
|
@ -9,7 +9,7 @@
|
|||
</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">
|
||||
</a><span>
|
||||
Formerly kubi-ui
|
||||
(Formerly <code>kubi-ui</code>)
|
||||
</span>
|
||||
</div>
|
||||
<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.
|
||||
macro_rules! ui_main {
|
||||
(init: $closure0: expr, run: $closure1: expr) => {
|
||||
fn main() {
|
||||
$crate::boilerplate::ui($closure0, $closure1);
|
||||
}
|
||||
};
|
||||
($closure: expr) => {
|
||||
fn main() {
|
||||
$crate::boilerplate::ui($closure);
|
||||
$crate::boilerplate::ui(|_|(), $closure);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
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 backend = GliumUiRenderer::new(&display);
|
||||
|
||||
let result = init(&mut hui);
|
||||
|
||||
event_loop.run(|event, window_target| {
|
||||
window.request_redraw();
|
||||
window_target.set_control_flow(ControlFlow::Poll);
|
||||
hui_winit::handle_winit_event(&mut hui, &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();
|
||||
},
|
||||
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();
|
||||
|
|
|
@ -69,7 +69,7 @@ fn main() {
|
|||
if instant.elapsed().as_secs_f32() < 5. {
|
||||
el.push(Box::new(Text {
|
||||
text: "Downloading your mom...".into(),
|
||||
font: font_handle,
|
||||
font: Some(font_handle),
|
||||
text_size: 24,
|
||||
..Default::default()
|
||||
}));
|
||||
|
@ -84,7 +84,7 @@ fn main() {
|
|||
size: (Size::Fraction(1.), Size::Auto).into(),
|
||||
children: ElementList(vec![Box::new(Text {
|
||||
text: format!("{:.2}% ({:.1} GB)", mom_ratio * 100., mom_ratio * 10000.).into(),
|
||||
font: font_handle,
|
||||
font: Some(font_handle),
|
||||
text_size: 16,
|
||||
..Default::default()
|
||||
})]),
|
||||
|
@ -93,14 +93,14 @@ fn main() {
|
|||
} else if instant.elapsed().as_secs() < 10 {
|
||||
el.push(Box::new(Text {
|
||||
text: "Error 413: Request Entity Too Large".into(),
|
||||
font: font_handle,
|
||||
font: Some(font_handle),
|
||||
color: vec4(1., 0.125, 0.125, 1.),
|
||||
text_size: 20,
|
||||
..Default::default()
|
||||
}));
|
||||
el.push(Box::new(Text {
|
||||
text: format!("Exiting in {}...", 10 - instant.elapsed().as_secs()).into(),
|
||||
font: font_handle,
|
||||
font: Some(font_handle),
|
||||
text_size: 16,
|
||||
..Default::default()
|
||||
}));
|
||||
|
|
|
@ -83,7 +83,7 @@ fn main() {
|
|||
}));
|
||||
elem.push(Box::new(Text {
|
||||
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,
|
||||
..Default::default()
|
||||
}));
|
||||
|
|
|
@ -8,7 +8,7 @@ use hui::{
|
|||
#[macro_use]
|
||||
mod boilerplate;
|
||||
|
||||
ui_main!(|ui, size| {
|
||||
ui_main!(|ui, size, _| {
|
||||
Container::default()
|
||||
.with_size(size!(100%, 50%))
|
||||
.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 {
|
||||
vertex_buffer: VertexBuffer::dynamic(facade, vtx).unwrap(),
|
||||
index_buffer: IndexBuffer::dynamic(facade, PrimitiveType::TrianglesList, idx).unwrap(),
|
||||
vertex_count: 0,
|
||||
index_count: 0,
|
||||
vertex_count: vtx.len(),
|
||||
index_count: idx.len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ pub struct GliumUiRenderer {
|
|||
|
||||
impl GliumUiRenderer {
|
||||
pub fn new<F: Facade>(facade: &F) -> Self {
|
||||
log::info!("initializing hui glium backend");
|
||||
log::info!("initializing hui-glium");
|
||||
Self {
|
||||
program: Program::from_source(facade, VERTEX_SHADER, FRAGMENT_SHADER, None).unwrap(),
|
||||
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_idx = &call.indices[..];
|
||||
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");
|
||||
self.ui_texture = Some(SrgbTexture2d::new(
|
||||
&self.context,
|
||||
|
@ -152,16 +153,13 @@ impl GliumUiRenderer {
|
|||
).unwrap());
|
||||
}
|
||||
|
||||
pub fn update(&mut self, hui: &UiInstance) {
|
||||
if self.ui_texture.is_none() || hui.atlas().modified {
|
||||
self.update_texture_atlas(&hui.atlas());
|
||||
pub fn update(&mut self, instance: &UiInstance) {
|
||||
if self.ui_texture.is_none() || instance.atlas().modified {
|
||||
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) {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
//! element API, built-in elements like `Container`, `Button`, `Text`, etc.
|
||||
use std::any::Any;
|
||||
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;
|
||||
|
@ -12,6 +17,7 @@ pub struct MeasureContext<'a> {
|
|||
pub state: &'a StateRepo,
|
||||
pub layout: &'a LayoutInfo,
|
||||
pub text_measure: TextMeasure<'a>,
|
||||
pub current_font: FontHandle,
|
||||
}
|
||||
|
||||
/// Context for the `Element::process` function
|
||||
|
@ -21,6 +27,7 @@ pub struct ProcessContext<'a> {
|
|||
pub layout: &'a LayoutInfo,
|
||||
pub draw: &'a mut UiDrawCommandList,
|
||||
pub text_measure: TextMeasure<'a>,
|
||||
pub current_font: FontHandle,
|
||||
}
|
||||
|
||||
pub trait UiElement {
|
||||
|
@ -58,13 +65,18 @@ pub trait UiElement {
|
|||
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>>);
|
||||
|
||||
impl ElementList {
|
||||
/// Add an element to the list
|
||||
pub fn add(&mut self, element: impl UiElement + 'static) {
|
||||
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 {
|
||||
let mut list = ElementList(Vec::new());
|
||||
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 {
|
||||
/// Add element as a child/nested element.
|
||||
fn add_child(self, ui: &mut ElementList);
|
||||
|
||||
/// Add element as a ui root.
|
||||
fn add_root(self, ui: &mut UiInstance, max_size: glam::Vec2);
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ impl UiElement for Container {
|
|||
direction: self.direction,
|
||||
},
|
||||
text_measure: ctx.text_measure,
|
||||
current_font: ctx.current_font,
|
||||
});
|
||||
match self.direction {
|
||||
UiDirection::Horizontal => {
|
||||
|
@ -160,7 +161,7 @@ impl UiElement for Container {
|
|||
//.0 = primary, .1 = secondary
|
||||
let pri_sec_align = match self.direction {
|
||||
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
|
||||
|
@ -194,6 +195,7 @@ impl UiElement for Container {
|
|||
state: ctx.state,
|
||||
layout: &el_layout,
|
||||
text_measure: ctx.text_measure,
|
||||
current_font: ctx.current_font,
|
||||
});
|
||||
|
||||
//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.;
|
||||
},
|
||||
(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) => {
|
||||
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,
|
||||
draw: ctx.draw,
|
||||
text_measure: ctx.text_measure,
|
||||
current_font: ctx.current_font,
|
||||
});
|
||||
|
||||
//layout
|
||||
|
|
|
@ -4,7 +4,7 @@ use glam::{vec2, Vec4};
|
|||
use crate::{
|
||||
draw::UiDrawCommand,
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
layout::Size,
|
||||
layout::{Size, Size2d},
|
||||
measure::Response,
|
||||
text::FontHandle,
|
||||
};
|
||||
|
@ -22,9 +22,12 @@ use crate::{
|
|||
pub struct Text {
|
||||
#[setters(into)]
|
||||
pub text: Cow<'static, str>,
|
||||
pub size: (Size, Size),
|
||||
#[setters(into)]
|
||||
pub size: Size2d,
|
||||
#[setters(into)]
|
||||
pub color: Vec4,
|
||||
pub font: FontHandle,
|
||||
#[setters(into)]
|
||||
pub font: Option<FontHandle>,
|
||||
pub text_size: u16,
|
||||
}
|
||||
|
||||
|
@ -32,30 +35,37 @@ impl Default for Text {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
text: "".into(),
|
||||
size: (Size::Auto, Size::Auto),
|
||||
size: (Size::Auto, Size::Auto).into(),
|
||||
color: Vec4::new(1., 1., 1., 1.),
|
||||
font: FontHandle::default(),
|
||||
font: None,
|
||||
text_size: 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
fn font(&self, f: FontHandle) -> FontHandle {
|
||||
self.font.unwrap_or(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl UiElement for Text {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
let mut size = (0., 0.);
|
||||
if matches!(self.size.0, Size::Auto) || matches!(self.size.1, Size::Auto) {
|
||||
let res = ctx.text_measure.measure(self.font, self.text_size, &self.text);
|
||||
if matches!(self.size.width, Size::Auto) || matches!(self.size.height, Size::Auto) {
|
||||
//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.1 = res.height;
|
||||
}
|
||||
Response {
|
||||
size: vec2(
|
||||
match self.size.0 {
|
||||
match self.size.width {
|
||||
Size::Auto => size.0,
|
||||
Size::Fraction(percentage) => ctx.layout.max_size.x * percentage,
|
||||
Size::Static(pixels) => pixels,
|
||||
},
|
||||
match self.size.1 {
|
||||
match self.size.height {
|
||||
Size::Auto => size.1,
|
||||
Size::Fraction(percentage) => ctx.layout.max_size.y * percentage,
|
||||
Size::Static(pixels) => pixels,
|
||||
|
@ -67,12 +77,15 @@ impl UiElement for Text {
|
|||
}
|
||||
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
if self.text.is_empty() || self.color.w == 0. {
|
||||
return
|
||||
}
|
||||
ctx.draw.add(UiDrawCommand::Text {
|
||||
text: self.text.clone(),
|
||||
position: ctx.layout.position,
|
||||
size: self.text_size,
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// Use the `max_size` parameter to specify the maximum size of the element\
|
||||
|
@ -77,6 +99,7 @@ impl UiInstance {
|
|||
state: &self.stateful_state,
|
||||
layout: &layout,
|
||||
text_measure: self.text_renderer.to_measure(),
|
||||
current_font: self.text_renderer.current_font(),
|
||||
});
|
||||
element.process(ProcessContext {
|
||||
measure: &measure,
|
||||
|
@ -84,6 +107,7 @@ impl UiInstance {
|
|||
layout: &layout,
|
||||
draw: &mut self.draw_commands,
|
||||
text_measure: self.text_renderer.to_measure(),
|
||||
current_font: self.text_renderer.current_font(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,43 +1,59 @@
|
|||
//! text rendering, styling, measuring
|
||||
|
||||
use std::sync::Arc;
|
||||
use fontdue::{Font, FontSettings};
|
||||
use crate::draw::atlas::TextureAtlasManager;
|
||||
|
||||
mod font;
|
||||
mod ftm;
|
||||
mod stack;
|
||||
|
||||
use font::FontManager;
|
||||
pub use font::FontHandle;
|
||||
#[cfg(feature="builtin_font")]
|
||||
pub use font::BUILTIN_FONT;
|
||||
use fontdue::{Font, FontSettings};
|
||||
pub use font::FontHandle;
|
||||
|
||||
use font::FontManager;
|
||||
use ftm::FontTextureManager;
|
||||
use ftm::GlyphCacheEntry;
|
||||
|
||||
use crate::draw::atlas::TextureAtlasManager;
|
||||
use stack::FontStack;
|
||||
|
||||
pub struct TextRenderer {
|
||||
fm: FontManager,
|
||||
manager: FontManager,
|
||||
ftm: FontTextureManager,
|
||||
stack: FontStack,
|
||||
}
|
||||
|
||||
impl TextRenderer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
fm: FontManager::new(),
|
||||
manager: FontManager::new(),
|
||||
ftm: FontTextureManager::default(),
|
||||
stack: FontStack::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
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 {
|
||||
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