mirror of
https://github.com/griffi-gh/hUI.git
synced 2024-11-28 18:08:42 -06:00
new "context" system and text measuring
This commit is contained in:
parent
ea6623f143
commit
b65e540f0e
62
hui-examples/examples/test2.rs
Normal file
62
hui-examples/examples/test2.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use std::time::Instant;
|
||||
use glam::{UVec2, vec4};
|
||||
use glium::{backend::glutin::SimpleWindowBuilder, Surface};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{EventLoopBuilder, ControlFlow}
|
||||
};
|
||||
use hui::{
|
||||
element::{
|
||||
container::{Alignment, Container, Sides}, progress_bar::ProgressBar, rect::Rect, text::Text, UiElement
|
||||
}, interaction::IntoInteractable, IfModified, UiDirection, UiInstance, UiSize
|
||||
};
|
||||
use hui_glium::GliumUiRenderer;
|
||||
|
||||
fn main() {
|
||||
kubi_logging::init();
|
||||
|
||||
let event_loop = EventLoopBuilder::new().build().unwrap();
|
||||
let (_window, display) = SimpleWindowBuilder::new().build(&event_loop);
|
||||
|
||||
let mut hui = UiInstance::new();
|
||||
let mut backend = GliumUiRenderer::new(&display);
|
||||
event_loop.run(|event, window_target| {
|
||||
window_target.set_control_flow(ControlFlow::Poll);
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
window_target.exit();
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
let mut frame = display.draw();
|
||||
frame.clear_color_srgb(0.5, 0.5, 0.5, 0.);
|
||||
|
||||
let resolution = UVec2::from(display.get_framebuffer_dimensions()).as_vec2();
|
||||
|
||||
hui.begin();
|
||||
|
||||
hui.add(Container {
|
||||
gap: 5.,
|
||||
padding: Sides::all(5.),
|
||||
align: (Alignment::Center, Alignment::Center),
|
||||
size: (UiSize::Percentage(1.), UiSize::Percentage(1.)),
|
||||
elements: vec![
|
||||
Box::new(Text {
|
||||
text: "Hello, world!\nGoodbye, world!\nowo\nuwu".into(),
|
||||
text_size: 120,
|
||||
..Default::default()
|
||||
}),
|
||||
],
|
||||
..Default::default()
|
||||
}, resolution);
|
||||
|
||||
hui.end();
|
||||
|
||||
backend.update(&hui);
|
||||
backend.draw(&mut frame, resolution);
|
||||
|
||||
frame.finish().unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
|
@ -4,7 +4,7 @@ description = "Simple UI library for games and other interactive applications"
|
|||
repository = "https://github.com/griffi-gh/hui"
|
||||
authors = ["griffi-gh <prasol258@gmail.com>"]
|
||||
rust-version = "1.75"
|
||||
version = "0.0.2"
|
||||
version = "0.0.3"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
publish = true
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::any::Any;
|
||||
use crate::{
|
||||
LayoutInfo,
|
||||
draw::UiDrawCommands,
|
||||
measure::Response,
|
||||
state::StateRepo
|
||||
state::StateRepo,
|
||||
text::TextMeasure,
|
||||
LayoutInfo
|
||||
};
|
||||
|
||||
#[cfg(feature = "builtin_elements")]
|
||||
|
@ -18,12 +19,26 @@ mod builtin {
|
|||
#[cfg(feature = "builtin_elements")]
|
||||
pub use builtin::*;
|
||||
|
||||
pub struct MeasureContext<'a> {
|
||||
pub state: &'a StateRepo,
|
||||
pub layout: &'a LayoutInfo,
|
||||
pub text_measure: TextMeasure<'a>,
|
||||
}
|
||||
|
||||
pub struct ProcessContext<'a> {
|
||||
pub measure: &'a Response,
|
||||
pub state: &'a mut StateRepo,
|
||||
pub layout: &'a LayoutInfo,
|
||||
pub draw: &'a mut UiDrawCommands,
|
||||
pub text_measure: TextMeasure<'a>,
|
||||
}
|
||||
|
||||
pub trait UiElement {
|
||||
fn name(&self) -> &'static str { "UiElement" }
|
||||
fn state_id(&self) -> Option<u64> { None }
|
||||
fn is_stateful(&self) -> bool { self.state_id().is_some() }
|
||||
fn is_stateless(&self) -> bool { self.state_id().is_none() }
|
||||
fn init_state(&self) -> Option<Box<dyn Any>> { None }
|
||||
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response;
|
||||
fn process(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut UiDrawCommands);
|
||||
fn measure(&self, ctx: MeasureContext) -> Response;
|
||||
fn process(&self, ctx: ProcessContext);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
use glam::{Vec2, vec2, Vec4};
|
||||
use crate::{
|
||||
UiDirection,
|
||||
UiSize,
|
||||
LayoutInfo,
|
||||
draw::{UiDrawCommand, UiDrawCommands},
|
||||
measure::{Response, Hints},
|
||||
state::StateRepo,
|
||||
element::UiElement
|
||||
draw::{UiDrawCommand, UiDrawCommands}, element::{MeasureContext, ProcessContext, UiElement}, measure::{Hints, Response}, state::StateRepo, LayoutInfo, UiDirection, UiSize
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
@ -106,15 +100,19 @@ impl Container {
|
|||
}
|
||||
|
||||
impl UiElement for Container {
|
||||
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
let mut size = Vec2::ZERO;
|
||||
//if matches!(self.size.0, UiSize::Auto) || matches!(self.size.1, UiSize::Auto) {
|
||||
let mut leftover_gap = Vec2::ZERO;
|
||||
for element in &self.elements {
|
||||
let measure = element.measure(state, &LayoutInfo {
|
||||
position: layout.position + size,
|
||||
max_size: self.measure_max_inner_size(layout), // - size TODO
|
||||
direction: self.direction,
|
||||
let measure = element.measure(MeasureContext{
|
||||
state: ctx.state,
|
||||
layout: &LayoutInfo {
|
||||
position: ctx.layout.position + size,
|
||||
max_size: self.measure_max_inner_size(ctx.layout), // - size TODO
|
||||
direction: self.direction,
|
||||
},
|
||||
text_measure: ctx.text_measure,
|
||||
});
|
||||
match self.direction {
|
||||
UiDirection::Horizontal => {
|
||||
|
@ -140,12 +138,12 @@ impl UiElement for Container {
|
|||
|
||||
match self.size.0 {
|
||||
UiSize::Auto => (),
|
||||
UiSize::Percentage(percentage) => size.x = layout.max_size.x * percentage,
|
||||
UiSize::Percentage(percentage) => size.x = ctx.layout.max_size.x * percentage,
|
||||
UiSize::Pixels(pixels) => size.x = pixels,
|
||||
}
|
||||
match self.size.1 {
|
||||
UiSize::Auto => (),
|
||||
UiSize::Percentage(percentage) => size.y = layout.max_size.y * percentage,
|
||||
UiSize::Percentage(percentage) => size.y = ctx.layout.max_size.y * percentage,
|
||||
UiSize::Pixels(pixels) => size.y = pixels,
|
||||
}
|
||||
|
||||
|
@ -159,14 +157,14 @@ impl UiElement for Container {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut UiDrawCommands) {
|
||||
let mut position = layout.position;
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
let mut position = ctx.layout.position;
|
||||
|
||||
//background
|
||||
if let Some(color) = self.background {
|
||||
draw.add(UiDrawCommand::Rectangle {
|
||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
position,
|
||||
size: measure.size,
|
||||
size: ctx.measure.size,
|
||||
color
|
||||
});
|
||||
}
|
||||
|
@ -178,16 +176,16 @@ impl UiElement for Container {
|
|||
match (self.align.0, self.direction) {
|
||||
(Alignment::Begin, _) => (),
|
||||
(Alignment::Center, UiDirection::Horizontal) => {
|
||||
position.x += (measure.size.x - measure.hints.inner_content_size.unwrap().x) / 2.;
|
||||
position.x += (ctx.measure.size.x - ctx.measure.hints.inner_content_size.unwrap().x) / 2.;
|
||||
},
|
||||
(Alignment::Center, UiDirection::Vertical) => {
|
||||
position.y += (measure.size.y - measure.hints.inner_content_size.unwrap().y) / 2.;
|
||||
position.y += (ctx.measure.size.y - ctx.measure.hints.inner_content_size.unwrap().y) / 2.;
|
||||
},
|
||||
(Alignment::End, UiDirection::Horizontal) => {
|
||||
position.x += measure.size.x - measure.hints.inner_content_size.unwrap().x - self.padding.right - self.padding.left;
|
||||
position.x += ctx.measure.size.x - ctx.measure.hints.inner_content_size.unwrap().x - self.padding.right - self.padding.left;
|
||||
},
|
||||
(Alignment::End, UiDirection::Vertical) => {
|
||||
position.y += measure.size.y - measure.hints.inner_content_size.unwrap().y - self.padding.bottom - self.padding.top;
|
||||
position.y += ctx.measure.size.y - ctx.measure.hints.inner_content_size.unwrap().y - self.padding.bottom - self.padding.top;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,32 +194,42 @@ impl UiElement for Container {
|
|||
|
||||
let mut el_layout = LayoutInfo {
|
||||
position,
|
||||
max_size: self.measure_max_inner_size(layout),
|
||||
max_size: self.measure_max_inner_size(ctx.layout),
|
||||
direction: self.direction,
|
||||
};
|
||||
|
||||
//measure
|
||||
let el_measure = element.measure(state, &el_layout);
|
||||
let el_measure = element.measure(MeasureContext {
|
||||
state: ctx.state,
|
||||
layout: &el_layout,
|
||||
text_measure: ctx.text_measure,
|
||||
});
|
||||
|
||||
//align (on sec. axis)
|
||||
match (self.align.1, self.direction) {
|
||||
(Alignment::Begin, _) => (),
|
||||
(Alignment::Center, UiDirection::Horizontal) => {
|
||||
el_layout.position.y += (measure.size.y - self.padding.bottom - self.padding.top - el_measure.size.y) / 2.;
|
||||
el_layout.position.y += (ctx.measure.size.y - self.padding.bottom - self.padding.top - el_measure.size.y) / 2.;
|
||||
},
|
||||
(Alignment::Center, UiDirection::Vertical) => {
|
||||
el_layout.position.x += (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) => {
|
||||
el_layout.position.y += 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;
|
||||
},
|
||||
(Alignment::End, UiDirection::Vertical) => {
|
||||
el_layout.position.x += 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;
|
||||
}
|
||||
}
|
||||
|
||||
//process
|
||||
element.process(&el_measure, state, &el_layout, draw);
|
||||
element.process(ProcessContext {
|
||||
measure: &el_measure,
|
||||
state: ctx.state,
|
||||
layout: &el_layout,
|
||||
draw: ctx.draw,
|
||||
text_measure: ctx.text_measure,
|
||||
});
|
||||
|
||||
//layout
|
||||
match self.direction {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use glam::{vec2, Vec4, vec4};
|
||||
use crate::{
|
||||
UiSize, LayoutInfo,
|
||||
draw::{UiDrawCommand, UiDrawCommands},
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
measure::Response,
|
||||
state::StateRepo,
|
||||
element::UiElement
|
||||
LayoutInfo,
|
||||
UiSize
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -31,17 +32,17 @@ const BAR_HEIGHT: f32 = 20.0;
|
|||
impl UiElement for ProgressBar {
|
||||
fn name(&self) -> &'static str { "Progress bar" }
|
||||
|
||||
fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
Response {
|
||||
size: vec2(
|
||||
match self.size.0 {
|
||||
UiSize::Auto => layout.max_size.x.max(300.),
|
||||
UiSize::Percentage(p) => layout.max_size.x * p,
|
||||
UiSize::Auto => ctx.layout.max_size.x.max(300.),
|
||||
UiSize::Percentage(p) => ctx.layout.max_size.x * p,
|
||||
UiSize::Pixels(p) => p,
|
||||
},
|
||||
match self.size.1 {
|
||||
UiSize::Auto => BAR_HEIGHT,
|
||||
UiSize::Percentage(p) => layout.max_size.y * p,
|
||||
UiSize::Percentage(p) => ctx.layout.max_size.y * p,
|
||||
UiSize::Pixels(p) => p,
|
||||
}
|
||||
),
|
||||
|
@ -50,19 +51,19 @@ impl UiElement for ProgressBar {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, measure: &Response, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut UiDrawCommands) {
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
let value = self.value.clamp(0., 1.);
|
||||
if value < 1. {
|
||||
draw.add(UiDrawCommand::Rectangle {
|
||||
position: layout.position,
|
||||
size: measure.size,
|
||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
position: ctx.layout.position,
|
||||
size: ctx.measure.size,
|
||||
color: self.color_background
|
||||
});
|
||||
}
|
||||
if value > 0. {
|
||||
draw.add(UiDrawCommand::Rectangle {
|
||||
position: layout.position,
|
||||
size: measure.size * vec2(value, 1.0),
|
||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
position: ctx.layout.position,
|
||||
size: ctx.measure.size * vec2(value, 1.0),
|
||||
color: self.color_foreground
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use glam::{vec2, Vec4};
|
||||
use crate::{
|
||||
LayoutInfo,
|
||||
UiSize,
|
||||
element::UiElement,
|
||||
state::StateRepo,
|
||||
draw::{UiDrawCommand, UiDrawCommands},
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
measure::Response,
|
||||
draw::{UiDrawCommand, UiDrawCommands}
|
||||
state::StateRepo,
|
||||
LayoutInfo, UiSize
|
||||
};
|
||||
|
||||
pub struct Rect {
|
||||
|
@ -23,17 +22,17 @@ impl Default for Rect {
|
|||
}
|
||||
|
||||
impl UiElement for Rect {
|
||||
fn measure(&self, _state: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
Response {
|
||||
size: vec2(
|
||||
match self.size.0 {
|
||||
UiSize::Auto => layout.max_size.x,
|
||||
UiSize::Percentage(percentage) => layout.max_size.x * percentage,
|
||||
UiSize::Auto => ctx.layout.max_size.x,
|
||||
UiSize::Percentage(percentage) => ctx.layout.max_size.x * percentage,
|
||||
UiSize::Pixels(pixels) => pixels,
|
||||
},
|
||||
match self.size.1 {
|
||||
UiSize::Auto => layout.max_size.y,
|
||||
UiSize::Percentage(percentage) => layout.max_size.y * percentage,
|
||||
UiSize::Auto => ctx.layout.max_size.y,
|
||||
UiSize::Percentage(percentage) => ctx.layout.max_size.y * percentage,
|
||||
UiSize::Pixels(pixels) => pixels,
|
||||
},
|
||||
),
|
||||
|
@ -42,11 +41,11 @@ impl UiElement for Rect {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, measure: &Response, _state: &mut StateRepo, layout: &LayoutInfo, draw: &mut UiDrawCommands) {
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
if let Some(color) = self.color {
|
||||
draw.add(UiDrawCommand::Rectangle {
|
||||
position: layout.position,
|
||||
size: measure.size,
|
||||
ctx.draw.add(UiDrawCommand::Rectangle {
|
||||
position: ctx.layout.position,
|
||||
size: ctx.measure.size,
|
||||
color,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use glam::vec2;
|
||||
use crate::{
|
||||
LayoutInfo,
|
||||
UiDirection,
|
||||
element::UiElement,
|
||||
state::StateRepo,
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
measure::Response,
|
||||
draw::{UiDrawCommand, UiDrawCommands}
|
||||
UiDirection
|
||||
};
|
||||
|
||||
pub struct Spacer(pub f32);
|
||||
|
@ -17,9 +14,9 @@ impl Default for Spacer {
|
|||
}
|
||||
|
||||
impl UiElement for Spacer {
|
||||
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
Response {
|
||||
size: match layout.direction {
|
||||
size: match ctx.layout.direction {
|
||||
UiDirection::Horizontal => vec2(self.0, 0.),
|
||||
UiDirection::Vertical => vec2(0., self.0),
|
||||
},
|
||||
|
@ -28,5 +25,5 @@ impl UiElement for Spacer {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, _measure: &Response, _state: &mut StateRepo, _layout: &LayoutInfo, _draw: &mut UiDrawCommands) {}
|
||||
fn process(&self, _ctx: ProcessContext) {}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
use glam::{vec2, Vec4};
|
||||
use crate::{
|
||||
LayoutInfo,
|
||||
UiSize,
|
||||
element::UiElement,
|
||||
state::StateRepo,
|
||||
draw::{UiDrawCommand, UiDrawCommands},
|
||||
element::{MeasureContext, ProcessContext, UiElement},
|
||||
measure::Response,
|
||||
draw::{UiDrawCommand, UiDrawCommands}, text::FontHandle
|
||||
state::StateRepo,
|
||||
text::FontHandle,
|
||||
LayoutInfo, UiSize
|
||||
};
|
||||
|
||||
pub struct Text {
|
||||
|
@ -30,17 +30,23 @@ impl Default for Text {
|
|||
}
|
||||
|
||||
impl UiElement for Text {
|
||||
fn measure(&self, _state: &StateRepo, layout: &LayoutInfo) -> Response {
|
||||
fn measure(&self, ctx: MeasureContext) -> Response {
|
||||
let mut size = (0., 0.);
|
||||
if matches!(self.size.0, UiSize::Auto) || matches!(self.size.1, UiSize::Auto) {
|
||||
let res = ctx.text_measure.measure(self.font, self.text_size, &self.text);
|
||||
size.0 = res.max_width;
|
||||
size.1 = res.height;
|
||||
}
|
||||
Response {
|
||||
size: vec2(
|
||||
match self.size.0 {
|
||||
UiSize::Auto => layout.max_size.x,
|
||||
UiSize::Percentage(percentage) => layout.max_size.x * percentage,
|
||||
UiSize::Auto => size.0,
|
||||
UiSize::Percentage(percentage) => ctx.layout.max_size.x * percentage,
|
||||
UiSize::Pixels(pixels) => pixels,
|
||||
},
|
||||
match self.size.1 {
|
||||
UiSize::Auto => self.text_size as f32,
|
||||
UiSize::Percentage(percentage) => layout.max_size.y * percentage,
|
||||
UiSize::Auto => size.1,
|
||||
UiSize::Percentage(percentage) => ctx.layout.max_size.y * percentage,
|
||||
UiSize::Pixels(pixels) => pixels,
|
||||
},
|
||||
),
|
||||
|
@ -49,10 +55,10 @@ impl UiElement for Text {
|
|||
}
|
||||
}
|
||||
|
||||
fn process(&self, _measure: &Response, _state: &mut StateRepo, layout: &LayoutInfo, draw: &mut UiDrawCommands) {
|
||||
draw.add(UiDrawCommand::Text {
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
ctx.draw.add(UiDrawCommand::Text {
|
||||
text: self.text.clone(),
|
||||
position: layout.position,
|
||||
position: ctx.layout.position,
|
||||
size: self.text_size,
|
||||
color: self.color,
|
||||
font: self.font
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{element::UiElement, draw::UiDrawCommands};
|
||||
use crate::element::{UiElement, MeasureContext, ProcessContext};
|
||||
|
||||
pub struct Interactable<T: UiElement> {
|
||||
pub element: T,
|
||||
|
@ -31,12 +31,12 @@ impl<T: UiElement> Interactable<T> {
|
|||
}
|
||||
|
||||
impl<T: UiElement> UiElement for Interactable<T> {
|
||||
fn measure(&self, state: &crate::state::StateRepo, layout: &crate::LayoutInfo) -> crate::measure::Response {
|
||||
self.element.measure(state, layout)
|
||||
fn measure(&self, ctx: MeasureContext) -> crate::measure::Response {
|
||||
self.element.measure(ctx)
|
||||
}
|
||||
|
||||
fn process(&self, measure: &crate::measure::Response, state: &mut crate::state::StateRepo, layout: &crate::LayoutInfo, draw: &mut UiDrawCommands) {
|
||||
self.element.process(measure, state, layout, draw)
|
||||
fn process(&self, ctx: ProcessContext) {
|
||||
self.element.process(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub mod state;
|
|||
pub mod text;
|
||||
pub mod interaction;
|
||||
|
||||
use element::UiElement;
|
||||
use element::{MeasureContext, ProcessContext, UiElement};
|
||||
use state::StateRepo;
|
||||
use draw::{UiDrawCommands, UiDrawPlan};
|
||||
use text::{TextRenderer, FontTextureInfo, FontHandle};
|
||||
|
@ -65,8 +65,18 @@ impl UiInstance {
|
|||
max_size,
|
||||
direction: UiDirection::Vertical,
|
||||
};
|
||||
let measure = element.measure(&self.stateful_state, &layout);
|
||||
element.process(&measure, &mut self.stateful_state, &layout, &mut self.draw_commands);
|
||||
let measure = element.measure(MeasureContext {
|
||||
state: &self.stateful_state,
|
||||
layout: &layout,
|
||||
text_measure: self.text_renderer.to_measure(),
|
||||
});
|
||||
element.process(ProcessContext {
|
||||
measure: &measure,
|
||||
state: &mut self.stateful_state,
|
||||
layout: &layout,
|
||||
draw: &mut self.draw_commands,
|
||||
text_measure: self.text_renderer.to_measure(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn begin(&mut self) {
|
||||
|
|
|
@ -48,3 +48,41 @@ impl Default for TextRenderer {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextMeasureResponse {
|
||||
pub max_width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TextMeasure<'a>(&'a TextRenderer);
|
||||
|
||||
impl<'a> TextMeasure<'a> {
|
||||
pub fn measure(&self, font: FontHandle, size: u8, text: &str) -> TextMeasureResponse {
|
||||
use fontdue::layout::{Layout, CoordinateSystem, TextStyle};
|
||||
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
|
||||
layout.append(
|
||||
&[self.0.internal_font(font)],
|
||||
&TextStyle::new(text, size as f32, 0)
|
||||
);
|
||||
TextMeasureResponse {
|
||||
max_width: layout.lines().map(|lines| {
|
||||
lines.iter().fold(0.0_f32, |acc, x| {
|
||||
let glyph = layout.glyphs().get(x.glyph_end).unwrap();
|
||||
acc.max(glyph.x + glyph.width as f32)
|
||||
})
|
||||
}).unwrap_or(0.),
|
||||
height: layout.height() as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextRenderer {
|
||||
pub fn to_measure(&self) -> TextMeasure {
|
||||
TextMeasure(self)
|
||||
}
|
||||
|
||||
pub fn measure(&self, font: FontHandle, size: u8, text: &str) -> TextMeasureResponse {
|
||||
TextMeasure(self).measure(font, size, text)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue