wgui: pass motion events further, add consume_mouse_events parameter to widgets (Closes #306), always reverse iter events
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
<elements>
|
||||
<div
|
||||
consume_mouse_events="1"
|
||||
id="root"
|
||||
new_pass="1"
|
||||
width="100%"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
and WINDOW_DECORATION_HEADER_HEIGHT in the source code accordingly
|
||||
-->
|
||||
<rectangle
|
||||
consume_mouse_events="1"
|
||||
flex_direction="column"
|
||||
round="16"
|
||||
border="2"
|
||||
|
||||
@@ -66,6 +66,10 @@ _They can be used in any widget/component._
|
||||
|
||||
_Set to 0 if you want to exclude this widget from altering the event state_
|
||||
|
||||
`consume_mouse_events`: "1" | "0"
|
||||
|
||||
_Used in case of overlapping pop-ups or windows, most notably applied to various backgrounds_
|
||||
|
||||
`new_pass`: "1" | "0"
|
||||
|
||||
_Set to 1 if you want to render overlapping pop-ups to properly render your widgets in order. Wgui renders with as few Vulkan drawcalls as possible, so this is your responsibility._
|
||||
|
||||
@@ -127,7 +127,7 @@ impl Color {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_alpha(&self, n: f32) -> Self {
|
||||
pub const fn with_alpha(&self, n: f32) -> Self {
|
||||
Self {
|
||||
r: self.r,
|
||||
g: self.g,
|
||||
@@ -267,7 +267,7 @@ fn draw_widget(
|
||||
|
||||
let mut widget_state = widget.state();
|
||||
|
||||
if widget_state.new_pass {
|
||||
if widget_state.flags.new_pass {
|
||||
state.primitives.push(RenderPrimitive::NewPass);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack},
|
||||
event::{self, CallbackDataCommon, EventAlterables},
|
||||
globals::WguiGlobals,
|
||||
widget::{self, EventParams, EventResult, WidgetObj, WidgetState, div::WidgetDiv},
|
||||
widget::{self, EventParams, EventResult, WidgetObj, WidgetState, WidgetStateFlags, div::WidgetDiv},
|
||||
};
|
||||
|
||||
use glam::{Vec2, vec2};
|
||||
@@ -377,7 +377,6 @@ impl Layout {
|
||||
event_result: &mut EventResult,
|
||||
alterables: &mut EventAlterables,
|
||||
user_data: &mut (&'a mut U1, &'a mut U2),
|
||||
reverse: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let count = self.state.tree.child_count(parent_node_id);
|
||||
|
||||
@@ -387,17 +386,10 @@ impl Layout {
|
||||
Ok(!event_result.can_propagate())
|
||||
};
|
||||
|
||||
if reverse {
|
||||
for idx in (0..count).rev() {
|
||||
if iter(idx)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for idx in 0..count {
|
||||
if iter(idx)? {
|
||||
break;
|
||||
}
|
||||
// reverse iter
|
||||
for idx in (0..count).rev() {
|
||||
if iter(idx)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,11 +440,8 @@ impl Layout {
|
||||
style,
|
||||
);
|
||||
|
||||
// topmost widgets are iterated in reverse order
|
||||
let reverse_iter = is_root_node;
|
||||
|
||||
// check children first
|
||||
self.push_event_children(node_id, event, event_result, alterables, user_data, reverse_iter)?;
|
||||
self.push_event_children(node_id, event, event_result, alterables, user_data)?;
|
||||
|
||||
if event_result.can_propagate() {
|
||||
let mut params = EventParams {
|
||||
@@ -532,7 +521,10 @@ impl Layout {
|
||||
&mut state.nodes,
|
||||
None, // no parent
|
||||
WidgetState {
|
||||
interactable: false,
|
||||
flags: WidgetStateFlags {
|
||||
interactable: false,
|
||||
..Default::default()
|
||||
},
|
||||
..WidgetDiv::create()
|
||||
},
|
||||
taffy::Style {
|
||||
@@ -547,7 +539,10 @@ impl Layout {
|
||||
&mut state.nodes,
|
||||
Some(tree_root_node),
|
||||
WidgetState {
|
||||
interactable: false,
|
||||
flags: WidgetStateFlags {
|
||||
interactable: false,
|
||||
..Default::default()
|
||||
},
|
||||
..WidgetDiv::create()
|
||||
},
|
||||
taffy::Style {
|
||||
|
||||
@@ -831,14 +831,21 @@ fn parse_widget_universal(ctx: &mut ParserContext, widget: &WidgetPair, attribs:
|
||||
}
|
||||
"new_pass" => {
|
||||
if let Some(num) = parse_i32(&pair.value) {
|
||||
widget.widget.state().new_pass = num != 0;
|
||||
widget.widget.state().flags.new_pass = num != 0;
|
||||
} else {
|
||||
print_invalid_attrib(&pair.attrib, &pair.value);
|
||||
}
|
||||
}
|
||||
"interactable" => {
|
||||
if let Some(num) = parse_i32(&pair.value) {
|
||||
widget.widget.state().interactable = num != 0;
|
||||
widget.widget.state().flags.interactable = num != 0;
|
||||
} else {
|
||||
print_invalid_attrib(&pair.attrib, &pair.value);
|
||||
}
|
||||
}
|
||||
"consume_mouse_events" => {
|
||||
if let Some(num) = parse_i32(&pair.value) {
|
||||
widget.widget.state().flags.consume_mouse_events = num != 0;
|
||||
} else {
|
||||
print_invalid_attrib(&pair.attrib, &pair.value);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use slotmap::Key;
|
||||
|
||||
use crate::layout::WidgetID;
|
||||
use crate::{layout::WidgetID, widget::WidgetStateFlags};
|
||||
|
||||
use super::{WidgetObj, WidgetState};
|
||||
|
||||
@@ -10,7 +10,7 @@ pub struct WidgetDiv {
|
||||
|
||||
impl WidgetDiv {
|
||||
pub fn create() -> WidgetState {
|
||||
WidgetState::new(Box::new(Self { id: WidgetID::null() }))
|
||||
WidgetState::new(WidgetStateFlags::default(), Box::new(Self { id: WidgetID::null() }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
i18n::Translation,
|
||||
layout::WidgetID,
|
||||
renderer_vk::text::TextStyle,
|
||||
widget::WidgetStateFlags,
|
||||
};
|
||||
|
||||
use super::{WidgetObj, WidgetState};
|
||||
@@ -53,12 +54,15 @@ impl WidgetLabel {
|
||||
);
|
||||
}
|
||||
|
||||
WidgetState::new(Box::new(Self {
|
||||
params,
|
||||
buffer: Rc::new(RefCell::new(buffer)),
|
||||
last_boundary: Boundary::default(),
|
||||
id: WidgetID::null(),
|
||||
}))
|
||||
WidgetState::new(
|
||||
WidgetStateFlags::default(),
|
||||
Box::new(Self {
|
||||
params,
|
||||
buffer: Rc::new(RefCell::new(buffer)),
|
||||
last_boundary: Boundary::default(),
|
||||
id: WidgetID::null(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// set text without layout/re-render update.
|
||||
|
||||
@@ -76,12 +76,31 @@ impl WidgetData {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetStateFlags {
|
||||
pub interactable: bool,
|
||||
|
||||
// consume any incoming mouse event which is hovered at given widget
|
||||
pub consume_mouse_events: bool,
|
||||
|
||||
// force a new render pass before rendering this widget
|
||||
pub new_pass: bool,
|
||||
}
|
||||
|
||||
impl Default for WidgetStateFlags {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
interactable: true,
|
||||
consume_mouse_events: false,
|
||||
new_pass: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetState {
|
||||
pub data: WidgetData,
|
||||
pub obj: Box<dyn WidgetObj>,
|
||||
pub event_listeners: EventListenerCollection,
|
||||
pub interactable: bool,
|
||||
pub new_pass: bool, // force a new render pass
|
||||
pub flags: WidgetStateFlags,
|
||||
}
|
||||
|
||||
impl WidgetState {
|
||||
@@ -91,7 +110,7 @@ impl WidgetState {
|
||||
(data, obj)
|
||||
}
|
||||
|
||||
fn new(obj: Box<dyn WidgetObj>) -> Self {
|
||||
fn new(flags: WidgetStateFlags, obj: Box<dyn WidgetObj>) -> Self {
|
||||
Self {
|
||||
data: WidgetData {
|
||||
hovered: 0,
|
||||
@@ -104,8 +123,7 @@ impl WidgetState {
|
||||
},
|
||||
obj,
|
||||
event_listeners: EventListenerCollection::default(),
|
||||
interactable: true,
|
||||
new_pass: false,
|
||||
flags,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,13 +259,19 @@ struct InvokeData<'a, 'b, U1: 'static, U2: 'static> {
|
||||
params: &'a mut EventParams<'a>,
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
enum InvokeListenersResult {
|
||||
NobodyListened,
|
||||
AtLeastOneCalled,
|
||||
}
|
||||
|
||||
impl WidgetState {
|
||||
fn invoke_listeners<U1: 'static, U2: 'static>(
|
||||
&mut self,
|
||||
call_data: &mut InvokeData<'_, '_, U1, U2>,
|
||||
kind: event::EventListenerKind,
|
||||
metadata: CallbackMetadata,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> anyhow::Result<InvokeListenersResult> {
|
||||
let mut data = CallbackData {
|
||||
obj: self.obj.as_mut(),
|
||||
widget_data: &mut self.data,
|
||||
@@ -261,13 +285,17 @@ impl WidgetState {
|
||||
alterables: call_data.params.alterables,
|
||||
};
|
||||
|
||||
let mut res = InvokeListenersResult::NobodyListened;
|
||||
|
||||
for listener in self.event_listeners.iter_filtered::<U1, U2>(kind) {
|
||||
let new_result = listener.call_with(&mut common, &mut data, call_data.user_data)?;
|
||||
res = InvokeListenersResult::AtLeastOneCalled;
|
||||
// Consider all listeners on this widget, even if we had a Consume.
|
||||
// Store the highest value for return.
|
||||
*call_data.event_result = call_data.event_result.merge(new_result);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn get_scroll_shift_smooth(&self, info: &ScrollbarInfo, l: &taffy::Layout, timestep_alpha: f32) -> (Vec2, bool) {
|
||||
@@ -449,29 +477,31 @@ impl WidgetState {
|
||||
params,
|
||||
};
|
||||
|
||||
let mut res: Option<InvokeListenersResult> = None;
|
||||
|
||||
match &event {
|
||||
Event::MouseDown(e) => {
|
||||
if hovered && self.data.set_device_pressed(e.device, true) {
|
||||
self.invoke_listeners(
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::MousePress,
|
||||
CallbackMetadata::MouseButton(event::MouseButton {
|
||||
index: e.index,
|
||||
pos: e.pos,
|
||||
}),
|
||||
)?;
|
||||
)?);
|
||||
}
|
||||
}
|
||||
Event::MouseUp(e) => {
|
||||
if self.data.set_device_pressed(e.device, false) {
|
||||
self.invoke_listeners(
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::MouseRelease,
|
||||
CallbackMetadata::MouseButton(event::MouseButton {
|
||||
index: e.index,
|
||||
pos: e.pos,
|
||||
}),
|
||||
)?;
|
||||
)?);
|
||||
}
|
||||
}
|
||||
Event::MouseMotion(e) => {
|
||||
@@ -479,18 +509,20 @@ impl WidgetState {
|
||||
|
||||
if hover_state_changed {
|
||||
if self.data.is_hovered() {
|
||||
self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseEnter, CallbackMetadata::None)?;
|
||||
res =
|
||||
Some(self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseEnter, CallbackMetadata::None)?);
|
||||
} else {
|
||||
self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseLeave, CallbackMetadata::None)?;
|
||||
res =
|
||||
Some(self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseLeave, CallbackMetadata::None)?);
|
||||
}
|
||||
} else if hovered {
|
||||
self.invoke_listeners(
|
||||
} else {
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::MouseMotion,
|
||||
CallbackMetadata::MousePosition(event::MousePosition { pos: e.pos }),
|
||||
)?;
|
||||
)?);
|
||||
|
||||
if self.interactable {
|
||||
if self.flags.interactable {
|
||||
*invoke_data.event_result = invoke_data.event_result.merge(EventResult::Pass);
|
||||
}
|
||||
}
|
||||
@@ -503,17 +535,29 @@ impl WidgetState {
|
||||
}
|
||||
Event::MouseLeave(e) => {
|
||||
if self.data.set_device_hovered(e.device, false) {
|
||||
self.invoke_listeners(&mut invoke_data, MouseLeave, CallbackMetadata::None)?;
|
||||
res = Some(self.invoke_listeners(&mut invoke_data, MouseLeave, CallbackMetadata::None)?);
|
||||
}
|
||||
}
|
||||
Event::InternalStateChange(e) => {
|
||||
self.invoke_listeners(
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
InternalStateChange,
|
||||
CallbackMetadata::Custom(e.metadata),
|
||||
)?;
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(res) = res {
|
||||
match res {
|
||||
InvokeListenersResult::NobodyListened => {
|
||||
if hovered && self.flags.consume_mouse_events {
|
||||
*invoke_data.event_result = EventResult::Consumed;
|
||||
}
|
||||
}
|
||||
InvokeListenersResult::AtLeastOneCalled => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use slotmap::Key;
|
||||
use crate::{
|
||||
drawing::{self, GradientMode, PrimitiveExtent},
|
||||
layout::WidgetID,
|
||||
widget::util::WLength,
|
||||
widget::{WidgetStateFlags, util::WLength},
|
||||
};
|
||||
|
||||
use super::{WidgetObj, WidgetState};
|
||||
@@ -27,10 +27,13 @@ pub struct WidgetRectangle {
|
||||
|
||||
impl WidgetRectangle {
|
||||
pub fn create(params: WidgetRectangleParams) -> WidgetState {
|
||||
WidgetState::new(Box::new(Self {
|
||||
params,
|
||||
id: WidgetID::null(),
|
||||
}))
|
||||
WidgetState::new(
|
||||
WidgetStateFlags::default(),
|
||||
Box::new(Self {
|
||||
params,
|
||||
id: WidgetID::null(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
DEFAULT_METRICS,
|
||||
custom_glyph::{CustomGlyph, CustomGlyphData},
|
||||
},
|
||||
widget::WidgetStateFlags,
|
||||
};
|
||||
|
||||
use super::{WidgetObj, WidgetState};
|
||||
@@ -29,10 +30,13 @@ pub struct WidgetSprite {
|
||||
|
||||
impl WidgetSprite {
|
||||
pub fn create(params: WidgetSpriteParams) -> WidgetState {
|
||||
WidgetState::new(Box::new(Self {
|
||||
params,
|
||||
id: WidgetID::null(),
|
||||
}))
|
||||
WidgetState::new(
|
||||
WidgetStateFlags::default(),
|
||||
Box::new(Self {
|
||||
params,
|
||||
id: WidgetID::null(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: drawing::Color) {
|
||||
|
||||
@@ -50,7 +50,7 @@ impl<T> OverlayWindowData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, IntegerId, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, IntegerId, PartialEq, Eq)]
|
||||
pub enum OverlayCategory {
|
||||
Internal,
|
||||
Keyboard,
|
||||
|
||||
Reference in New Issue
Block a user