diff --git a/dash-frontend/assets/gui/tab/apps.xml b/dash-frontend/assets/gui/tab/apps.xml index d477a3c..0f36747 100644 --- a/dash-frontend/assets/gui/tab/apps.xml +++ b/dash-frontend/assets/gui/tab/apps.xml @@ -36,6 +36,7 @@ flex_wrap="wrap" justify_content="stretch" gap="4" + overflow_y="scroll" /> \ No newline at end of file diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs index ea56f1d..22d1744 100644 --- a/dash-frontend/src/tab/apps.rs +++ b/dash-frontend/src/tab/apps.rs @@ -1,10 +1,9 @@ use std::{collections::HashMap, rc::Rc}; use wgui::{ - components::{self, button::ComponentButton}, + components::button::ComponentButton, layout::WidgetPair, parser::{Fetchable, ParseDocumentParams, ParserData, ParserState}, - taffy::{self, Dimension, prelude::length}, }; use crate::{ @@ -16,7 +15,9 @@ pub struct TabApps { #[allow(dead_code)] pub state: ParserState, + #[allow(dead_code)] entries: Vec, + #[allow(dead_code)] app_list: AppList, } @@ -105,7 +106,7 @@ impl AppList { )?; let button = data.fetch_component_as::("button")?; - button.on_click(Box::new(move |common, evt| { + button.on_click(Box::new(move |_common, _evt| { log::info!("click"); Ok(()) })); diff --git a/wgui/src/animation.rs b/wgui/src/animation.rs index 8a057ec..10ef809 100644 --- a/wgui/src/animation.rs +++ b/wgui/src/animation.rs @@ -1,6 +1,7 @@ -use glam::{FloatExt, Vec2}; +use glam::FloatExt; use crate::{ + drawing::Boundary, event::{CallbackDataCommon, EventAlterables}, layout::{LayoutState, WidgetID}, widget::{WidgetData, WidgetObj}, @@ -46,7 +47,7 @@ pub struct CallbackData<'a> { pub obj: &'a mut dyn WidgetObj, pub data: &'a mut WidgetData, 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) } @@ -98,14 +99,13 @@ impl Animation { }; 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 (data, obj) = widget_state.get_data_obj_mut(); let data = &mut CallbackData { widget_id: self.target_widget, - widget_size: Vec2::new(layout.size.width, layout.size.height), + widget_boundary: state.get_widget_boundary(widget_node), obj, data, pos, diff --git a/wgui/src/components/button.rs b/wgui/src/components/button.rs index 6a3939f..b8d52b8 100644 --- a/wgui/src/components/button.rs +++ b/wgui/src/components/button.rs @@ -1,21 +1,25 @@ -use std::{cell::RefCell, rc::Rc}; -use taffy::{AlignItems, JustifyContent}; - use crate::{ animation::{Animation, AnimationEasing}, components::{Component, ComponentBase, ComponentTrait, InitData}, - drawing::{self, Color}, + drawing::{self, Boundary, Color}, event::{CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, globals::Globals, i18n::Translation, - layout::{Layout, WidgetID}, - renderer_vk::text::{FontWeight, TextStyle}, + layout::{Layout, WidgetID, WidgetPair}, + renderer_vk::{ + text::{FontWeight, TextStyle}, + util::centered_matrix, + }, widget::{ + WidgetData, label::{WidgetLabel, WidgetLabelParams}, rectangle::{WidgetRectangle, WidgetRectangleParams}, util::WLength, }, }; +use glam::{Mat4, Vec3}; +use std::{cell::RefCell, rc::Rc}; +use taffy::{AlignItems, JustifyContent}; pub struct Params { pub text: Option, // 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) } -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 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.color2 = get_color2(&bgcolor); rect.params.border_color = data.initial_border_color.lerp(&data.initial_hover_border_color, mult); rect.params.border = 2.0; } -fn anim_hover_out(data: Rc, state: Rc>, widget_id: WidgetID) -> Animation { +fn anim_hover_create(data: Rc, state: Rc>, widget_id: WidgetID, fade_in: bool) -> Animation { Animation::new( widget_id, - 15, + if fade_in { 5 } else { 10 }, AnimationEasing::OutCubic, Box::new(move |common, anim_data| { let rect = anim_data.obj.get_as_mut::().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(); }), ) @@ -129,10 +152,13 @@ fn register_event_mouse_enter( Box::new(move |common, event_data, _, _| { common.alterables.trigger_haptics(); common.alterables.mark_redraw(); - let rect = event_data.obj.get_as_mut::().unwrap(); - let mut state = state.borrow_mut(); - anim_hover(rect, &data, 1.0, state.down); - state.hovered = true; + common.alterables.animate(anim_hover_create( + data.clone(), + state.clone(), + event_data.widget_id, + true, + )); + state.borrow_mut().hovered = true; Ok(()) }), ); @@ -150,9 +176,12 @@ fn register_event_mouse_leave( EventListenerKind::MouseLeave, Box::new(move |common, event_data, _, _| { common.alterables.trigger_haptics(); - common - .alterables - .animate(anim_hover_out(data.clone(), state.clone(), event_data.widget_id)); + common.alterables.animate(anim_hover_create( + data.clone(), + state.clone(), + event_data.widget_id, + false, + )); state.borrow_mut().hovered = false; Ok(()) }), @@ -173,7 +202,14 @@ fn register_event_mouse_press( let mut state = state.borrow_mut(); let rect = event_data.obj.get_as_mut::().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 { state.down = true; @@ -199,7 +235,14 @@ fn register_event_mouse_release( EventListenerKind::MouseRelease, Box::new(move |common, event_data, _, _| { let rect = event_data.obj.get_as_mut::().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(); if state.down { @@ -226,7 +269,7 @@ pub fn construct( listeners: &mut EventListenerCollection, parent: WidgetID, params: Params, -) -> anyhow::Result<(WidgetID, Rc)> { +) -> anyhow::Result<(WidgetPair, Rc)> { let mut style = params.style; // force-override style @@ -260,7 +303,7 @@ pub fn construct( 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, WidgetRectangle::create(WidgetRectangleParams { color, @@ -272,12 +315,13 @@ pub fn construct( }), style, )?; - let id_rect = id_root; + + let id_rect = root.id; let light_text = (color.r + color.g + color.b) < 1.5; 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, WidgetLabel::create( globals, @@ -296,7 +340,7 @@ pub fn construct( ), Default::default(), )?; - id_label + label.id } else { WidgetID::default() }; @@ -326,5 +370,5 @@ pub fn construct( let button = Rc::new(ComponentButton { base, data, state }); layout.defer_component_init(Component(button.clone())); - Ok((id_root, button)) + Ok((root, button)) } diff --git a/wgui/src/components/checkbox.rs b/wgui/src/components/checkbox.rs index f7bf828..5b4f9b7 100644 --- a/wgui/src/components/checkbox.rs +++ b/wgui/src/components/checkbox.rs @@ -10,7 +10,7 @@ use crate::{ drawing::Color, event::{CallbackDataCommon, EventAlterables, EventListenerCollection, EventListenerKind, ListenerHandleVec}, i18n::Translation, - layout::{self, Layout, LayoutState, WidgetID}, + layout::{self, Layout, LayoutState, WidgetID, WidgetPair}, renderer_vk::text::{FontWeight, TextStyle}, widget::{ label::{WidgetLabel, WidgetLabelParams}, @@ -249,7 +249,7 @@ pub fn construct( listeners: &mut EventListenerCollection, parent: WidgetID, params: Params, -) -> anyhow::Result<(WidgetID, Rc)> { +) -> anyhow::Result<(WidgetPair, Rc)> { let mut style = params.style; // force-override style @@ -267,7 +267,7 @@ pub fn construct( let globals = layout.state.globals.clone(); - let (id_root, _) = layout.add_child( + let (root, _) = layout.add_child( parent, WidgetRectangle::create(WidgetRectangleParams { color: Color::new(1.0, 1.0, 1.0, 0.0), @@ -277,14 +277,15 @@ pub fn construct( }), style, )?; - let id_container = id_root; + + let id_container = root.id; let box_size = taffy::Size { width: length(params.box_size), height: length(params.box_size), }; - let (id_outer_box, _) = layout.add_child( + let (outer_box, _) = layout.add_child( id_container, WidgetRectangle::create(WidgetRectangleParams { border: 2.0, @@ -302,8 +303,8 @@ pub fn construct( }, )?; - let (id_inner_box, _) = layout.add_child( - id_outer_box, + let (inner_box, _) = layout.add_child( + outer_box.id, WidgetRectangle::create(WidgetRectangleParams { round: WLength::Units(5.0), color: if params.checked { COLOR_CHECKED } else { COLOR_UNCHECKED }, @@ -318,7 +319,7 @@ pub fn construct( }, )?; - let (id_label, _node_label) = layout.add_child( + let (label, _node_label) = layout.add_child( id_container, WidgetLabel::create( &mut globals.get(), @@ -335,8 +336,8 @@ pub fn construct( let data = Rc::new(Data { id_container, - id_inner_box, - id_label, + id_inner_box: inner_box.id, + id_label: label.id, }); let state = Rc::new(RefCell::new(State { @@ -356,5 +357,5 @@ pub fn construct( let checkbox = Rc::new(ComponentCheckbox { base, data, state }); layout.defer_component_init(Component(checkbox.clone())); - Ok((id_root, checkbox)) + Ok((root, checkbox)) } diff --git a/wgui/src/components/slider.rs b/wgui/src/components/slider.rs index 4951a9f..fee9930 100644 --- a/wgui/src/components/slider.rs +++ b/wgui/src/components/slider.rs @@ -9,7 +9,7 @@ use crate::{ drawing::{self}, event::{self, CallbackDataCommon, EventListenerCollection, EventListenerKind, ListenerHandleVec}, i18n::Translation, - layout::{Layout, WidgetID}, + layout::{Layout, WidgetID, WidgetPair}, renderer_vk::{ text::{FontWeight, HorizontalAlign, TextStyle}, util, @@ -175,7 +175,7 @@ fn on_enter_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) { AnimationEasing::OutBack, Box::new(move |common, data| { let rect = data.obj.get_as_mut::().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); common.alterables.mark_redraw(); }), @@ -189,7 +189,7 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID) { AnimationEasing::OutQuad, Box::new(move |common, data| { let rect = data.obj.get_as_mut::().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); common.alterables.mark_redraw(); }), @@ -308,14 +308,14 @@ pub fn construct( listeners: &mut EventListenerCollection, parent: WidgetID, params: Params, -) -> anyhow::Result<(WidgetID, Rc)> { +) -> anyhow::Result<(WidgetPair, Rc)> { let mut style = params.style; style.position = taffy::Position::Relative; style.min_size = style.size; style.max_size = style.size; - let (root_id, slider_body_node) = layout.add_child(parent, WidgetDiv::create(), style)?; - let body_id = root_id; + let (root, slider_body_node) = layout.add_child(parent, WidgetDiv::create(), style)?; + let body_id = root.id; let (_background_id, _) = layout.add_child( body_id, @@ -350,10 +350,10 @@ pub fn construct( }; // 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( - slider_handle_id, + let (slider_handle_rect, _) = layout.add_child( + slider_handle.id, WidgetRectangle::create(WidgetRectangleParams { color: HANDLE_COLOR, border_color: HANDLE_BORDER_COLOR, @@ -379,8 +379,8 @@ pub fn construct( let globals = layout.state.globals.clone(); - let (slider_text_id, _) = layout.add_child( - slider_handle_id, + let (slider_text, _) = layout.add_child( + slider_handle.id, WidgetLabel::create( &mut globals.get(), WidgetLabelParams { @@ -399,9 +399,9 @@ pub fn construct( let data = Rc::new(Data { body: body_id, slider_handle_node, - slider_handle_rect_id, + slider_handle_rect_id: slider_handle_rect.id, slider_body_node, - slider_text_id, + slider_text_id: slider_text.id, }); let state = Rc::new(RefCell::new(state)); @@ -418,5 +418,5 @@ pub fn construct( let slider = Rc::new(ComponentSlider { base, data, state }); layout.defer_component_init(Component(slider.clone())); - Ok((root_id, slider)) + Ok((root, slider)) } diff --git a/wgui/src/drawing.rs b/wgui/src/drawing.rs index fc15dc8..0ee45e2 100644 --- a/wgui/src/drawing.rs +++ b/wgui/src/drawing.rs @@ -7,8 +7,8 @@ use taffy::TraversePartialTree; use crate::{ drawing, layout::Widget, - renderer_vk::text::{custom_glyph::CustomGlyph, TextShadow}, - stack::{self, ScissorStack, TransformStack}, + renderer_vk::text::{TextShadow, custom_glyph::CustomGlyph}, + stack::{self, ScissorBoundary, ScissorStack, TransformStack}, widget::{self}, }; @@ -29,11 +29,22 @@ impl Boundary { 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(); 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), } } @@ -137,8 +148,7 @@ pub enum RenderPrimitive { Rectangle(PrimitiveExtent, Rectangle), Text(PrimitiveExtent, Rc>, Option), Sprite(PrimitiveExtent, Option), //option because we want as_slice - ScissorEnable(Boundary), - ScissorDisable, + ScissorSet(ScissorBoundary), } pub struct DrawParams<'a> { @@ -146,7 +156,7 @@ pub struct DrawParams<'a> { 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 } @@ -171,7 +181,6 @@ fn draw_widget( node_id: taffy::NodeId, style: &taffy::Style, widget: &Widget, - parent_transform: &glam::Mat4, ) { let Ok(l) = params.layout.state.tree.layout(node_id) else { debug_assert!(false); @@ -180,24 +189,23 @@ fn draw_widget( let mut widget_state = widget.state(); - let transform = widget_state.data.transform * *parent_transform; - - let (shift, info) = match widget::get_scrollbar_info(l) { + let (scroll_shift, info) = match widget::get_scrollbar_info(l) { Some(info) => (widget_state.get_scroll_shift(&info, l), Some(info)), None => (Vec2::default(), None), }; state.transform_stack.push(stack::Transform { - pos: Vec2::new(l.location.x, l.location.y) - shift, - transform, + rel_pos: Vec2::new(l.location.x, l.location.y) - scroll_shift, + transform: widget_state.data.transform, dim: Vec2::new(l.size.width, l.size.height), + ..Default::default() }); 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( &boundary, - &transform, + &state.transform_stack.get().transform, 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); if scissor_pushed { - let boundary = drawing::Boundary::construct(state.transform_stack); - state.scissor_stack.push(boundary); + let mut boundary_absolute = drawing::Boundary::construct_absolute(state.transform_stack); + boundary_absolute.pos += scroll_shift; + state.scissor_stack.push(ScissorBoundary(boundary_absolute)); + 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( - &boundary, - &transform, + &boundary_relative, + &state.transform_stack.get().transform, 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 { @@ -226,11 +240,13 @@ fn draw_widget( widget_state.draw_all(state, &draw_params); - draw_children(params, state, node_id, &transform); + draw_children(params, state, node_id); if scissor_pushed { - state.primitives.push(drawing::RenderPrimitive::ScissorDisable); state.scissor_stack.pop(); + state + .primitives + .push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get())); } 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 = ¶ms.layout; 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; }; - 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> { let mut primitives = Vec::::new(); let mut transform_stack = TransformStack::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 { panic!(); @@ -277,11 +292,6 @@ pub fn draw(params: &DrawParams) -> anyhow::Result> { panic!(); }; - scissor_stack.push(Boundary { - pos: Default::default(), - size: Vec2::splat(1.0e12), - }); - let mut state = DrawState { primitives: &mut primitives, transform_stack: &mut transform_stack, @@ -289,7 +299,7 @@ pub fn draw(params: &DrawParams) -> anyhow::Result> { 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) } diff --git a/wgui/src/event.rs b/wgui/src/event.rs index 6e90b9a..64944a3 100644 --- a/wgui/src/event.rs +++ b/wgui/src/event.rs @@ -10,7 +10,7 @@ use crate::{ animation::{self, Animation}, i18n::I18n, layout::{LayoutState, WidgetID}, - stack::{Transform, TransformStack}, + stack::{ScissorStack, Transform, TransformStack}, widget::{WidgetData, WidgetObj}, }; @@ -74,10 +74,10 @@ pub enum Event { impl Event { fn test_transform_pos(transform: &Transform, pos: Vec2) -> bool { - pos.x >= transform.pos.x - && pos.x < transform.pos.x + transform.dim.x - && pos.y >= transform.pos.y - && pos.y < transform.pos.y + transform.dim.y + pos.x >= transform.abs_pos.x + && pos.x < transform.abs_pos.x + transform.dim.x + && pos.y >= transform.abs_pos.y + && pos.y < transform.abs_pos.y + transform.dim.y } 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 animations: Vec, pub transform_stack: TransformStack, + pub scissor_stack: ScissorStack, pub needs_redraw: bool, pub trigger_haptics: bool, } @@ -170,7 +171,7 @@ impl CallbackMetadata { pub fn get_mouse_pos_relative(&self, transform_stack: &TransformStack) -> Option { 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) } } diff --git a/wgui/src/layout.rs b/wgui/src/layout.rs index c34b29a..42b7126 100644 --- a/wgui/src/layout.rs +++ b/wgui/src/layout.rs @@ -1,21 +1,22 @@ use std::{ cell::{RefCell, RefMut}, collections::VecDeque, - rc::Rc, + rc::{Rc, Weak}, }; use crate::{ animation::Animations, components::{Component, InitData}, + drawing::{self, Boundary, has_overflow_clip}, event::{self, CallbackDataCommon, EventAlterables, EventListenerCollection}, globals::WguiGlobals, - stack::Transform, + stack::{self, ScissorBoundary, Transform}, widget::{self, EventParams, WidgetObj, WidgetState, div::WidgetDiv}, }; use glam::{Vec2, vec2}; use slotmap::{HopSlotMap, SecondaryMap, new_key_type}; -use taffy::{TaffyTree, TraversePartialTree}; +use taffy::{NodeId, TaffyTree, TraversePartialTree}; new_key_type! { pub struct WidgetID; @@ -23,6 +24,7 @@ new_key_type! { #[derive(Clone)] pub struct Widget(Rc>); +pub struct WeakWidget(Weak>); impl Widget { 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::()).ok() } + pub fn downgrade(&self) -> WeakWidget { + WeakWidget(Rc::downgrade(&self.0)) + } + pub fn state(&self) -> RefMut<'_, WidgetState> { self.0.borrow_mut() } } +impl WeakWidget { + pub fn upgrade(&self) -> Option { + self.0.upgrade().map(Widget) + } +} + pub struct WidgetMap(HopSlotMap); pub type WidgetNodeMap = SecondaryMap; @@ -128,9 +140,10 @@ fn add_child_internal( parent_node: Option, widget_state: WidgetState, style: taffy::Style, -) -> anyhow::Result<(WidgetID, taffy::NodeId)> { - #[allow(clippy::arc_with_non_send_sync)] - let child_id = widgets.insert(Widget::new(widget_state)); +) -> anyhow::Result<(WidgetPair, taffy::NodeId)> { + let new_widget = Widget::new(widget_state); + + let child_id = widgets.insert(new_widget.clone()); let child_node = tree.new_leaf_with_context(style, child_id)?; if let Some(parent_node) = parent_node { @@ -139,7 +152,13 @@ fn add_child_internal( nodes.insert(child_id, child_node); - Ok((child_id, child_node)) + Ok(( + WidgetPair { + id: child_id, + widget: new_widget, + }, + child_node, + )) } impl Layout { @@ -152,7 +171,7 @@ impl Layout { parent_widget_id: WidgetID, widget: WidgetState, style: taffy::Style, - ) -> anyhow::Result<(WidgetID, taffy::NodeId)> { + ) -> anyhow::Result<(WidgetPair, taffy::NodeId)> { let parent_node = *self.state.nodes.get(parent_widget_id).unwrap(); self.mark_redraw(); @@ -255,13 +274,26 @@ impl Layout { anyhow::bail!("invalid widget"); }; - let transform = Transform { - pos: Vec2::new(l.location.x, l.location.y), - dim: Vec2::new(l.size.width, l.size.height), - transform: glam::Mat4::IDENTITY, // TODO: event transformations? Not needed for now + let mut widget = widget.0.borrow_mut(); + let (scroll_shift, info) = match widget::get_scrollbar_info(l) { + Some(info) => (widget.get_scroll_shift(&info, l), Some(info)), + 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; @@ -275,8 +307,6 @@ impl Layout { 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)? { widget::EventResult::Pass => { // go on @@ -292,6 +322,10 @@ impl Layout { self.push_event_children(listeners, node_id, event, alterables, user_data)?; } + if scissor_pushed { + alterables.scissor_stack.pop(); + } + alterables.transform_stack.pop(); Ok(()) @@ -361,7 +395,7 @@ impl Layout { prev_size: Vec2::default(), content_size: Vec2::default(), root_node, - root_widget, + root_widget: root_widget.id, needs_redraw: true, haptics_triggered: false, animations: Animations::default(), @@ -468,3 +502,24 @@ impl Layout { 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) + } +} diff --git a/wgui/src/parser/component_button.rs b/wgui/src/parser/component_button.rs index cffc13c..1aefe75 100644 --- a/wgui/src/parser/component_button.rs +++ b/wgui/src/parser/component_button.rs @@ -1,12 +1,11 @@ use crate::{ - components::{button, Component}, + components::{Component, button}, drawing::Color, i18n::Translation, layout::WidgetID, parser::{ - parse_children, process_component, + AttribPair, ParserContext, ParserFile, parse_children, process_component, style::{parse_color_opt, parse_round, parse_style, parse_text_style}, - AttribPair, ParserContext, ParserFile, }, widget::util::WLength, }; @@ -30,27 +29,27 @@ pub fn parse_component_button<'a, U1, U2>( for pair in attribs { let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); - match key.as_ref() { + match key { "text" => { - translation = Some(Translation::from_raw_text(&value)); + translation = Some(Translation::from_raw_text(value)); } "translation" => { - translation = Some(Translation::from_translation_key(&value)); + translation = Some(Translation::from_translation_key(value)); } "round" => { - parse_round(&value, &mut round); + parse_round(value, &mut round); } "color" => { - parse_color_opt(&value, &mut color); + parse_color_opt(value, &mut color); } "border_color" => { - parse_color_opt(&value, &mut border_color); + parse_color_opt(value, &mut border_color); } "hover_color" => { - parse_color_opt(&value, &mut hover_color); + parse_color_opt(value, &mut hover_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 (new_id, component) = button::construct( + let (widget, component) = button::construct( &mut globals.get(), ctx.layout, ctx.listeners, @@ -75,8 +74,8 @@ pub fn parse_component_button<'a, U1, U2>( }, )?; - process_component(ctx, Component(component), new_id, attribs); - parse_children(file, ctx, node, new_id)?; + process_component(ctx, Component(component), widget.id, attribs); + parse_children(file, ctx, node, widget.id)?; - Ok(new_id) + Ok(widget.id) } diff --git a/wgui/src/parser/component_checkbox.rs b/wgui/src/parser/component_checkbox.rs index 95d8128..c7d736f 100644 --- a/wgui/src/parser/component_checkbox.rs +++ b/wgui/src/parser/component_checkbox.rs @@ -1,11 +1,11 @@ use crate::{ - components::{checkbox, Component}, + components::{Component, checkbox}, i18n::Translation, 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( ctx: &mut ParserContext, parent_id: WidgetID, 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.listeners, 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) } diff --git a/wgui/src/parser/component_slider.rs b/wgui/src/parser/component_slider.rs index b6ff768..7904e26 100644 --- a/wgui/src/parser/component_slider.rs +++ b/wgui/src/parser/component_slider.rs @@ -1,10 +1,10 @@ use crate::{ - components::{slider, Component}, + components::{Component, slider}, 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( ctx: &mut ParserContext, parent_id: WidgetID, 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.listeners, 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) } diff --git a/wgui/src/parser/mod.rs b/wgui/src/parser/mod.rs index 68f027c..34dc228 100644 --- a/wgui/src/parser/mod.rs +++ b/wgui/src/parser/mod.rs @@ -530,9 +530,9 @@ fn parse_widget_other_internal( Ok(()) } -fn parse_widget_other<'a, U1, U2>( +fn parse_widget_other( xml_tag_name: &str, - file: &'a ParserFile, + file: &ParserFile, ctx: &mut ParserContext, parent_id: WidgetID, attribs: &[AttribPair], @@ -548,7 +548,7 @@ fn parse_widget_other<'a, U1, U2>( parse_widget_other_internal(&template, template_parameters, file, ctx, parent_id) } -fn parse_tag_include<'a, U1, U2>( +fn parse_tag_include( file: &ParserFile, ctx: &mut ParserContext, parent_id: WidgetID, @@ -641,7 +641,7 @@ fn process_attrib<'a, U1, U2>( let name = &value[1..]; match ctx.get_var(name) { - Some(name) => AttribPair::new(key, name.clone()), + Some(name) => AttribPair::new(key, name), None => AttribPair::new(key, "undefined"), } } else { @@ -655,7 +655,7 @@ fn raw_attribs<'a>(node: &'a roxmltree::Node<'a, 'a>) -> Vec { let (key, value) = (attrib.name(), attrib.value()); res.push(AttribPair::new(key, value)); } - return res; + res } fn process_attribs<'a, U1, U2>( @@ -761,7 +761,7 @@ fn parse_tag_macro(file: &ParserFile, ctx: &mut ParserContext, n ctx.insert_macro_attrib(name, MacroAttribs { attribs: macro_attribs }); } -fn process_component<'a, U1, U2>( +fn process_component( ctx: &mut ParserContext, component: Component, widget_id: WidgetID, @@ -782,7 +782,7 @@ fn process_component<'a, U1, U2>( ctx.insert_component(widget_id, component, component_id); } -fn parse_widget_universal<'a, U1, U2>(ctx: &mut ParserContext, widget_id: WidgetID, attribs: &[AttribPair]) { +fn parse_widget_universal(ctx: &mut ParserContext, widget_id: WidgetID, attribs: &[AttribPair]) { for pair in attribs { #[allow(clippy::single_match)] match pair.attrib.as_ref() { @@ -957,7 +957,7 @@ impl CustomAttribsInfo<'_> { CustomAttribsInfoOwned { parent_id: self.parent_id, widget_id: self.widget_id, - pairs: self.pairs.iter().cloned().collect(), + pairs: self.pairs.to_vec(), } } } diff --git a/wgui/src/parser/style.rs b/wgui/src/parser/style.rs index 1018c28..a3f6094 100644 --- a/wgui/src/parser/style.rs +++ b/wgui/src/parser/style.rs @@ -6,8 +6,8 @@ use taffy::{ use crate::{ drawing, parser::{ - is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, print_invalid_attrib, - print_invalid_value, AttribPair, + AttribPair, is_percent, parse_color_hex, parse_f32, parse_percent, parse_size_unit, parse_val, + print_invalid_attrib, print_invalid_value, }, renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle}, widget::util::WLength, @@ -54,7 +54,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle { style.color = Some(color); } } - "align" => match value.as_ref() { + "align" => match value { "left" => style.align = Some(HorizontalAlign::Left), "right" => style.align = Some(HorizontalAlign::Right), "center" => style.align = Some(HorizontalAlign::Center), @@ -64,7 +64,7 @@ pub fn parse_text_style(attribs: &[AttribPair]) -> TextStyle { print_invalid_attrib(key, value); } }, - "weight" => match value.as_ref() { + "weight" => match value { "normal" => style.weight = Some(FontWeight::Normal), "bold" => style.weight = Some(FontWeight::Bold), _ => { @@ -111,8 +111,8 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { for pair in attribs { let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref()); - match key.as_ref() { - "display" => match value.as_ref() { + match key { + "display" => match value { "flex" => style.display = Display::Flex, "block" => style.display = Display::Block, "grid" => style.display = Display::Grid, @@ -176,7 +176,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { style.padding.bottom = dim; } } - "overflow" => match value.as_ref() { + "overflow" => match value { "hidden" => { style.overflow.x = Overflow::Hidden; style.overflow.y = Overflow::Hidden; @@ -197,7 +197,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { print_invalid_attrib(key, value); } }, - "overflow_x" => match value.as_ref() { + "overflow_x" => match value { "hidden" => style.overflow.x = Overflow::Hidden, "visible" => style.overflow.x = Overflow::Visible, "clip" => style.overflow.x = Overflow::Clip, @@ -206,7 +206,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { print_invalid_attrib(key, value); } }, - "overflow_y" => match value.as_ref() { + "overflow_y" => match value { "hidden" => style.overflow.y = Overflow::Hidden, "visible" => style.overflow.y = Overflow::Visible, "clip" => style.overflow.y = Overflow::Clip, @@ -265,21 +265,21 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { style.flex_shrink = val; } } - "position" => match value.as_ref() { + "position" => match value { "absolute" => style.position = taffy::Position::Absolute, "relative" => style.position = taffy::Position::Relative, _ => { print_invalid_attrib(key, value); } }, - "box_sizing" => match value.as_ref() { + "box_sizing" => match value { "border_box" => style.box_sizing = BoxSizing::BorderBox, "content_box" => style.box_sizing = BoxSizing::ContentBox, _ => { print_invalid_attrib(key, value); } }, - "align_self" => match value.as_ref() { + "align_self" => match value { "baseline" => style.align_self = Some(AlignSelf::Baseline), "center" => style.align_self = Some(AlignSelf::Center), "end" => style.align_self = Some(AlignSelf::End), @@ -291,7 +291,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { print_invalid_attrib(key, value); } }, - "justify_self" => match value.as_ref() { + "justify_self" => match value { "center" => style.justify_self = Some(JustifySelf::Center), "end" => style.justify_self = Some(JustifySelf::End), "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); } }, - "align_items" => match value.as_ref() { + "align_items" => match value { "baseline" => style.align_items = Some(AlignItems::Baseline), "center" => style.align_items = Some(AlignItems::Center), "end" => style.align_items = Some(AlignItems::End), @@ -314,7 +314,7 @@ pub fn parse_style(attribs: &[AttribPair]) -> taffy::Style { print_invalid_attrib(key, value); } }, - "align_content" => match value.as_ref() { + "align_content" => match value { "center" => style.align_content = Some(AlignContent::Center), "end" => style.align_content = Some(AlignContent::End), "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); } }, - "justify_content" => match value.as_ref() { + "justify_content" => match value { "center" => style.justify_content = Some(JustifyContent::Center), "end" => style.justify_content = Some(JustifyContent::End), "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); } }, - "flex_wrap" => match value.as_ref() { + "flex_wrap" => match value { "wrap" => style.flex_wrap = FlexWrap::Wrap, "no_wrap" => style.flex_wrap = FlexWrap::NoWrap, "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" => style.flex_direction = FlexDirection::Column, "row_reverse" => style.flex_direction = FlexDirection::RowReverse, diff --git a/wgui/src/parser/widget_div.rs b/wgui/src/parser/widget_div.rs index cf1cbf3..1a4a814 100644 --- a/wgui/src/parser/widget_div.rs +++ b/wgui/src/parser/widget_div.rs @@ -1,6 +1,6 @@ use crate::{ 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, }; @@ -13,10 +13,10 @@ pub fn parse_widget_div<'a, U1, U2>( ) -> anyhow::Result { 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_children(file, ctx, node, new_id)?; + parse_widget_universal(ctx, widget.id, attribs); + parse_children(file, ctx, node, widget.id)?; - Ok(new_id) + Ok(widget.id) } diff --git a/wgui/src/parser/widget_label.rs b/wgui/src/parser/widget_label.rs index 31b1726..82552ee 100644 --- a/wgui/src/parser/widget_label.rs +++ b/wgui/src/parser/widget_label.rs @@ -2,9 +2,8 @@ use crate::{ i18n::Translation, layout::WidgetID, parser::{ - parse_children, parse_widget_universal, + AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::{parse_style, parse_text_style}, - AttribPair, ParserContext, ParserFile, }, widget::label::{WidgetLabel, WidgetLabelParams}, }; @@ -40,12 +39,12 @@ pub fn parse_widget_label<'a, U1, U2>( let globals = ctx.layout.state.globals.clone(); - let (new_id, _) = ctx + let (widget, _) = ctx .layout .add_child(parent_id, WidgetLabel::create(&mut globals.get(), params), style)?; - parse_widget_universal(ctx, new_id, attribs); - parse_children(file, ctx, node, new_id)?; + parse_widget_universal(ctx, widget.id, attribs); + parse_children(file, ctx, node, widget.id)?; - Ok(new_id) + Ok(widget.id) } diff --git a/wgui/src/parser/widget_rectangle.rs b/wgui/src/parser/widget_rectangle.rs index 33a9030..4ec9c78 100644 --- a/wgui/src/parser/widget_rectangle.rs +++ b/wgui/src/parser/widget_rectangle.rs @@ -2,9 +2,8 @@ use crate::{ drawing::GradientMode, layout::WidgetID, 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}, - AttribPair, ParserContext, ParserFile, }, widget::rectangle::{WidgetRectangle, WidgetRectangleParams}, }; @@ -17,7 +16,7 @@ pub fn parse_widget_rectangle<'a, U1, U2>( attribs: &[AttribPair], ) -> anyhow::Result { let mut params = WidgetRectangleParams::default(); - let style = parse_style(&attribs); + let style = parse_style(attribs); for pair in attribs { 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 .add_child(parent_id, WidgetRectangle::create(params), style)?; - parse_widget_universal(ctx, new_id, attribs); - parse_children(file, ctx, node, new_id)?; + parse_widget_universal(ctx, widget.id, attribs); + parse_children(file, ctx, node, widget.id)?; - Ok(new_id) + Ok(widget.id) } diff --git a/wgui/src/parser/widget_sprite.rs b/wgui/src/parser/widget_sprite.rs index cf11218..e64c781 100644 --- a/wgui/src/parser/widget_sprite.rs +++ b/wgui/src/parser/widget_sprite.rs @@ -1,6 +1,6 @@ use crate::{ 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}, widget::sprite::{WidgetSprite, WidgetSpriteParams}, }; @@ -15,7 +15,7 @@ pub fn parse_widget_sprite<'a, U1, U2>( attribs: &[AttribPair], ) -> anyhow::Result { let mut params = WidgetSpriteParams::default(); - let style = parse_style(&attribs); + let style = parse_style(attribs); let mut glyph = None; for pair in attribs { @@ -54,10 +54,10 @@ pub fn parse_widget_sprite<'a, U1, U2>( 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_children(file, ctx, node, new_id)?; + parse_widget_universal(ctx, widget.id, attribs); + parse_children(file, ctx, node, widget.id)?; - Ok(new_id) + Ok(widget.id) } diff --git a/wgui/src/renderer_vk/context.rs b/wgui/src/renderer_vk/context.rs index 8fa66f0..78bdd8b 100644 --- a/wgui/src/renderer_vk/context.rs +++ b/wgui/src/renderer_vk/context.rs @@ -2,20 +2,20 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use cosmic_text::Buffer; use glam::{Mat4, Vec2, Vec3}; -use slotmap::{new_key_type, SlotMap}; +use slotmap::{SlotMap, new_key_type}; use vulkano::pipeline::graphics::viewport; use crate::{ drawing::{self}, - gfx::{cmd::GfxCommandBuffer, WGfx}, + gfx::{WGfx, cmd::GfxCommandBuffer}, }; use super::{ rect::{RectPipeline, RectRenderer}, text::{ + DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, TextArea, TextBounds, text_atlas::{TextAtlas, TextPipeline}, text_renderer::TextRenderer, - TextArea, TextBounds, DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, }, viewport::Viewport, }; @@ -287,12 +287,8 @@ impl Context { transform: extent.transform, }); } - drawing::RenderPrimitive::ScissorEnable(boundary) => { - next_scissor = Some(*boundary); - needs_new_pass = true; - } - drawing::RenderPrimitive::ScissorDisable => { - next_scissor = None; + drawing::RenderPrimitive::ScissorSet(boundary) => { + next_scissor = Some(boundary.0); needs_new_pass = true; } } diff --git a/wgui/src/stack.rs b/wgui/src/stack.rs index f365949..28f77a9 100644 --- a/wgui/src/stack.rs +++ b/wgui/src/stack.rs @@ -1,4 +1,4 @@ -use glam::Vec2; +use glam::{Mat4, Vec2, Vec3}; use crate::drawing; @@ -50,18 +50,40 @@ impl, const STACK_MAX: usize> Default for GenericStack Self { + Self { + abs_pos: Default::default(), + rel_pos: Default::default(), + dim: Default::default(), + transform: Mat4::IDENTITY, + transform_rel: Default::default(), + } + } } impl StackItem for Transform where Transform: Pushable {} impl Pushable for 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; // Scissor stack // ######################################## -impl StackItem for drawing::Boundary where drawing::Boundary: Pushable {} +#[derive(Copy, Clone)] +pub struct ScissorBoundary(pub drawing::Boundary); -impl Pushable for drawing::Boundary { - fn push(&mut self, upper: &drawing::Boundary) { - let mut display_pos = self.pos; - let mut display_size = self.size; +impl Default for ScissorBoundary { + fn default() -> Self { + Self(drawing::Boundary { + pos: Default::default(), + size: Vec2::splat(1.0e12), + }) + } +} + +impl StackItem for ScissorBoundary where ScissorBoundary: Pushable {} + +impl Pushable 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 if display_pos.x < upper.left() { @@ -100,9 +136,9 @@ impl Pushable for drawing::Boundary { display_size.y = upper.bottom() - display_pos.y; } - self.pos = display_pos; - self.size = display_size; + self.0.pos = display_pos; + self.0.size = display_size; } } -pub type ScissorStack = GenericStack; +pub type ScissorStack = GenericStack; diff --git a/wgui/src/widget/label.rs b/wgui/src/widget/label.rs index 8518cf2..fc8b4cf 100644 --- a/wgui/src/widget/label.rs +++ b/wgui/src/widget/label.rs @@ -10,7 +10,7 @@ use crate::{ globals::Globals, i18n::{I18n, Translation}, layout::WidgetID, - renderer_vk::text::{TextStyle, FONT_SYSTEM}, + renderer_vk::text::{FONT_SYSTEM, TextStyle}, }; use super::{WidgetObj, WidgetState}; @@ -109,7 +109,7 @@ impl WidgetLabel { impl WidgetObj for WidgetLabel { 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 { self.last_boundary = boundary; diff --git a/wgui/src/widget/mod.rs b/wgui/src/widget/mod.rs index f83c8e5..e7019c0 100644 --- a/wgui/src/widget/mod.rs +++ b/wgui/src/widget/mod.rs @@ -11,6 +11,7 @@ use crate::{ }, layout::{Layout, LayoutState, WidgetID}, stack::{ScissorStack, TransformStack}, + widget, }; pub mod div; @@ -248,8 +249,8 @@ impl WidgetState { PrimitiveExtent { boundary: drawing::Boundary::from_pos_size( Vec2::new( - transform.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.x + transform.dim.x * (1.0 - info.handle_size.x) * self.data.scrolling.x, + transform.abs_pos.y + transform.dim.y - thickness - margin, ), Vec2::new(transform.dim.x * info.handle_size.x, thickness), ), @@ -265,8 +266,8 @@ impl WidgetState { PrimitiveExtent { boundary: drawing::Boundary::from_pos_size( Vec2::new( - transform.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.x + transform.dim.x - thickness - margin, + 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), ), diff --git a/wgui/src/widget/rectangle.rs b/wgui/src/widget/rectangle.rs index 7408d4a..9a98e5e 100644 --- a/wgui/src/widget/rectangle.rs +++ b/wgui/src/widget/rectangle.rs @@ -36,7 +36,7 @@ impl WidgetRectangle { impl WidgetObj for WidgetRectangle { 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 { WLength::Units(units) => units as u8, diff --git a/wgui/src/widget/sprite.rs b/wgui/src/widget/sprite.rs index f150c98..af7d34a 100644 --- a/wgui/src/widget/sprite.rs +++ b/wgui/src/widget/sprite.rs @@ -7,8 +7,8 @@ use crate::{ drawing::{self, PrimitiveExtent}, layout::WidgetID, renderer_vk::text::{ - custom_glyph::{CustomGlyph, CustomGlyphData}, DEFAULT_METRICS, FONT_SYSTEM, + custom_glyph::{CustomGlyph, CustomGlyphData}, }, }; @@ -37,7 +37,7 @@ impl WidgetSprite { impl WidgetObj for WidgetSprite { 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() { let glyph = CustomGlyph {