scissor stack fixes, proper render & event transformations

This commit is contained in:
Aleksander
2025-09-29 22:04:16 +02:00
parent b73870236f
commit a1edc2f0b8
24 changed files with 361 additions and 218 deletions

View File

@@ -36,6 +36,7 @@
flex_wrap="wrap" flex_wrap="wrap"
justify_content="stretch" justify_content="stretch"
gap="4" gap="4"
overflow_y="scroll"
/> />
</elements> </elements>
</layout> </layout>

View File

@@ -1,10 +1,9 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use wgui::{ use wgui::{
components::{self, button::ComponentButton}, components::button::ComponentButton,
layout::WidgetPair, layout::WidgetPair,
parser::{Fetchable, ParseDocumentParams, ParserData, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserData, ParserState},
taffy::{self, Dimension, prelude::length},
}; };
use crate::{ use crate::{
@@ -16,7 +15,9 @@ pub struct TabApps {
#[allow(dead_code)] #[allow(dead_code)]
pub state: ParserState, pub state: ParserState,
#[allow(dead_code)]
entries: Vec<DesktopEntry>, entries: Vec<DesktopEntry>,
#[allow(dead_code)]
app_list: AppList, app_list: AppList,
} }
@@ -105,7 +106,7 @@ impl AppList {
)?; )?;
let button = data.fetch_component_as::<ComponentButton>("button")?; let button = data.fetch_component_as::<ComponentButton>("button")?;
button.on_click(Box::new(move |common, evt| { button.on_click(Box::new(move |_common, _evt| {
log::info!("click"); log::info!("click");
Ok(()) Ok(())
})); }));

View File

@@ -1,6 +1,7 @@
use glam::{FloatExt, Vec2}; use glam::FloatExt;
use crate::{ use crate::{
drawing::Boundary,
event::{CallbackDataCommon, EventAlterables}, event::{CallbackDataCommon, EventAlterables},
layout::{LayoutState, WidgetID}, layout::{LayoutState, WidgetID},
widget::{WidgetData, WidgetObj}, widget::{WidgetData, WidgetObj},
@@ -46,7 +47,7 @@ pub struct CallbackData<'a> {
pub obj: &'a mut dyn WidgetObj, pub obj: &'a mut dyn WidgetObj,
pub data: &'a mut WidgetData, pub data: &'a mut WidgetData,
pub widget_id: WidgetID, pub widget_id: WidgetID,
pub widget_size: Vec2, pub widget_boundary: Boundary,
pub pos: f32, // 0.0 (start of animation) - 1.0 (end of animation) pub pos: f32, // 0.0 (start of animation) - 1.0 (end of animation)
} }
@@ -98,14 +99,13 @@ impl Animation {
}; };
let widget_node = *state.nodes.get(self.target_widget).unwrap(); let widget_node = *state.nodes.get(self.target_widget).unwrap();
let layout = state.tree.layout(widget_node).unwrap(); // should always succeed
let mut widget_state = widget.state(); let mut widget_state = widget.state();
let (data, obj) = widget_state.get_data_obj_mut(); let (data, obj) = widget_state.get_data_obj_mut();
let data = &mut CallbackData { let data = &mut CallbackData {
widget_id: self.target_widget, widget_id: self.target_widget,
widget_size: Vec2::new(layout.size.width, layout.size.height), widget_boundary: state.get_widget_boundary(widget_node),
obj, obj,
data, data,
pos, pos,

View File

@@ -1,21 +1,25 @@
use std::{cell::RefCell, rc::Rc};
use taffy::{AlignItems, JustifyContent};
use crate::{ use crate::{
animation::{Animation, AnimationEasing}, animation::{Animation, AnimationEasing},
components::{Component, ComponentBase, ComponentTrait, InitData}, components::{Component, ComponentBase, ComponentTrait, InitData},
drawing::{self, Color}, drawing::{self, Boundary, Color},
event::{CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec},
globals::Globals, globals::Globals,
i18n::Translation, i18n::Translation,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::{
text::{FontWeight, TextStyle},
util::centered_matrix,
},
widget::{ widget::{
WidgetData,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength, util::WLength,
}, },
}; };
use glam::{Mat4, Vec3};
use std::{cell::RefCell, rc::Rc};
use taffy::{AlignItems, JustifyContent};
pub struct Params { pub struct Params {
pub text: Option<Translation>, // if unset, label will not be populated pub text: Option<Translation>, // if unset, label will not be populated
@@ -93,24 +97,43 @@ fn get_color2(color: &drawing::Color) -> drawing::Color {
color.lerp(&Color::new(0.0, 0.0, 0.0, color.a), 0.2) color.lerp(&Color::new(0.0, 0.0, 0.0, color.a), 0.2)
} }
fn anim_hover(rect: &mut WidgetRectangle, data: &Data, pos: f32, pressed: bool) { fn anim_hover(
rect: &mut WidgetRectangle,
widget_data: &mut WidgetData,
data: &Data,
widget_boundary: Boundary,
pos: f32,
pressed: bool,
) {
let mult = pos * if pressed { 1.5 } else { 1.0 }; let mult = pos * if pressed { 1.5 } else { 1.0 };
let bgcolor = data.initial_color.lerp(&data.initial_hover_color, mult); let bgcolor = data.initial_color.lerp(&data.initial_hover_color, mult);
//let t = Mat4::from_scale(Vec3::splat(1.0 + pos * 0.5)) * Mat4::from_rotation_z(pos * 1.0);
let t = Mat4::from_scale(Vec3::splat(1.0 + pos * 0.05));
widget_data.transform = centered_matrix(widget_boundary.size, &t);
rect.params.color = bgcolor; rect.params.color = bgcolor;
rect.params.color2 = get_color2(&bgcolor); rect.params.color2 = get_color2(&bgcolor);
rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult); rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult);
rect.params.border = 2.0; rect.params.border = 2.0;
} }
fn anim_hover_out(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: WidgetID) -> Animation { fn anim_hover_create(data: Rc<Data>, state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool) -> Animation {
Animation::new( Animation::new(
widget_id, widget_id,
15, if fade_in { 5 } else { 10 },
AnimationEasing::OutCubic, AnimationEasing::OutCubic,
Box::new(move |common, anim_data| { Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
anim_hover(rect, &data, 1.0 - anim_data.pos, state.borrow().down); anim_hover(
rect,
anim_data.data,
&data,
anim_data.widget_boundary,
if fade_in { anim_data.pos } else { 1.0 - anim_data.pos },
state.borrow().down,
);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
}), }),
) )
@@ -129,10 +152,13 @@ fn register_event_mouse_enter<U1, U2>(
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, _, _| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common.alterables.mark_redraw(); common.alterables.mark_redraw();
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); common.alterables.animate(anim_hover_create(
let mut state = state.borrow_mut(); data.clone(),
anim_hover(rect, &data, 1.0, state.down); state.clone(),
state.hovered = true; event_data.widget_id,
true,
));
state.borrow_mut().hovered = true;
Ok(()) Ok(())
}), }),
); );
@@ -150,9 +176,12 @@ fn register_event_mouse_leave<U1, U2>(
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, _, _| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
common common.alterables.animate(anim_hover_create(
.alterables data.clone(),
.animate(anim_hover_out(data.clone(), state.clone(), event_data.widget_id)); state.clone(),
event_data.widget_id,
false,
));
state.borrow_mut().hovered = false; state.borrow_mut().hovered = false;
Ok(()) Ok(())
}), }),
@@ -173,7 +202,14 @@ fn register_event_mouse_press<U1, U2>(
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
anim_hover(rect, &data, 1.0, true); anim_hover(
rect,
event_data.widget_data,
&data,
common.state.get_widget_boundary(event_data.node_id),
1.0,
true,
);
if state.hovered { if state.hovered {
state.down = true; state.down = true;
@@ -199,7 +235,14 @@ fn register_event_mouse_release<U1, U2>(
EventListenerKind::MouseRelease, EventListenerKind::MouseRelease,
Box::new(move |common, event_data, _, _| { Box::new(move |common, event_data, _, _| {
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
anim_hover(rect, &data, 1.0, false); anim_hover(
rect,
event_data.widget_data,
&data,
common.state.get_widget_boundary(event_data.node_id),
1.0,
false,
);
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
if state.down { if state.down {
@@ -226,7 +269,7 @@ pub fn construct<U1, U2>(
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection<U1, U2>,
parent: WidgetID, parent: WidgetID,
params: Params, params: Params,
) -> anyhow::Result<(WidgetID, Rc<ComponentButton>)> { ) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
let mut style = params.style; let mut style = params.style;
// force-override style // force-override style
@@ -260,7 +303,7 @@ pub fn construct<U1, U2>(
Color::new(color.r + 0.5, color.g + 0.5, color.g + 0.5, color.a + 0.5) Color::new(color.r + 0.5, color.g + 0.5, color.g + 0.5, color.a + 0.5)
}; };
let (id_root, _) = layout.add_child( let (root, _) = layout.add_child(
parent, parent,
WidgetRectangle::create(WidgetRectangleParams { WidgetRectangle::create(WidgetRectangleParams {
color, color,
@@ -272,12 +315,13 @@ pub fn construct<U1, U2>(
}), }),
style, style,
)?; )?;
let id_rect = id_root;
let id_rect = root.id;
let light_text = (color.r + color.g + color.b) < 1.5; let light_text = (color.r + color.g + color.b) < 1.5;
let id_label = if let Some(content) = params.text { let id_label = if let Some(content) = params.text {
let (id_label, _node_label) = layout.add_child( let (label, _node_label) = layout.add_child(
id_rect, id_rect,
WidgetLabel::create( WidgetLabel::create(
globals, globals,
@@ -296,7 +340,7 @@ pub fn construct<U1, U2>(
), ),
Default::default(), Default::default(),
)?; )?;
id_label label.id
} else { } else {
WidgetID::default() WidgetID::default()
}; };
@@ -326,5 +370,5 @@ pub fn construct<U1, U2>(
let button = Rc::new(ComponentButton { base, data, state }); let button = Rc::new(ComponentButton { base, data, state });
layout.defer_component_init(Component(button.clone())); layout.defer_component_init(Component(button.clone()));
Ok((id_root, button)) Ok((root, button))
} }

View File

@@ -10,7 +10,7 @@ use crate::{
drawing::Color, drawing::Color,
event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, ListenerHandleVec},
i18n::Translation, i18n::Translation,
layout::{self, Layout, LayoutState, WidgetID}, layout::{self, Layout, LayoutState, WidgetID, WidgetPair},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::text::{FontWeight, TextStyle},
widget::{ widget::{
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
@@ -249,7 +249,7 @@ pub fn construct<U1, U2>(
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection<U1, U2>,
parent: WidgetID, parent: WidgetID,
params: Params, params: Params,
) -> anyhow::Result<(WidgetID, Rc<ComponentCheckbox>)> { ) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
let mut style = params.style; let mut style = params.style;
// force-override style // force-override style
@@ -267,7 +267,7 @@ pub fn construct<U1, U2>(
let globals = layout.state.globals.clone(); let globals = layout.state.globals.clone();
let (id_root, _) = layout.add_child( let (root, _) = layout.add_child(
parent, parent,
WidgetRectangle::create(WidgetRectangleParams { WidgetRectangle::create(WidgetRectangleParams {
color: Color::new(1.0, 1.0, 1.0, 0.0), color: Color::new(1.0, 1.0, 1.0, 0.0),
@@ -277,14 +277,15 @@ pub fn construct<U1, U2>(
}), }),
style, style,
)?; )?;
let id_container = id_root;
let id_container = root.id;
let box_size = taffy::Size { let box_size = taffy::Size {
width: length(params.box_size), width: length(params.box_size),
height: length(params.box_size), height: length(params.box_size),
}; };
let (id_outer_box, _) = layout.add_child( let (outer_box, _) = layout.add_child(
id_container, id_container,
WidgetRectangle::create(WidgetRectangleParams { WidgetRectangle::create(WidgetRectangleParams {
border: 2.0, border: 2.0,
@@ -302,8 +303,8 @@ pub fn construct<U1, U2>(
}, },
)?; )?;
let (id_inner_box, _) = layout.add_child( let (inner_box, _) = layout.add_child(
id_outer_box, outer_box.id,
WidgetRectangle::create(WidgetRectangleParams { WidgetRectangle::create(WidgetRectangleParams {
round: WLength::Units(5.0), round: WLength::Units(5.0),
color: if params.checked { COLOR_CHECKED } else { COLOR_UNCHECKED }, color: if params.checked { COLOR_CHECKED } else { COLOR_UNCHECKED },
@@ -318,7 +319,7 @@ pub fn construct<U1, U2>(
}, },
)?; )?;
let (id_label, _node_label) = layout.add_child( let (label, _node_label) = layout.add_child(
id_container, id_container,
WidgetLabel::create( WidgetLabel::create(
&mut globals.get(), &mut globals.get(),
@@ -335,8 +336,8 @@ pub fn construct<U1, U2>(
let data = Rc::new(Data { let data = Rc::new(Data {
id_container, id_container,
id_inner_box, id_inner_box: inner_box.id,
id_label, id_label: label.id,
}); });
let state = Rc::new(RefCell::new(State { let state = Rc::new(RefCell::new(State {
@@ -356,5 +357,5 @@ pub fn construct<U1, U2>(
let checkbox = Rc::new(ComponentCheckbox { base, data, state }); let checkbox = Rc::new(ComponentCheckbox { base, data, state });
layout.defer_component_init(Component(checkbox.clone())); layout.defer_component_init(Component(checkbox.clone()));
Ok((id_root, checkbox)) Ok((root, checkbox))
} }

View File

@@ -9,7 +9,7 @@ use crate::{
drawing::{self}, drawing::{self},
event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec},
i18n::Translation, i18n::Translation,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID, WidgetPair},
renderer_vk::{ renderer_vk::{
text::{FontWeight, HorizontalAlign, TextStyle}, text::{FontWeight, HorizontalAlign, TextStyle},
util, util,
@@ -175,7 +175,7 @@ fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
AnimationEasing::OutBack, AnimationEasing::OutBack,
Box::new(move |common, data| { Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
data.data.transform = get_anim_transform(data.pos, data.widget_size); data.data.transform = get_anim_transform(data.pos, data.widget_boundary.size);
anim_rect(rect, data.pos); anim_rect(rect, data.pos);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
}), }),
@@ -189,7 +189,7 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) {
AnimationEasing::OutQuad, AnimationEasing::OutQuad,
Box::new(move |common, data| { Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap(); let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_size); data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_boundary.size);
anim_rect(rect, 1.0 - data.pos); anim_rect(rect, 1.0 - data.pos);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
}), }),
@@ -308,14 +308,14 @@ pub fn construct<U1, U2>(
listeners: &mut EventListenerCollection<U1, U2>, listeners: &mut EventListenerCollection<U1, U2>,
parent: WidgetID, parent: WidgetID,
params: Params, params: Params,
) -> anyhow::Result<(WidgetID, Rc<ComponentSlider>)> { ) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
let mut style = params.style; let mut style = params.style;
style.position = taffy::Position::Relative; style.position = taffy::Position::Relative;
style.min_size = style.size; style.min_size = style.size;
style.max_size = style.size; style.max_size = style.size;
let (root_id, slider_body_node) = layout.add_child(parent, WidgetDiv::create(), style)?; let (root, slider_body_node) = layout.add_child(parent, WidgetDiv::create(), style)?;
let body_id = root_id; let body_id = root.id;
let (_background_id, _) = layout.add_child( let (_background_id, _) = layout.add_child(
body_id, body_id,
@@ -350,10 +350,10 @@ pub fn construct<U1, U2>(
}; };
// invisible outer handle body // invisible outer handle body
let (slider_handle_id, slider_handle_node) = layout.add_child(body_id, WidgetDiv::create(), slider_handle_style)?; let (slider_handle, slider_handle_node) = layout.add_child(body_id, WidgetDiv::create(), slider_handle_style)?;
let (slider_handle_rect_id, _) = layout.add_child( let (slider_handle_rect, _) = layout.add_child(
slider_handle_id, slider_handle.id,
WidgetRectangle::create(WidgetRectangleParams { WidgetRectangle::create(WidgetRectangleParams {
color: HANDLE_COLOR, color: HANDLE_COLOR,
border_color: HANDLE_BORDER_COLOR, border_color: HANDLE_BORDER_COLOR,
@@ -379,8 +379,8 @@ pub fn construct<U1, U2>(
let globals = layout.state.globals.clone(); let globals = layout.state.globals.clone();
let (slider_text_id, _) = layout.add_child( let (slider_text, _) = layout.add_child(
slider_handle_id, slider_handle.id,
WidgetLabel::create( WidgetLabel::create(
&mut globals.get(), &mut globals.get(),
WidgetLabelParams { WidgetLabelParams {
@@ -399,9 +399,9 @@ pub fn construct<U1, U2>(
let data = Rc::new(Data { let data = Rc::new(Data {
body: body_id, body: body_id,
slider_handle_node, slider_handle_node,
slider_handle_rect_id, slider_handle_rect_id: slider_handle_rect.id,
slider_body_node, slider_body_node,
slider_text_id, slider_text_id: slider_text.id,
}); });
let state = Rc::new(RefCell::new(state)); let state = Rc::new(RefCell::new(state));
@@ -418,5 +418,5 @@ pub fn construct<U1, U2>(
let slider = Rc::new(ComponentSlider { base, data, state }); let slider = Rc::new(ComponentSlider { base, data, state });
layout.defer_component_init(Component(slider.clone())); layout.defer_component_init(Component(slider.clone()));
Ok((root_id, slider)) Ok((root, slider))
} }

View File

@@ -7,8 +7,8 @@ use taffy::TraversePartialTree;
use crate::{ use crate::{
drawing, drawing,
layout::Widget, layout::Widget,
renderer_vk::text::{custom_glyph::CustomGlyph, TextShadow}, renderer_vk::text::{TextShadow, custom_glyph::CustomGlyph},
stack::{self, ScissorStack, TransformStack}, stack::{self, ScissorBoundary, ScissorStack, TransformStack},
widget::{self}, widget::{self},
}; };
@@ -29,11 +29,22 @@ impl Boundary {
Self { pos, size } Self { pos, size }
} }
pub const fn construct(transform_stack: &TransformStack) -> Self { /// top-left is an absolute position
pub const fn construct_absolute(transform_stack: &TransformStack) -> Self {
let transform = transform_stack.get(); let transform = transform_stack.get();
Self { Self {
pos: Vec2::new(transform.pos.x, transform.pos.y), pos: Vec2::new(transform.abs_pos.x, transform.abs_pos.y),
size: Vec2::new(transform.dim.x, transform.dim.y),
}
}
/// top-left is zero
pub const fn construct_relative(transform_stack: &TransformStack) -> Self {
let transform = transform_stack.get();
Self {
pos: Vec2::ZERO,
size: Vec2::new(transform.dim.x, transform.dim.y), size: Vec2::new(transform.dim.x, transform.dim.y),
} }
} }
@@ -137,8 +148,7 @@ pub enum RenderPrimitive {
Rectangle(PrimitiveExtent, Rectangle), Rectangle(PrimitiveExtent, Rectangle),
Text(PrimitiveExtent, Rc<RefCell<Buffer>>, Option<TextShadow>), Text(PrimitiveExtent, Rc<RefCell<Buffer>>, Option<TextShadow>),
Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice Sprite(PrimitiveExtent, Option<CustomGlyph>), //option because we want as_slice
ScissorEnable(Boundary), ScissorSet(ScissorBoundary),
ScissorDisable,
} }
pub struct DrawParams<'a> { pub struct DrawParams<'a> {
@@ -146,7 +156,7 @@ pub struct DrawParams<'a> {
pub debug_draw: bool, pub debug_draw: bool,
} }
fn has_overflow_clip(style: &taffy::Style) -> bool { pub fn has_overflow_clip(style: &taffy::Style) -> bool {
style.overflow.x != taffy::Overflow::Visible || style.overflow.y != taffy::Overflow::Visible style.overflow.x != taffy::Overflow::Visible || style.overflow.y != taffy::Overflow::Visible
} }
@@ -171,7 +181,6 @@ fn draw_widget(
node_id: taffy::NodeId, node_id: taffy::NodeId,
style: &taffy::Style, style: &taffy::Style,
widget: &Widget, widget: &Widget,
parent_transform: &glam::Mat4,
) { ) {
let Ok(l) = params.layout.state.tree.layout(node_id) else { let Ok(l) = params.layout.state.tree.layout(node_id) else {
debug_assert!(false); debug_assert!(false);
@@ -180,24 +189,23 @@ fn draw_widget(
let mut widget_state = widget.state(); let mut widget_state = widget.state();
let transform = widget_state.data.transform * *parent_transform; let (scroll_shift, info) = match widget::get_scrollbar_info(l) {
let (shift, info) = match widget::get_scrollbar_info(l) {
Some(info) => (widget_state.get_scroll_shift(&info, l), Some(info)), Some(info) => (widget_state.get_scroll_shift(&info, l), Some(info)),
None => (Vec2::default(), None), None => (Vec2::default(), None),
}; };
state.transform_stack.push(stack::Transform { state.transform_stack.push(stack::Transform {
pos: Vec2::new(l.location.x, l.location.y) - shift, rel_pos: Vec2::new(l.location.x, l.location.y) - scroll_shift,
transform, transform: widget_state.data.transform,
dim: Vec2::new(l.size.width, l.size.height), dim: Vec2::new(l.size.width, l.size.height),
..Default::default()
}); });
if params.debug_draw { if params.debug_draw {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct_relative(state.transform_stack);
state.primitives.push(primitive_debug_rect( state.primitives.push(primitive_debug_rect(
&boundary, &boundary,
&transform, &state.transform_stack.get().transform,
Color::new(0.0, 1.0, 1.0, 0.5), Color::new(0.0, 1.0, 1.0, 0.5),
)); ));
} }
@@ -205,17 +213,23 @@ fn draw_widget(
let scissor_pushed = info.is_some() && has_overflow_clip(style); let scissor_pushed = info.is_some() && has_overflow_clip(style);
if scissor_pushed { if scissor_pushed {
let boundary = drawing::Boundary::construct(state.transform_stack); let mut boundary_absolute = drawing::Boundary::construct_absolute(state.transform_stack);
state.scissor_stack.push(boundary); boundary_absolute.pos += scroll_shift;
state.scissor_stack.push(ScissorBoundary(boundary_absolute));
if params.debug_draw { if params.debug_draw {
let mut boundary_relative = drawing::Boundary::construct_relative(state.transform_stack);
boundary_relative.pos += scroll_shift;
state.primitives.push(primitive_debug_rect( state.primitives.push(primitive_debug_rect(
&boundary, &boundary_relative,
&transform, &state.transform_stack.get().transform,
Color::new(1.0, 0.0, 1.0, 1.0), Color::new(1.0, 0.0, 1.0, 1.0),
)); ));
} }
state.primitives.push(drawing::RenderPrimitive::ScissorEnable(boundary)); state
.primitives
.push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get()));
} }
let draw_params = widget::DrawParams { let draw_params = widget::DrawParams {
@@ -226,11 +240,13 @@ fn draw_widget(
widget_state.draw_all(state, &draw_params); widget_state.draw_all(state, &draw_params);
draw_children(params, state, node_id, &transform); draw_children(params, state, node_id);
if scissor_pushed { if scissor_pushed {
state.primitives.push(drawing::RenderPrimitive::ScissorDisable);
state.scissor_stack.pop(); state.scissor_stack.pop();
state
.primitives
.push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get()));
} }
state.transform_stack.pop(); state.transform_stack.pop();
@@ -240,7 +256,7 @@ fn draw_widget(
} }
} }
fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId, model: &glam::Mat4) { fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taffy::NodeId) {
let layout = &params.layout; let layout = &params.layout;
for node_id in layout.state.tree.child_ids(parent_node_id) { for node_id in layout.state.tree.child_ids(parent_node_id) {
@@ -259,7 +275,7 @@ fn draw_children(params: &DrawParams, state: &mut DrawState, parent_node_id: taf
continue; continue;
}; };
draw_widget(params, state, node_id, style, widget, model); draw_widget(params, state, node_id, style, widget);
} }
} }
@@ -267,7 +283,6 @@ pub fn draw(params: &DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
let mut primitives = Vec::<RenderPrimitive>::new(); let mut primitives = Vec::<RenderPrimitive>::new();
let mut transform_stack = TransformStack::new(); let mut transform_stack = TransformStack::new();
let mut scissor_stack = ScissorStack::new(); let mut scissor_stack = ScissorStack::new();
let model = glam::Mat4::IDENTITY;
let Some(root_widget) = params.layout.state.widgets.get(params.layout.root_widget) else { let Some(root_widget) = params.layout.state.widgets.get(params.layout.root_widget) else {
panic!(); panic!();
@@ -277,11 +292,6 @@ pub fn draw(params: &DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
panic!(); panic!();
}; };
scissor_stack.push(Boundary {
pos: Default::default(),
size: Vec2::splat(1.0e12),
});
let mut state = DrawState { let mut state = DrawState {
primitives: &mut primitives, primitives: &mut primitives,
transform_stack: &mut transform_stack, transform_stack: &mut transform_stack,
@@ -289,7 +299,7 @@ pub fn draw(params: &DrawParams) -> anyhow::Result<Vec<RenderPrimitive>> {
layout: params.layout, layout: params.layout,
}; };
draw_widget(params, &mut state, params.layout.root_node, style, root_widget, &model); draw_widget(params, &mut state, params.layout.root_node, style, root_widget);
Ok(primitives) Ok(primitives)
} }

View File

@@ -10,7 +10,7 @@ use crate::{
animation::{self, Animation}, animation::{self, Animation},
i18n::I18n, i18n::I18n,
layout::{LayoutState, WidgetID}, layout::{LayoutState, WidgetID},
stack::{Transform, TransformStack}, stack::{ScissorStack, Transform, TransformStack},
widget::{WidgetData, WidgetObj}, widget::{WidgetData, WidgetObj},
}; };
@@ -74,10 +74,10 @@ pub enum Event {
impl Event { impl Event {
fn test_transform_pos(transform: &Transform, pos: Vec2) -> bool { fn test_transform_pos(transform: &Transform, pos: Vec2) -> bool {
pos.x >= transform.pos.x pos.x >= transform.abs_pos.x
&& pos.x < transform.pos.x + transform.dim.x && pos.x < transform.abs_pos.x + transform.dim.x
&& pos.y >= transform.pos.y && pos.y >= transform.abs_pos.y
&& pos.y < transform.pos.y + transform.dim.y && pos.y < transform.abs_pos.y + transform.dim.y
} }
pub fn test_mouse_within_transform(&self, transform: &Transform) -> bool { pub fn test_mouse_within_transform(&self, transform: &Transform) -> bool {
@@ -97,6 +97,7 @@ pub struct EventAlterables {
pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>, pub style_set_requests: Vec<(taffy::NodeId, taffy::Style)>,
pub animations: Vec<animation::Animation>, pub animations: Vec<animation::Animation>,
pub transform_stack: TransformStack, pub transform_stack: TransformStack,
pub scissor_stack: ScissorStack,
pub needs_redraw: bool, pub needs_redraw: bool,
pub trigger_haptics: bool, pub trigger_haptics: bool,
} }
@@ -170,7 +171,7 @@ impl CallbackMetadata {
pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option<Vec2> { pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option<Vec2> {
let mouse_pos_abs = self.get_mouse_pos_absolute()?; let mouse_pos_abs = self.get_mouse_pos_absolute()?;
Some(mouse_pos_abs - transform_stack.get().pos) Some(mouse_pos_abs - transform_stack.get().abs_pos)
} }
} }

View File

@@ -1,21 +1,22 @@
use std::{ use std::{
cell::{RefCell, RefMut}, cell::{RefCell, RefMut},
collections::VecDeque, collections::VecDeque,
rc::Rc, rc::{Rc, Weak},
}; };
use crate::{ use crate::{
animation::Animations, animation::Animations,
components::{Component, InitData}, components::{Component, InitData},
drawing::{self, Boundary, has_overflow_clip},
event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection}, event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection},
globals::WguiGlobals, globals::WguiGlobals,
stack::Transform, stack::{self, ScissorBoundary, Transform},
widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv}, widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv},
}; };
use glam::{Vec2, vec2}; use glam::{Vec2, vec2};
use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
use taffy::{TaffyTree, TraversePartialTree}; use taffy::{NodeId, TaffyTree, TraversePartialTree};
new_key_type! { new_key_type! {
pub struct WidgetID; pub struct WidgetID;
@@ -23,6 +24,7 @@ new_key_type! {
#[derive(Clone)] #[derive(Clone)]
pub struct Widget(Rc<RefCell<WidgetState>>); pub struct Widget(Rc<RefCell<WidgetState>>);
pub struct WeakWidget(Weak<RefCell<WidgetState>>);
impl Widget { impl Widget {
pub fn new(widget_state: WidgetState) -> Self { pub fn new(widget_state: WidgetState) -> Self {
@@ -33,11 +35,21 @@ impl Widget {
RefMut::filter_map(self.0.borrow_mut(), |w| w.obj.get_as_mut::<T>()).ok() RefMut::filter_map(self.0.borrow_mut(), |w| w.obj.get_as_mut::<T>()).ok()
} }
pub fn downgrade(&self) -> WeakWidget {
WeakWidget(Rc::downgrade(&self.0))
}
pub fn state(&self) -> RefMut<'_, WidgetState> { pub fn state(&self) -> RefMut<'_, WidgetState> {
self.0.borrow_mut() self.0.borrow_mut()
} }
} }
impl WeakWidget {
pub fn upgrade(&self) -> Option<Widget> {
self.0.upgrade().map(Widget)
}
}
pub struct WidgetMap(HopSlotMap<WidgetID, Widget>); pub struct WidgetMap(HopSlotMap<WidgetID, Widget>);
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>; pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
@@ -128,9 +140,10 @@ fn add_child_internal(
parent_node: Option<taffy::NodeId>, parent_node: Option<taffy::NodeId>,
widget_state: WidgetState, widget_state: WidgetState,
style: taffy::Style, style: taffy::Style,
) -> anyhow::Result<(WidgetID, taffy::NodeId)> { ) -> anyhow::Result<(WidgetPair, taffy::NodeId)> {
#[allow(clippy::arc_with_non_send_sync)] let new_widget = Widget::new(widget_state);
let child_id = widgets.insert(Widget::new(widget_state));
let child_id = widgets.insert(new_widget.clone());
let child_node = tree.new_leaf_with_context(style, child_id)?; let child_node = tree.new_leaf_with_context(style, child_id)?;
if let Some(parent_node) = parent_node { if let Some(parent_node) = parent_node {
@@ -139,7 +152,13 @@ fn add_child_internal(
nodes.insert(child_id, child_node); nodes.insert(child_id, child_node);
Ok((child_id, child_node)) Ok((
WidgetPair {
id: child_id,
widget: new_widget,
},
child_node,
))
} }
impl Layout { impl Layout {
@@ -152,7 +171,7 @@ impl Layout {
parent_widget_id: WidgetID, parent_widget_id: WidgetID,
widget: WidgetState, widget: WidgetState,
style: taffy::Style, style: taffy::Style,
) -> anyhow::Result<(WidgetID, taffy::NodeId)> { ) -> anyhow::Result<(WidgetPair, taffy::NodeId)> {
let parent_node = *self.state.nodes.get(parent_widget_id).unwrap(); let parent_node = *self.state.nodes.get(parent_widget_id).unwrap();
self.mark_redraw(); self.mark_redraw();
@@ -255,13 +274,26 @@ impl Layout {
anyhow::bail!("invalid widget"); anyhow::bail!("invalid widget");
}; };
let transform = Transform { let mut widget = widget.0.borrow_mut();
pos: Vec2::new(l.location.x, l.location.y), let (scroll_shift, info) = match widget::get_scrollbar_info(l) {
dim: Vec2::new(l.size.width, l.size.height), Some(info) => (widget.get_scroll_shift(&info, l), Some(info)),
transform: glam::Mat4::IDENTITY, // TODO: event transformations? Not needed for now None => (Vec2::default(), None),
}; };
alterables.transform_stack.push(transform); alterables.transform_stack.push(stack::Transform {
rel_pos: Vec2::new(l.location.x, l.location.y) - scroll_shift,
transform: widget.data.transform,
dim: Vec2::new(l.size.width, l.size.height),
..Default::default()
});
// see drawing.rs too
let scissor_pushed = info.is_some() && has_overflow_clip(style);
if scissor_pushed {
let mut boundary_absolute = drawing::Boundary::construct_absolute(&alterables.transform_stack);
boundary_absolute.pos += scroll_shift;
alterables.scissor_stack.push(ScissorBoundary(boundary_absolute));
}
let mut iter_children = true; let mut iter_children = true;
@@ -275,8 +307,6 @@ impl Layout {
let listeners_vec = listeners.get(widget_id); let listeners_vec = listeners.get(widget_id);
let mut widget = widget.0.borrow_mut();
match widget.process_event(widget_id, listeners_vec, node_id, event, user_data, &mut params)? { match widget.process_event(widget_id, listeners_vec, node_id, event, user_data, &mut params)? {
widget::EventResult::Pass => { widget::EventResult::Pass => {
// go on // go on
@@ -292,6 +322,10 @@ impl Layout {
self.push_event_children(listeners, node_id, event, alterables, user_data)?; self.push_event_children(listeners, node_id, event, alterables, user_data)?;
} }
if scissor_pushed {
alterables.scissor_stack.pop();
}
alterables.transform_stack.pop(); alterables.transform_stack.pop();
Ok(()) Ok(())
@@ -361,7 +395,7 @@ impl Layout {
prev_size: Vec2::default(), prev_size: Vec2::default(),
content_size: Vec2::default(), content_size: Vec2::default(),
root_node, root_node,
root_widget, root_widget: root_widget.id,
needs_redraw: true, needs_redraw: true,
haptics_triggered: false, haptics_triggered: false,
animations: Animations::default(), animations: Animations::default(),
@@ -468,3 +502,24 @@ impl Layout {
Ok(()) Ok(())
} }
} }
impl LayoutState {
pub fn get_widget_boundary(&self, id: NodeId) -> Boundary {
let Ok(layout) = self.tree.layout(id) else {
return Boundary::default();
};
Boundary {
pos: Vec2::new(layout.location.x, layout.location.y),
size: Vec2::new(layout.size.width, layout.size.height),
}
}
pub fn get_widget_size(&self, id: NodeId) -> Vec2 {
let Ok(layout) = self.tree.layout(id) else {
return Vec2::ZERO;
};
Vec2::new(layout.size.width, layout.size.height)
}
}

View File

@@ -1,12 +1,11 @@
use crate::{ use crate::{
components::{button, Component}, components::{Component, button},
drawing::Color, drawing::Color,
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
parse_children, process_component, AttribPair, ParserContext, ParserFile, parse_children, process_component,
style::{parse_color_opt, parse_round, parse_style, parse_text_style}, style::{parse_color_opt, parse_round, parse_style, parse_text_style},
AttribPair, ParserContext, ParserFile,
}, },
widget::util::WLength, widget::util::WLength,
}; };
@@ -30,27 +29,27 @@ pub fn parse_component_button<'a, U1, U2>(
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
match key.as_ref() { match key {
"text" => { "text" => {
translation = Some(Translation::from_raw_text(&value)); translation = Some(Translation::from_raw_text(value));
} }
"translation" => { "translation" => {
translation = Some(Translation::from_translation_key(&value)); translation = Some(Translation::from_translation_key(value));
} }
"round" => { "round" => {
parse_round(&value, &mut round); parse_round(value, &mut round);
} }
"color" => { "color" => {
parse_color_opt(&value, &mut color); parse_color_opt(value, &mut color);
} }
"border_color" => { "border_color" => {
parse_color_opt(&value, &mut border_color); parse_color_opt(value, &mut border_color);
} }
"hover_color" => { "hover_color" => {
parse_color_opt(&value, &mut hover_color); parse_color_opt(value, &mut hover_color);
} }
"hover_border_color" => { "hover_border_color" => {
parse_color_opt(&value, &mut hover_border_color); parse_color_opt(value, &mut hover_border_color);
} }
_ => {} _ => {}
} }
@@ -58,7 +57,7 @@ pub fn parse_component_button<'a, U1, U2>(
let globals = ctx.layout.state.globals.clone(); let globals = ctx.layout.state.globals.clone();
let (new_id, component) = button::construct( let (widget, component) = button::construct(
&mut globals.get(), &mut globals.get(),
ctx.layout, ctx.layout,
ctx.listeners, ctx.listeners,
@@ -75,8 +74,8 @@ pub fn parse_component_button<'a, U1, U2>(
}, },
)?; )?;
process_component(ctx, Component(component), new_id, attribs); process_component(ctx, Component(component), widget.id, attribs);
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, widget.id)?;
Ok(new_id) Ok(widget.id)
} }

View File

@@ -1,11 +1,11 @@
use crate::{ use crate::{
components::{checkbox, Component}, components::{Component, checkbox},
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{parse_check_f32, parse_check_i32, process_component, style::parse_style, AttribPair, ParserContext}, parser::{AttribPair, ParserContext, parse_check_f32, parse_check_i32, process_component, style::parse_style},
}; };
pub fn parse_component_checkbox<'a, U1, U2>( pub fn parse_component_checkbox<U1, U2>(
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
@@ -35,7 +35,7 @@ pub fn parse_component_checkbox<'a, U1, U2>(
} }
} }
let (new_id, component) = checkbox::construct( let (widget, component) = checkbox::construct(
ctx.layout, ctx.layout,
ctx.listeners, ctx.listeners,
parent_id, parent_id,
@@ -47,7 +47,7 @@ pub fn parse_component_checkbox<'a, U1, U2>(
}, },
)?; )?;
process_component(ctx, Component(component), new_id, attribs); process_component(ctx, Component(component), widget.id, attribs);
Ok(new_id) Ok(widget.id)
} }

View File

@@ -1,10 +1,10 @@
use crate::{ use crate::{
components::{slider, Component}, components::{Component, slider},
layout::WidgetID, layout::WidgetID,
parser::{parse_check_f32, process_component, style::parse_style, AttribPair, ParserContext}, parser::{AttribPair, ParserContext, parse_check_f32, process_component, style::parse_style},
}; };
pub fn parse_component_slider<'a, U1, U2>( pub fn parse_component_slider<U1, U2>(
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
@@ -31,7 +31,7 @@ pub fn parse_component_slider<'a, U1, U2>(
} }
} }
let (new_id, component) = slider::construct( let (widget, component) = slider::construct(
ctx.layout, ctx.layout,
ctx.listeners, ctx.listeners,
parent_id, parent_id,
@@ -45,7 +45,7 @@ pub fn parse_component_slider<'a, U1, U2>(
}, },
)?; )?;
process_component(ctx, Component(component), new_id, attribs); process_component(ctx, Component(component), widget.id, attribs);
Ok(new_id) Ok(widget.id)
} }

View File

@@ -530,9 +530,9 @@ fn parse_widget_other_internal<U1, U2>(
Ok(()) Ok(())
} }
fn parse_widget_other<'a, U1, U2>( fn parse_widget_other<U1, U2>(
xml_tag_name: &str, xml_tag_name: &str,
file: &'a ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
attribs: &[AttribPair], attribs: &[AttribPair],
@@ -548,7 +548,7 @@ fn parse_widget_other<'a, U1, U2>(
parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id) parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id)
} }
fn parse_tag_include<'a, U1, U2>( fn parse_tag_include<U1, U2>(
file: &ParserFile, file: &ParserFile,
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
parent_id: WidgetID, parent_id: WidgetID,
@@ -641,7 +641,7 @@ fn process_attrib<'a, U1, U2>(
let name = &value[1..]; let name = &value[1..];
match ctx.get_var(name) { match ctx.get_var(name) {
Some(name) => AttribPair::new(key, name.clone()), Some(name) => AttribPair::new(key, name),
None => AttribPair::new(key, "undefined"), None => AttribPair::new(key, "undefined"),
} }
} else { } else {
@@ -655,7 +655,7 @@ fn raw_attribs<'a>(node: &'a roxmltree::Node<'a, 'a>) -> Vec<AttribPair> {
let (key, value) = (attrib.name(), attrib.value()); let (key, value) = (attrib.name(), attrib.value());
res.push(AttribPair::new(key, value)); res.push(AttribPair::new(key, value));
} }
return res; res
} }
fn process_attribs<'a, U1, U2>( fn process_attribs<'a, U1, U2>(
@@ -761,7 +761,7 @@ fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, n
ctx.insert_macro_attrib(name, MacroAttribs { attribs: macro_attribs }); ctx.insert_macro_attrib(name, MacroAttribs { attribs: macro_attribs });
} }
fn process_component<'a, U1, U2>( fn process_component<U1, U2>(
ctx: &mut ParserContext<U1, U2>, ctx: &mut ParserContext<U1, U2>,
component: Component, component: Component,
widget_id: WidgetID, widget_id: WidgetID,
@@ -782,7 +782,7 @@ fn process_component<'a, U1, U2>(
ctx.insert_component(widget_id, component, component_id); ctx.insert_component(widget_id, component, component_id);
} }
fn parse_widget_universal<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, widget_id: WidgetID, attribs: &[AttribPair]) { fn parse_widget_universal<U1, U2>(ctx: &mut ParserContext<U1, U2>, widget_id: WidgetID, attribs: &[AttribPair]) {
for pair in attribs { for pair in attribs {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match pair.attrib.as_ref() { match pair.attrib.as_ref() {
@@ -957,7 +957,7 @@ impl CustomAttribsInfo<'_> {
CustomAttribsInfoOwned { CustomAttribsInfoOwned {
parent_id: self.parent_id, parent_id: self.parent_id,
widget_id: self.widget_id, widget_id: self.widget_id,
pairs: self.pairs.iter().cloned().collect(), pairs: self.pairs.to_vec(),
} }
} }
} }

View File

@@ -6,8 +6,8 @@ use taffy::{
use crate::{ use crate::{
drawing, drawing,
parser::{ parser::{
is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, print_invalid_attrib, AttribPair, is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val,
print_invalid_value, AttribPair, print_invalid_attrib, print_invalid_value,
}, },
renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle}, renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
widget::util::WLength, widget::util::WLength,
@@ -54,7 +54,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
style.color = Some(color); style.color = Some(color);
} }
} }
"align" => match value.as_ref() { "align" => match value {
"left" => style.align = Some(HorizontalAlign::Left), "left" => style.align = Some(HorizontalAlign::Left),
"right" => style.align = Some(HorizontalAlign::Right), "right" => style.align = Some(HorizontalAlign::Right),
"center" => style.align = Some(HorizontalAlign::Center), "center" => style.align = Some(HorizontalAlign::Center),
@@ -64,7 +64,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"weight" => match value.as_ref() { "weight" => match value {
"normal" => style.weight = Some(FontWeight::Normal), "normal" => style.weight = Some(FontWeight::Normal),
"bold" => style.weight = Some(FontWeight::Bold), "bold" => style.weight = Some(FontWeight::Bold),
_ => { _ => {
@@ -111,8 +111,8 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
match key.as_ref() { match key {
"display" => match value.as_ref() { "display" => match value {
"flex" => style.display = Display::Flex, "flex" => style.display = Display::Flex,
"block" => style.display = Display::Block, "block" => style.display = Display::Block,
"grid" => style.display = Display::Grid, "grid" => style.display = Display::Grid,
@@ -176,7 +176,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
style.padding.bottom = dim; style.padding.bottom = dim;
} }
} }
"overflow" => match value.as_ref() { "overflow" => match value {
"hidden" => { "hidden" => {
style.overflow.x = Overflow::Hidden; style.overflow.x = Overflow::Hidden;
style.overflow.y = Overflow::Hidden; style.overflow.y = Overflow::Hidden;
@@ -197,7 +197,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"overflow_x" => match value.as_ref() { "overflow_x" => match value {
"hidden" => style.overflow.x = Overflow::Hidden, "hidden" => style.overflow.x = Overflow::Hidden,
"visible" => style.overflow.x = Overflow::Visible, "visible" => style.overflow.x = Overflow::Visible,
"clip" => style.overflow.x = Overflow::Clip, "clip" => style.overflow.x = Overflow::Clip,
@@ -206,7 +206,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"overflow_y" => match value.as_ref() { "overflow_y" => match value {
"hidden" => style.overflow.y = Overflow::Hidden, "hidden" => style.overflow.y = Overflow::Hidden,
"visible" => style.overflow.y = Overflow::Visible, "visible" => style.overflow.y = Overflow::Visible,
"clip" => style.overflow.y = Overflow::Clip, "clip" => style.overflow.y = Overflow::Clip,
@@ -265,21 +265,21 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
style.flex_shrink = val; style.flex_shrink = val;
} }
} }
"position" => match value.as_ref() { "position" => match value {
"absolute" => style.position = taffy::Position::Absolute, "absolute" => style.position = taffy::Position::Absolute,
"relative" => style.position = taffy::Position::Relative, "relative" => style.position = taffy::Position::Relative,
_ => { _ => {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"box_sizing" => match value.as_ref() { "box_sizing" => match value {
"border_box" => style.box_sizing = BoxSizing::BorderBox, "border_box" => style.box_sizing = BoxSizing::BorderBox,
"content_box" => style.box_sizing = BoxSizing::ContentBox, "content_box" => style.box_sizing = BoxSizing::ContentBox,
_ => { _ => {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"align_self" => match value.as_ref() { "align_self" => match value {
"baseline" => style.align_self = Some(AlignSelf::Baseline), "baseline" => style.align_self = Some(AlignSelf::Baseline),
"center" => style.align_self = Some(AlignSelf::Center), "center" => style.align_self = Some(AlignSelf::Center),
"end" => style.align_self = Some(AlignSelf::End), "end" => style.align_self = Some(AlignSelf::End),
@@ -291,7 +291,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"justify_self" => match value.as_ref() { "justify_self" => match value {
"center" => style.justify_self = Some(JustifySelf::Center), "center" => style.justify_self = Some(JustifySelf::Center),
"end" => style.justify_self = Some(JustifySelf::End), "end" => style.justify_self = Some(JustifySelf::End),
"flex_end" => style.justify_self = Some(JustifySelf::FlexEnd), "flex_end" => style.justify_self = Some(JustifySelf::FlexEnd),
@@ -302,7 +302,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"align_items" => match value.as_ref() { "align_items" => match value {
"baseline" => style.align_items = Some(AlignItems::Baseline), "baseline" => style.align_items = Some(AlignItems::Baseline),
"center" => style.align_items = Some(AlignItems::Center), "center" => style.align_items = Some(AlignItems::Center),
"end" => style.align_items = Some(AlignItems::End), "end" => style.align_items = Some(AlignItems::End),
@@ -314,7 +314,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"align_content" => match value.as_ref() { "align_content" => match value {
"center" => style.align_content = Some(AlignContent::Center), "center" => style.align_content = Some(AlignContent::Center),
"end" => style.align_content = Some(AlignContent::End), "end" => style.align_content = Some(AlignContent::End),
"flex_end" => style.align_content = Some(AlignContent::FlexEnd), "flex_end" => style.align_content = Some(AlignContent::FlexEnd),
@@ -328,7 +328,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"justify_content" => match value.as_ref() { "justify_content" => match value {
"center" => style.justify_content = Some(JustifyContent::Center), "center" => style.justify_content = Some(JustifyContent::Center),
"end" => style.justify_content = Some(JustifyContent::End), "end" => style.justify_content = Some(JustifyContent::End),
"flex_end" => style.justify_content = Some(JustifyContent::FlexEnd), "flex_end" => style.justify_content = Some(JustifyContent::FlexEnd),
@@ -342,13 +342,13 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style {
print_invalid_attrib(key, value); print_invalid_attrib(key, value);
} }
}, },
"flex_wrap" => match value.as_ref() { "flex_wrap" => match value {
"wrap" => style.flex_wrap = FlexWrap::Wrap, "wrap" => style.flex_wrap = FlexWrap::Wrap,
"no_wrap" => style.flex_wrap = FlexWrap::NoWrap, "no_wrap" => style.flex_wrap = FlexWrap::NoWrap,
"wrap_reverse" => style.flex_wrap = FlexWrap::WrapReverse, "wrap_reverse" => style.flex_wrap = FlexWrap::WrapReverse,
_ => {} _ => {}
}, },
"flex_direction" => match value.as_ref() { "flex_direction" => match value {
"column_reverse" => style.flex_direction = FlexDirection::ColumnReverse, "column_reverse" => style.flex_direction = FlexDirection::ColumnReverse,
"column" => style.flex_direction = FlexDirection::Column, "column" => style.flex_direction = FlexDirection::Column,
"row_reverse" => style.flex_direction = FlexDirection::RowReverse, "row_reverse" => style.flex_direction = FlexDirection::RowReverse,

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::WidgetID, layout::WidgetID,
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile}, parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
widget::div::WidgetDiv, widget::div::WidgetDiv,
}; };
@@ -13,10 +13,10 @@ pub fn parse_widget_div<'a, U1, U2>(
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let style = parse_style(attribs); let style = parse_style(attribs);
let (new_id, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?; let (widget, _) = ctx.layout.add_child(parent_id, WidgetDiv::create(), style)?;
parse_widget_universal(ctx, new_id, attribs); parse_widget_universal(ctx, widget.id, attribs);
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, widget.id)?;
Ok(new_id) Ok(widget.id)
} }

View File

@@ -2,9 +2,8 @@ use crate::{
i18n::Translation, i18n::Translation,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
parse_children, parse_widget_universal, AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal,
style::{parse_style, parse_text_style}, style::{parse_style, parse_text_style},
AttribPair, ParserContext, ParserFile,
}, },
widget::label::{WidgetLabel, WidgetLabelParams}, widget::label::{WidgetLabel, WidgetLabelParams},
}; };
@@ -40,12 +39,12 @@ pub fn parse_widget_label<'a, U1, U2>(
let globals = ctx.layout.state.globals.clone(); let globals = ctx.layout.state.globals.clone();
let (new_id, _) = ctx let (widget, _) = ctx
.layout .layout
.add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?; .add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?;
parse_widget_universal(ctx, new_id, attribs); parse_widget_universal(ctx, widget.id, attribs);
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, widget.id)?;
Ok(new_id) Ok(widget.id)
} }

View File

@@ -2,9 +2,8 @@ use crate::{
drawing::GradientMode, drawing::GradientMode,
layout::WidgetID, layout::WidgetID,
parser::{ parser::{
parse_children, parse_widget_universal, print_invalid_attrib, AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, print_invalid_attrib,
style::{parse_color, parse_round, parse_style}, style::{parse_color, parse_round, parse_style},
AttribPair, ParserContext, ParserFile,
}, },
widget::rectangle::{WidgetRectangle, WidgetRectangleParams}, widget::rectangle::{WidgetRectangle, WidgetRectangleParams},
}; };
@@ -17,7 +16,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetRectangleParams::default(); let mut params = WidgetRectangleParams::default();
let style = parse_style(&attribs); let style = parse_style(attribs);
for pair in attribs { for pair in attribs {
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
@@ -56,12 +55,12 @@ pub fn parse_widget_rectangle<'a, U1, U2>(
} }
} }
let (new_id, _) = ctx let (widget, _) = ctx
.layout .layout
.add_child(parent_id, WidgetRectangle::create(params), style)?; .add_child(parent_id, WidgetRectangle::create(params), style)?;
parse_widget_universal(ctx, new_id, attribs); parse_widget_universal(ctx, widget.id, attribs);
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, widget.id)?;
Ok(new_id) Ok(widget.id)
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
layout::WidgetID, layout::WidgetID,
parser::{parse_children, parse_widget_universal, style::parse_style, AttribPair, ParserContext, ParserFile}, parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData}, renderer_vk::text::custom_glyph::{CustomGlyphContent, CustomGlyphData},
widget::sprite::{WidgetSprite, WidgetSpriteParams}, widget::sprite::{WidgetSprite, WidgetSpriteParams},
}; };
@@ -15,7 +15,7 @@ pub fn parse_widget_sprite<'a, U1, U2>(
attribs: &[AttribPair], attribs: &[AttribPair],
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut params = WidgetSpriteParams::default(); let mut params = WidgetSpriteParams::default();
let style = parse_style(&attribs); let style = parse_style(attribs);
let mut glyph = None; let mut glyph = None;
for pair in attribs { for pair in attribs {
@@ -54,10 +54,10 @@ pub fn parse_widget_sprite<'a, U1, U2>(
log::warn!("No source for sprite node!"); log::warn!("No source for sprite node!");
} }
let (new_id, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?; let (widget, _) = ctx.layout.add_child(parent_id, WidgetSprite::create(params), style)?;
parse_widget_universal(ctx, new_id, attribs); parse_widget_universal(ctx, widget.id, attribs);
parse_children(file, ctx, node, new_id)?; parse_children(file, ctx, node, widget.id)?;
Ok(new_id) Ok(widget.id)
} }

View File

@@ -2,20 +2,20 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};
use cosmic_text::Buffer; use cosmic_text::Buffer;
use glam::{Mat4, Vec2, Vec3}; use glam::{Mat4, Vec2, Vec3};
use slotmap::{new_key_type, SlotMap}; use slotmap::{SlotMap, new_key_type};
use vulkano::pipeline::graphics::viewport; use vulkano::pipeline::graphics::viewport;
use crate::{ use crate::{
drawing::{self}, drawing::{self},
gfx::{cmd::GfxCommandBuffer, WGfx}, gfx::{WGfx, cmd::GfxCommandBuffer},
}; };
use super::{ use super::{
rect::{RectPipeline, RectRenderer}, rect::{RectPipeline, RectRenderer},
text::{ text::{
DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, TextArea, TextBounds,
text_atlas::{TextAtlas, TextPipeline}, text_atlas::{TextAtlas, TextPipeline},
text_renderer::TextRenderer, text_renderer::TextRenderer,
TextArea, TextBounds, DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE,
}, },
viewport::Viewport, viewport::Viewport,
}; };
@@ -287,12 +287,8 @@ impl Context {
transform: extent.transform, transform: extent.transform,
}); });
} }
drawing::RenderPrimitive::ScissorEnable(boundary) => { drawing::RenderPrimitive::ScissorSet(boundary) => {
next_scissor = Some(*boundary); next_scissor = Some(boundary.0);
needs_new_pass = true;
}
drawing::RenderPrimitive::ScissorDisable => {
next_scissor = None;
needs_new_pass = true; needs_new_pass = true;
} }
} }

View File

@@ -1,4 +1,4 @@
use glam::Vec2; use glam::{Mat4, Vec2, Vec3};
use crate::drawing; use crate::drawing;
@@ -50,18 +50,40 @@ impl<T: StackItem<T>, const STACK_MAX: usize> Default for GenericStack<T, STACK_
// Transform stack // Transform stack
// ######################################## // ########################################
#[derive(Default, Copy, Clone)] #[derive(Copy, Clone)]
pub struct Transform { pub struct Transform {
pub pos: Vec2, pub rel_pos: Vec2,
pub transform: glam::Mat4,
pub dim: Vec2, // for convenience pub dim: Vec2, // for convenience
pub abs_pos: Vec2, // for convenience, will be set after pushing
pub transform: glam::Mat4,
pub transform_rel: glam::Mat4,
}
impl Default for Transform {
fn default() -> Self {
Self {
abs_pos: Default::default(),
rel_pos: Default::default(),
dim: Default::default(),
transform: Mat4::IDENTITY,
transform_rel: Default::default(),
}
}
} }
impl<T> StackItem<T> for Transform where Transform: Pushable<T> {} impl<T> StackItem<T> for Transform where Transform: Pushable<T> {}
impl Pushable<Transform> for Transform { impl Pushable<Transform> for Transform {
fn push(&mut self, upper: &Transform) { fn push(&mut self, upper: &Transform) {
self.pos += upper.pos; // fixme: there is definitely a better way to do these operations
let translation_matrix = Mat4::from_translation(Vec3::new(self.rel_pos.x, self.rel_pos.y, 0.0));
self.abs_pos = upper.abs_pos + self.rel_pos;
let absolute_shift_matrix = Mat4::from_translation(Vec3::new(self.abs_pos.x, self.abs_pos.y, 0.0));
let absolute_shift_matrix_neg = Mat4::from_translation(Vec3::new(-self.abs_pos.x, -self.abs_pos.y, 0.0));
self.transform =
(absolute_shift_matrix * self.transform * absolute_shift_matrix_neg) * upper.transform * translation_matrix;
} }
} }
@@ -71,12 +93,26 @@ pub type TransformStack = GenericStack<Transform, 64>;
// Scissor stack // Scissor stack
// ######################################## // ########################################
impl<T> StackItem<T> for drawing::Boundary where drawing::Boundary: Pushable<T> {} #[derive(Copy, Clone)]
pub struct ScissorBoundary(pub drawing::Boundary);
impl Pushable<drawing::Boundary> for drawing::Boundary { impl Default for ScissorBoundary {
fn push(&mut self, upper: &drawing::Boundary) { fn default() -> Self {
let mut display_pos = self.pos; Self(drawing::Boundary {
let mut display_size = self.size; pos: Default::default(),
size: Vec2::splat(1.0e12),
})
}
}
impl<T> StackItem<T> for ScissorBoundary where ScissorBoundary: Pushable<T> {}
impl Pushable<ScissorBoundary> for ScissorBoundary {
fn push(&mut self, upper: &ScissorBoundary) {
let mut display_pos = self.0.pos;
let mut display_size = self.0.size;
let upper = &upper.0;
// limit in x-coord // limit in x-coord
if display_pos.x < upper.left() { if display_pos.x < upper.left() {
@@ -100,9 +136,9 @@ impl Pushable<drawing::Boundary> for drawing::Boundary {
display_size.y = upper.bottom() - display_pos.y; display_size.y = upper.bottom() - display_pos.y;
} }
self.pos = display_pos; self.0.pos = display_pos;
self.size = display_size; self.0.size = display_size;
} }
} }
pub type ScissorStack = GenericStack<drawing::Boundary, 64>; pub type ScissorStack = GenericStack<ScissorBoundary, 64>;

View File

@@ -10,7 +10,7 @@ use crate::{
globals::Globals, globals::Globals,
i18n::{I18n, Translation}, i18n::{I18n, Translation},
layout::WidgetID, layout::WidgetID,
renderer_vk::text::{TextStyle, FONT_SYSTEM}, renderer_vk::text::{FONT_SYSTEM, TextStyle},
}; };
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
@@ -109,7 +109,7 @@ impl WidgetLabel {
impl WidgetObj for WidgetLabel { impl WidgetObj for WidgetLabel {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct_relative(state.transform_stack);
if self.last_boundary != boundary { if self.last_boundary != boundary {
self.last_boundary = boundary; self.last_boundary = boundary;

View File

@@ -11,6 +11,7 @@ use crate::{
}, },
layout::{Layout, LayoutState, WidgetID}, layout::{Layout, LayoutState, WidgetID},
stack::{ScissorStack, TransformStack}, stack::{ScissorStack, TransformStack},
widget,
}; };
pub mod div; pub mod div;
@@ -248,8 +249,8 @@ impl WidgetState {
PrimitiveExtent { PrimitiveExtent {
boundary: drawing::Boundary::from_pos_size( boundary: drawing::Boundary::from_pos_size(
Vec2::new( Vec2::new(
transform.pos.x + transform.dim.x * (1.0 - info.handle_size.x) * self.data.scrolling.x, transform.abs_pos.x + transform.dim.x * (1.0 - info.handle_size.x) * self.data.scrolling.x,
transform.pos.y + transform.dim.y - thickness - margin, transform.abs_pos.y + transform.dim.y - thickness - margin,
), ),
Vec2::new(transform.dim.x * info.handle_size.x, thickness), Vec2::new(transform.dim.x * info.handle_size.x, thickness),
), ),
@@ -265,8 +266,8 @@ impl WidgetState {
PrimitiveExtent { PrimitiveExtent {
boundary: drawing::Boundary::from_pos_size( boundary: drawing::Boundary::from_pos_size(
Vec2::new( Vec2::new(
transform.pos.x + transform.dim.x - thickness - margin, transform.abs_pos.x + transform.dim.x - thickness - margin,
transform.pos.y + transform.dim.y * (1.0 - info.handle_size.y) * self.data.scrolling.y, transform.abs_pos.y + transform.dim.y * (1.0 - info.handle_size.y) * self.data.scrolling.y,
), ),
Vec2::new(thickness, transform.dim.y * info.handle_size.y), Vec2::new(thickness, transform.dim.y * info.handle_size.y),
), ),

View File

@@ -36,7 +36,7 @@ impl WidgetRectangle {
impl WidgetObj for WidgetRectangle { impl WidgetObj for WidgetRectangle {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct_relative(state.transform_stack);
let round_units = match self.params.round { let round_units = match self.params.round {
WLength::Units(units) => units as u8, WLength::Units(units) => units as u8,

View File

@@ -7,8 +7,8 @@ use crate::{
drawing::{self, PrimitiveExtent}, drawing::{self, PrimitiveExtent},
layout::WidgetID, layout::WidgetID,
renderer_vk::text::{ renderer_vk::text::{
custom_glyph::{CustomGlyph, CustomGlyphData},
DEFAULT_METRICS, FONT_SYSTEM, DEFAULT_METRICS, FONT_SYSTEM,
custom_glyph::{CustomGlyph, CustomGlyphData},
}, },
}; };
@@ -37,7 +37,7 @@ impl WidgetSprite {
impl WidgetObj for WidgetSprite { impl WidgetObj for WidgetSprite {
fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) { fn draw(&mut self, state: &mut super::DrawState, _params: &super::DrawParams) {
let boundary = drawing::Boundary::construct(state.transform_stack); let boundary = drawing::Boundary::construct_relative(state.transform_stack);
if let Some(glyph_data) = self.params.glyph_data.as_ref() { if let Some(glyph_data) = self.params.glyph_data.as_ref() {
let glyph = CustomGlyph { let glyph = CustomGlyph {