mirror of https://github.com/wayvr-org/wayvr.git
Merge pull request #443 from wlx-team/feat-editbox
Editbox, wgui fixes, doc
This commit is contained in:
commit
b84d565b1e
|
|
@ -93,19 +93,18 @@
|
|||
position="absolute" width="100%" height="100%"
|
||||
gradient="radial" color="#44BBFF11" color2="#00000000" />
|
||||
|
||||
<div
|
||||
id="content"
|
||||
flex_direction="column"
|
||||
overflow_x="scroll"
|
||||
overflow_y="scroll"
|
||||
padding="16"
|
||||
gap="8"
|
||||
width="100%"
|
||||
min_height="100%"
|
||||
>
|
||||
<!-- filled-in at runtime -->
|
||||
<div overflow_x="scroll" overflow_y="scroll" width="100%" height="100%">
|
||||
<div
|
||||
id="content"
|
||||
flex_direction="column"
|
||||
padding="16"
|
||||
gap="8"
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<!-- filled-in at runtime -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div position="absolute" id="popup_manager" width="100%" height="100%" />
|
||||
</rectangle>
|
||||
<!-- BOTTOM PANEL -->
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
flex_direction="row"
|
||||
flex_wrap="wrap"
|
||||
gap="4"
|
||||
overflow_y="scroll"
|
||||
/>
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<layout>
|
||||
<elements>
|
||||
<div id="running_games_list_parent" align_self="center" />
|
||||
<div id="game_list_parent" align_items="center" flex_direction="column" gap="8" overflow_y="scroll" />
|
||||
<div id="game_list_parent" align_items="center" flex_direction="column" gap="8" />
|
||||
</elements>
|
||||
</layout>
|
||||
|
|
@ -5,6 +5,7 @@ use wgui::{
|
|||
widget::label::{WidgetLabel, WidgetLabelParams},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation) -> anyhow::Result<()> {
|
||||
let label = WidgetLabel::create(
|
||||
&mut layout.state.globals.get(),
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ enum CompositorMode {
|
|||
enum Task {
|
||||
SetCompositor(CompositorMode),
|
||||
SetRes(ResMode),
|
||||
SetPos(PosMode), // TODO?
|
||||
SetOrientation(OrientationMode),
|
||||
SetAutoStart(bool),
|
||||
Launch,
|
||||
|
|
@ -295,7 +294,6 @@ impl View {
|
|||
match task {
|
||||
Task::SetCompositor(mode) => self.compositor_mode = mode,
|
||||
Task::SetRes(mode) => self.res_mode = mode,
|
||||
Task::SetPos(mode) => self.pos_mode = mode,
|
||||
Task::SetOrientation(mode) => self.orientation_mode = mode,
|
||||
Task::SetAutoStart(auto_start) => self.auto_start = auto_start,
|
||||
Task::Launch => self.action_launch(interface, data),
|
||||
|
|
|
|||
|
|
@ -59,10 +59,18 @@
|
|||
</div>
|
||||
</rectangle>
|
||||
|
||||
<rectangle macro="rect">
|
||||
<label text="Context menu test" />
|
||||
<Button id="button_context_menu" text="Show context menu" />
|
||||
</rectangle>
|
||||
<div>
|
||||
<rectangle macro="rect">
|
||||
<label text="Context menu test" />
|
||||
<Button id="button_context_menu" text="Show context menu" />
|
||||
</rectangle>
|
||||
<rectangle macro="rect">
|
||||
<label text="Editbox test" />
|
||||
<EditBox text="Initial text. Test, abcdef." width="400" />
|
||||
|
||||
<EditBox margin_left="42" margin_top="42" margin_bottom="40" width="200" />
|
||||
</rectangle>
|
||||
</div>
|
||||
|
||||
<rectangle macro="rect">
|
||||
<label text="visibility test" weight="bold" />
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
use glam::{vec2, Vec2};
|
||||
use std::sync::Arc;
|
||||
use testbed::{testbed_any::TestbedAny, Testbed};
|
||||
use glam::{Vec2, vec2};
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
use testbed::{Testbed, testbed_any::TestbedAny};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use vulkan::init_window;
|
||||
use vulkano::{
|
||||
Validated, VulkanError,
|
||||
command_buffer::CommandBufferUsage,
|
||||
format::Format,
|
||||
image::{view::ImageView, ImageUsage},
|
||||
image::{ImageUsage, view::ImageView},
|
||||
swapchain::{
|
||||
acquire_next_image, ColorSpace, CompositeAlpha, PresentMode, Surface, SurfaceInfo, Swapchain,
|
||||
SwapchainCreateInfo, SwapchainPresentInfo,
|
||||
ColorSpace, CompositeAlpha, PresentMode, Surface, SurfaceInfo, Swapchain, SwapchainCreateInfo,
|
||||
SwapchainPresentInfo, acquire_next_image,
|
||||
},
|
||||
sync::GpuFuture,
|
||||
Validated, VulkanError,
|
||||
};
|
||||
use wgui::{
|
||||
event::{MouseButtonIndex, MouseDownEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent},
|
||||
gfx::{cmd::WGfxClearMode, WGfx},
|
||||
event::{MouseButtonEvent, MouseButtonIndex, MouseMotionEvent, MouseWheelEvent},
|
||||
gfx::{WGfx, cmd::WGfxClearMode},
|
||||
renderer_vk::{self},
|
||||
};
|
||||
use winit::{
|
||||
|
|
@ -32,7 +32,7 @@ use wlx_common::{audio, timestep::Timestep};
|
|||
use crate::{
|
||||
rate_limiter::RateLimiter,
|
||||
testbed::{
|
||||
testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric, TestbedUpdateParams,
|
||||
TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
testbed
|
||||
.layout()
|
||||
.push_event(
|
||||
&wgui::event::Event::MouseDown(MouseDownEvent {
|
||||
&wgui::event::Event::MouseDown(MouseButtonEvent {
|
||||
pos: mouse / scale,
|
||||
index: MouseButtonIndex::Left,
|
||||
device: 0,
|
||||
|
|
@ -187,7 +187,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
testbed
|
||||
.layout()
|
||||
.push_event(
|
||||
&wgui::event::Event::MouseUp(MouseUpEvent {
|
||||
&wgui::event::Event::MouseUp(MouseButtonEvent {
|
||||
pos: mouse / scale,
|
||||
index: MouseButtonIndex::Left,
|
||||
device: 0,
|
||||
|
|
@ -221,33 +221,46 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
..
|
||||
} => {
|
||||
if event.state == ElementState::Pressed {
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::F10) {
|
||||
debug_draw_enabled = !debug_draw_enabled;
|
||||
if debug_draw_enabled {
|
||||
log::info!(
|
||||
"Debug draw enabled\n\tAqua: widget boundary\n\tMagenta: Scissoring (separate render pass)"
|
||||
);
|
||||
if !event.repeat {
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::F10) {
|
||||
debug_draw_enabled = !debug_draw_enabled;
|
||||
if debug_draw_enabled {
|
||||
log::info!(
|
||||
"Debug draw enabled\n\tAqua: widget boundary\n\tMagenta: Scissoring (separate render pass)"
|
||||
);
|
||||
}
|
||||
testbed.layout().mark_redraw();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::F11) {
|
||||
testbed.layout().print_tree();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::Equal) {
|
||||
scale *= 1.25;
|
||||
render_context
|
||||
.update_viewport(&mut shared_context, swapchain_size, scale)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::Minus) {
|
||||
scale *= 0.75;
|
||||
render_context
|
||||
.update_viewport(&mut shared_context, swapchain_size, scale)
|
||||
.unwrap();
|
||||
}
|
||||
testbed.layout().mark_redraw();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::F11) {
|
||||
testbed.layout().print_tree();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::Equal) {
|
||||
scale *= 1.25;
|
||||
render_context
|
||||
.update_viewport(&mut shared_context, swapchain_size, scale)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if event.physical_key == PhysicalKey::Code(KeyCode::Minus) {
|
||||
scale *= 0.75;
|
||||
render_context
|
||||
.update_viewport(&mut shared_context, swapchain_size, scale)
|
||||
.unwrap();
|
||||
}
|
||||
testbed
|
||||
.layout()
|
||||
.push_event(
|
||||
&wgui::event::Event::TextInput(wgui::event::TextInputEvent {
|
||||
text: event.text.map(|text| Rc::from(text.as_str())),
|
||||
}),
|
||||
&mut (),
|
||||
&mut (),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ use wgui::{
|
|||
drawing,
|
||||
event::{
|
||||
CallbackDataCommon, Event as WguiEvent, EventAlterables, EventCallback, EventListenerID,
|
||||
EventListenerKind, InternalStateChangeEvent, MouseButtonIndex, MouseDownEvent,
|
||||
MouseLeaveEvent, MouseMotionEvent, MouseUpEvent, MouseWheelEvent,
|
||||
EventListenerKind, InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex,
|
||||
MouseLeaveEvent, MouseMotionEvent, MouseWheelEvent,
|
||||
},
|
||||
gfx::cmd::WGfxClearMode,
|
||||
i18n::Translation,
|
||||
|
|
@ -416,13 +416,13 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
|
|||
};
|
||||
|
||||
let e = if pressed {
|
||||
WguiEvent::MouseDown(MouseDownEvent {
|
||||
WguiEvent::MouseDown(MouseButtonEvent {
|
||||
pos: hit.uv * self.layout.content_size,
|
||||
index,
|
||||
device: hit.pointer,
|
||||
})
|
||||
} else {
|
||||
WguiEvent::MouseUp(MouseUpEvent {
|
||||
WguiEvent::MouseUp(MouseButtonEvent {
|
||||
pos: hit.uv * self.layout.content_size,
|
||||
index,
|
||||
device: hit.pointer,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use wayvr_ipc::{
|
|||
};
|
||||
use wgui::{
|
||||
event::{
|
||||
Event as WguiEvent, MouseButtonIndex, MouseDownEvent, MouseLeaveEvent, MouseMotionEvent,
|
||||
MouseUpEvent, MouseWheelEvent,
|
||||
Event as WguiEvent, MouseButtonEvent, MouseButtonIndex, MouseLeaveEvent, MouseMotionEvent,
|
||||
MouseWheelEvent,
|
||||
},
|
||||
gfx::cmd::WGfxClearMode,
|
||||
renderer_vk::context::Context as WguiContext,
|
||||
|
|
@ -247,13 +247,13 @@ impl OverlayBackend for DashFrontend {
|
|||
};
|
||||
|
||||
let e = if pressed {
|
||||
WguiEvent::MouseDown(MouseDownEvent {
|
||||
WguiEvent::MouseDown(MouseButtonEvent {
|
||||
pos: hit.uv * self.inner.layout.content_size,
|
||||
index,
|
||||
device: hit.pointer,
|
||||
})
|
||||
} else {
|
||||
WguiEvent::MouseUp(MouseUpEvent {
|
||||
WguiEvent::MouseUp(MouseButtonEvent {
|
||||
pos: hit.uv * self.inner.layout.content_size,
|
||||
index,
|
||||
device: hit.pointer,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use regex::Regex;
|
|||
use slotmap::{SlotMap, new_key_type};
|
||||
use wgui::{
|
||||
drawing,
|
||||
event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
|
||||
event::{InternalStateChangeEvent, MouseButtonEvent, MouseButtonIndex},
|
||||
};
|
||||
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
||||
use wlx_common::{
|
||||
|
|
@ -390,7 +390,7 @@ fn handle_press(
|
|||
app: &mut AppState,
|
||||
key: &KeyState,
|
||||
keyboard: &mut KeyboardState,
|
||||
button: MouseButton,
|
||||
button: MouseButtonEvent,
|
||||
) {
|
||||
match &key.button_state {
|
||||
KeyButtonData::Key { vk, pressed } => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
|
||||
use anyhow::Context;
|
||||
use glam::{Affine3A, Vec3, Vec3A};
|
||||
use slotmap::{HopSlotMap, Key, SecondaryMap};
|
||||
use slotmap::{Key, SecondaryMap, SlotMap};
|
||||
use wgui::log::LogErr;
|
||||
use wlx_common::{
|
||||
astr_containers::{AStrMap, AStrMapExt},
|
||||
|
|
@ -42,7 +42,7 @@ pub const MAX_OVERLAY_SETS: usize = 6;
|
|||
|
||||
pub struct OverlayWindowManager<T> {
|
||||
wrappers: EditWrapperManager,
|
||||
overlays: HopSlotMap<OverlayID, OverlayWindowData<T>>,
|
||||
overlays: SlotMap<OverlayID, OverlayWindowData<T>>,
|
||||
sets: Vec<OverlayWindowSet>,
|
||||
global_set: OverlayWindowSet,
|
||||
/// The set that is currently visible.
|
||||
|
|
@ -65,7 +65,7 @@ where
|
|||
pub fn new(app: &mut AppState, headless: bool) -> anyhow::Result<Self> {
|
||||
let mut me = Self {
|
||||
wrappers: EditWrapperManager::default(),
|
||||
overlays: HopSlotMap::with_key(),
|
||||
overlays: SlotMap::<OverlayID, OverlayWindowData<T>>::with_key(),
|
||||
current_set: Some(0),
|
||||
restore_set: 0,
|
||||
sets: vec![OverlayWindowSet::default()],
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
## [Built-in components](#components)
|
||||
|
||||
[Button](#button-component), [Slider](#slider-component), [CheckBox](#checkbox-component)
|
||||
[Button](#button-component), [Slider](#slider-component), [CheckBox](#checkbox-component), [Tabs](#tabs-component) ([Tab](#tab-component)), [EditBox](#editbox-component)
|
||||
|
||||
## [Examples](#examples)
|
||||
|
||||
|
|
@ -370,6 +370,56 @@ _optional value that will be set as the RadioGroup's value_
|
|||
|
||||
### A radio group. Place `<RadioBox>` components inside this.
|
||||
|
||||
## Tabs component
|
||||
|
||||
### `<Tabs>`
|
||||
|
||||
### A tab bar component
|
||||
|
||||
#### Parameters
|
||||
|
||||
`id`: **string**
|
||||
|
||||
_Unique identifier for the tab group_
|
||||
|
||||
Only `<Tab>` components should be placed inside `<Tabs>`.
|
||||
|
||||
---
|
||||
|
||||
## Tab component
|
||||
|
||||
### `<Tab>`
|
||||
|
||||
### A single tab inside a `<Tabs>` component
|
||||
|
||||
#### Parameters
|
||||
|
||||
`name`: **string**
|
||||
|
||||
_Unique identifier for the tab_
|
||||
|
||||
`translation`: **string**
|
||||
|
||||
_Translated by key_
|
||||
|
||||
`sprite_src` | `sprite_src_ext` | `sprite_src_builtin` | `sprite_src_internal`
|
||||
|
||||
_Image path (see [sprite](#sprite-widget)) for src descriptions_
|
||||
|
||||
---
|
||||
|
||||
## EditBox component
|
||||
|
||||
### `<EditBox>`
|
||||
|
||||
### A single-line text input field.
|
||||
|
||||
#### Parameters
|
||||
|
||||
`text`: **string**
|
||||
|
||||
_Initial text content_
|
||||
|
||||
---
|
||||
|
||||
# Examples
|
||||
|
|
|
|||
|
|
@ -349,6 +349,7 @@ fn register_event_mouse_press(state: Rc<RefCell<State>>, listeners: &mut EventLi
|
|||
common.alterables.trigger_haptics();
|
||||
common.alterables.play_sound(WguiSoundType::ButtonPress);
|
||||
common.alterables.mark_redraw();
|
||||
common.alterables.unfocus();
|
||||
|
||||
if state.hovered {
|
||||
state.down = true;
|
||||
|
|
@ -556,25 +557,19 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||
let base = ComponentBase {
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(id_rect).unwrap().state();
|
||||
let listeners = &mut root.widget.state().event_listeners;
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(
|
||||
data.clone(),
|
||||
state.clone(),
|
||||
&mut widget.event_listeners,
|
||||
params.tooltip,
|
||||
anim_mult,
|
||||
),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(data.clone(), state.clone(), listeners, params.tooltip, anim_mult),
|
||||
register_event_mouse_leave(state.clone(), listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), listeners),
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
let button = Rc::new(ComponentButton { base, data, state });
|
||||
|
||||
ess.layout.register_component_refresh(Component(button.clone()));
|
||||
ess.layout.register_component_refresh(&Component(button.clone()));
|
||||
Ok((root, button))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ fn register_event_mouse_press(state: Rc<RefCell<State>>, listeners: &mut EventLi
|
|||
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.mark_redraw();
|
||||
common.alterables.unfocus();
|
||||
|
||||
if state.hovered {
|
||||
state.down = true;
|
||||
|
|
@ -431,13 +432,13 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||
let base = ComponentBase {
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(id_container).unwrap().state();
|
||||
let listeners = &mut root.widget.state().event_listeners;
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(state.clone(), &mut widget.event_listeners, params.tooltip, anim_mult),
|
||||
register_event_mouse_leave(state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(state.clone(), listeners, params.tooltip, anim_mult),
|
||||
register_event_mouse_leave(state.clone(), listeners, anim_mult),
|
||||
register_event_mouse_press(state.clone(), listeners),
|
||||
register_event_mouse_release(data.clone(), state.clone(), listeners),
|
||||
]
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,421 @@
|
|||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use glam::FloatExt;
|
||||
use taffy::prelude::{auto, length, percent};
|
||||
|
||||
use crate::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::{Component, ComponentBase, ComponentTrait, FocusChangeData, RefreshData},
|
||||
drawing::{self, Color},
|
||||
event::{self, CallbackDataCommon, CallbackMetadata, EventListenerCollection, EventListenerKind, StyleSetRequest},
|
||||
i18n::Translation,
|
||||
layout::{WidgetID, WidgetPair},
|
||||
renderer_vk::text::{TextShadow, TextStyle},
|
||||
widget::{
|
||||
ConstructEssentials, EventResult,
|
||||
div::WidgetDiv,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Params {
|
||||
pub style: taffy::Style,
|
||||
pub initial_text: String,
|
||||
}
|
||||
|
||||
struct State {
|
||||
text: String,
|
||||
hovered: bool,
|
||||
focused: bool,
|
||||
focused_prev: bool,
|
||||
first_refresh: bool,
|
||||
self_ref: Weak<ComponentEditBox>,
|
||||
}
|
||||
|
||||
#[allow(clippy::struct_field_names)]
|
||||
struct Data {
|
||||
#[allow(dead_code)]
|
||||
id_rect_container: WidgetID,
|
||||
id_rect_bottom: WidgetID,
|
||||
id_rect_cursor: WidgetID,
|
||||
id_label: WidgetID,
|
||||
}
|
||||
|
||||
pub struct ComponentEditBox {
|
||||
base: ComponentBase,
|
||||
data: Rc<Data>,
|
||||
state: Rc<RefCell<State>>,
|
||||
}
|
||||
|
||||
fn anim_bottom_rect(
|
||||
common: &mut CallbackDataCommon,
|
||||
accent_color: drawing::Color,
|
||||
id_rect: WidgetID,
|
||||
anim_mult: f32,
|
||||
focused: bool,
|
||||
) {
|
||||
common.alterables.animate(Animation::new(
|
||||
id_rect,
|
||||
(10.0 * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
{
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
let pos_bidir = if focused { data.pos } else { 1.0 - data.pos };
|
||||
|
||||
rect.set_color(
|
||||
common,
|
||||
accent_color.lerp(&drawing::Color::new(1.0, 1.0, 1.0, 1.0), pos_bidir),
|
||||
);
|
||||
|
||||
common.alterables.set_style(
|
||||
data.widget_id,
|
||||
StyleSetRequest::Size(taffy::Size {
|
||||
width: percent(0.95.lerp(1.0, pos_bidir)),
|
||||
height: length(1.0 + pos_bidir),
|
||||
}),
|
||||
);
|
||||
|
||||
common.alterables.set_style(
|
||||
data.widget_id,
|
||||
StyleSetRequest::Margin(taffy::Rect {
|
||||
bottom: length(pos_bidir),
|
||||
left: auto(),
|
||||
right: auto(),
|
||||
top: auto(),
|
||||
}),
|
||||
);
|
||||
|
||||
common.alterables.mark_redraw();
|
||||
})
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn refresh_all(common: &mut CallbackDataCommon, data: &Data, state: &mut State) -> Option<()> {
|
||||
let defaults = common.defaults();
|
||||
let editbox_color = defaults.editbox_color;
|
||||
let anim_mult = defaults.animation_mult;
|
||||
let accent_color = defaults.accent_color;
|
||||
drop(defaults);
|
||||
|
||||
let (rect_color, border_color) = if state.focused {
|
||||
(editbox_color.add_rgb(0.15), editbox_color.add_rgb(0.15 + 0.25))
|
||||
} else if state.hovered {
|
||||
(editbox_color.add_rgb(0.1), editbox_color.add_rgb(0.1 + 0.15))
|
||||
} else {
|
||||
(editbox_color, editbox_color.add_rgb(0.15))
|
||||
};
|
||||
|
||||
// update background color
|
||||
let mut rect = common.state.widgets.get_as::<WidgetRectangle>(data.id_rect_container)?;
|
||||
rect.params.border_color = border_color;
|
||||
rect.set_color(common, rect_color);
|
||||
|
||||
if state.focused_prev != state.focused || state.first_refresh {
|
||||
anim_bottom_rect(common, accent_color, data.id_rect_bottom, anim_mult, state.focused);
|
||||
state.focused_prev = state.focused;
|
||||
}
|
||||
|
||||
// Cursor
|
||||
common.alterables.set_style(
|
||||
data.id_rect_cursor,
|
||||
StyleSetRequest::Display(if state.focused {
|
||||
taffy::Display::Flex
|
||||
} else {
|
||||
taffy::Display::None
|
||||
}),
|
||||
);
|
||||
|
||||
state.first_refresh = false;
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
impl ComponentTrait for ComponentEditBox {
|
||||
fn base(&self) -> &ComponentBase {
|
||||
&self.base
|
||||
}
|
||||
|
||||
fn base_mut(&mut self) -> &mut ComponentBase {
|
||||
&mut self.base
|
||||
}
|
||||
|
||||
fn refresh(&self, data: &mut RefreshData) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
let res = refresh_all(data.common, &self.data, &mut state);
|
||||
debug_assert!(res.is_some());
|
||||
}
|
||||
|
||||
fn on_focus_change(&self, data: &mut FocusChangeData) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.focused = data.focused;
|
||||
data.common.alterables.refresh_component_once(&state.self_ref);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_text(common: &mut CallbackDataCommon, state: &mut State, data: &Data, text: String) {
|
||||
let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(data.id_label) else {
|
||||
return;
|
||||
};
|
||||
|
||||
label.set_text(common, Translation::from_raw_text(&text));
|
||||
|
||||
common.alterables.refresh_component_once(&state.self_ref);
|
||||
|
||||
state.text = text;
|
||||
}
|
||||
|
||||
impl ComponentEditBox {
|
||||
pub fn set_text(&self, common: &mut CallbackDataCommon, text: &str) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
update_text(common, &mut state, &self.data, String::from(text));
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> Ref<'_, String> {
|
||||
Ref::map(self.state.borrow(), |x| &x.text)
|
||||
}
|
||||
}
|
||||
|
||||
fn register_event_text_input(
|
||||
state: Rc<RefCell<State>>,
|
||||
data: Rc<Data>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::TextInput,
|
||||
Box::new(move |common, evt, (), ()| {
|
||||
let mut state = state.borrow_mut();
|
||||
if !state.focused {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let CallbackMetadata::TextInput(input) = &evt.metadata else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let Some(input_text) = &input.text else {
|
||||
return Ok(EventResult::Pass); // nothing to do
|
||||
};
|
||||
|
||||
let Some(ch) = input_text.chars().next() else {
|
||||
return Ok(EventResult::Pass); // ???
|
||||
};
|
||||
|
||||
let mut new_text = std::mem::take(&mut state.text);
|
||||
|
||||
if ch == '\x08' {
|
||||
// Backspace
|
||||
new_text.pop();
|
||||
} else {
|
||||
let printable = !input_text.chars().any(char::is_control);
|
||||
if printable {
|
||||
new_text.push_str(input_text);
|
||||
}
|
||||
}
|
||||
|
||||
update_text(common, &mut state, &data, new_text);
|
||||
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_enter(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseEnter,
|
||||
Box::new(move |common, _evt, (), ()| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.hovered = true;
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.refresh_component_once(&state.self_ref);
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_press(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MousePress,
|
||||
Box::new(move |common, _evt, (), ()| {
|
||||
let state = state.borrow_mut();
|
||||
common.alterables.focus(&state.self_ref);
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.refresh_component_once(&state.self_ref);
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn register_event_mouse_leave(
|
||||
state: Rc<RefCell<State>>,
|
||||
listeners: &mut EventListenerCollection,
|
||||
) -> event::EventListenerID {
|
||||
listeners.register(
|
||||
EventListenerKind::MouseLeave,
|
||||
Box::new(move |common, _evt, (), ()| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.hovered = false;
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.refresh_component_once(&state.self_ref);
|
||||
Ok(EventResult::Pass)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn construct(
|
||||
ess: &mut ConstructEssentials,
|
||||
mut params: Params,
|
||||
) -> anyhow::Result<(WidgetPair, Rc<ComponentEditBox>)> {
|
||||
let globals = ess.layout.state.globals.clone();
|
||||
let defaults = globals.defaults();
|
||||
let text_color = defaults.text_color;
|
||||
drop(defaults);
|
||||
|
||||
if params.style.size.width.is_auto() {
|
||||
params.style.size.width = length(128.0);
|
||||
}
|
||||
|
||||
if params.style.size.height.is_auto() {
|
||||
params.style.size.height = length(32.0);
|
||||
}
|
||||
|
||||
// override style
|
||||
params.style.align_items = Some(taffy::AlignItems::Center);
|
||||
params.style.position = taffy::Position::Relative;
|
||||
params.style.overflow = taffy::Point {
|
||||
x: taffy::Overflow::Scroll,
|
||||
y: taffy::Overflow::Visible,
|
||||
};
|
||||
params.style.min_size = params.style.max_size;
|
||||
|
||||
let (root, _) = ess.layout.add_child(
|
||||
ess.parent,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
border: 2.0,
|
||||
round: WLength::Units(3.0),
|
||||
..Default::default()
|
||||
}),
|
||||
params.style,
|
||||
)?;
|
||||
|
||||
// for centering
|
||||
let (rect_bottom_parent, _) = ess.layout.add_child(
|
||||
root.id,
|
||||
WidgetDiv::create(),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Absolute,
|
||||
flex_direction: taffy::FlexDirection::Column,
|
||||
align_content: Some(taffy::AlignContent::Center),
|
||||
align_items: Some(taffy::AlignItems::Center),
|
||||
size: taffy::Size {
|
||||
width: percent(1.0),
|
||||
height: percent(1.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let (rect_bottom, _) = ess.layout.add_child(
|
||||
rect_bottom_parent.id,
|
||||
WidgetRectangle::create(Default::default()),
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
let id_container = root.id;
|
||||
|
||||
let (label_parent, _) = ess.layout.add_child(
|
||||
root.id,
|
||||
WidgetDiv::create(),
|
||||
taffy::Style {
|
||||
padding: taffy::Rect::length(8.0),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let (label, _node_label) = ess.layout.add_child(
|
||||
label_parent.id,
|
||||
WidgetLabel::create(
|
||||
&mut globals.get(),
|
||||
WidgetLabelParams {
|
||||
content: Translation::from_raw_text(¶ms.initial_text),
|
||||
style: TextStyle {
|
||||
shadow: Some(TextShadow {
|
||||
x: 1.0,
|
||||
y: 1.0,
|
||||
color: Color::new(0.0, 0.0, 0.0, 1.0),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Relative,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let (rect_cursor, _) = ess.layout.add_child(
|
||||
label_parent.id,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: text_color.with_alpha(0.75),
|
||||
..Default::default()
|
||||
}),
|
||||
taffy::Style {
|
||||
align_self: Some(taffy::AlignSelf::Center),
|
||||
justify_self: Some(taffy::JustifySelf::End),
|
||||
min_size: taffy::Size {
|
||||
width: length(2.0),
|
||||
height: length(16.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let data = Rc::new(Data {
|
||||
id_rect_container: id_container,
|
||||
id_label: label.id,
|
||||
id_rect_bottom: rect_bottom.id,
|
||||
id_rect_cursor: rect_cursor.id,
|
||||
});
|
||||
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
self_ref: Weak::new(),
|
||||
text: params.initial_text,
|
||||
hovered: false,
|
||||
focused: false,
|
||||
focused_prev: false,
|
||||
first_refresh: true,
|
||||
}));
|
||||
|
||||
let base = ComponentBase {
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut root_state = root.widget.state();
|
||||
vec![
|
||||
register_event_mouse_enter(state.clone(), &mut root_state.event_listeners),
|
||||
register_event_mouse_leave(state.clone(), &mut root_state.event_listeners),
|
||||
register_event_mouse_press(state.clone(), &mut root_state.event_listeners),
|
||||
register_event_text_input(state.clone(), data.clone(), &mut root_state.event_listeners),
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
let editbox = Rc::new(ComponentEditBox { base, data, state });
|
||||
editbox.state.borrow_mut().self_ref = Rc::downgrade(&editbox);
|
||||
|
||||
ess.layout.defer_component_refresh(Component(editbox.clone()));
|
||||
Ok((root, editbox))
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ use crate::{
|
|||
|
||||
pub mod button;
|
||||
pub mod checkbox;
|
||||
pub mod editbox;
|
||||
pub mod radio_group;
|
||||
pub mod slider;
|
||||
pub mod tabs;
|
||||
|
|
@ -18,6 +19,11 @@ pub struct RefreshData<'a> {
|
|||
pub common: &'a mut CallbackDataCommon<'a>,
|
||||
}
|
||||
|
||||
pub struct FocusChangeData<'a> {
|
||||
pub common: &'a mut CallbackDataCommon<'a>,
|
||||
pub focused: bool,
|
||||
}
|
||||
|
||||
// common component data
|
||||
#[derive(Default)]
|
||||
pub struct ComponentBase {
|
||||
|
|
@ -36,6 +42,7 @@ pub trait ComponentTrait: AnyTrait {
|
|||
fn base(&self) -> &ComponentBase;
|
||||
fn base_mut(&mut self) -> &mut ComponentBase;
|
||||
fn refresh(&self, data: &mut RefreshData);
|
||||
fn on_focus_change(&self, _data: &mut FocusChangeData) {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
|||
|
|
@ -324,7 +324,6 @@ fn register_event_mouse_enter(
|
|||
common.alterables.trigger_haptics();
|
||||
state.borrow_mut().hovered = true;
|
||||
on_enter_anim(common, data.slider_handle_rect_id, anim_mult);
|
||||
|
||||
ComponentTooltip::register_hover_in(common, &tooltip_info, event_data.widget_id, state.clone());
|
||||
|
||||
Ok(EventResult::Pass)
|
||||
|
|
@ -388,6 +387,7 @@ fn register_event_mouse_press(
|
|||
EventListenerKind::MousePress,
|
||||
Box::new(move |common, event_data, (), ()| {
|
||||
common.alterables.trigger_haptics();
|
||||
common.alterables.unfocus();
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
let CallbackMetadata::MouseButton(btn) = event_data.metadata else {
|
||||
|
|
@ -534,26 +534,20 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||
let base = ComponentBase {
|
||||
id: root.id,
|
||||
lhandles: {
|
||||
let mut widget = ess.layout.state.widgets.get(body_id).unwrap().state();
|
||||
let listeners = &mut root.widget.state().event_listeners;
|
||||
let anim_mult = ess.layout.state.globals.defaults().animation_mult;
|
||||
vec![
|
||||
register_event_mouse_enter(
|
||||
data.clone(),
|
||||
state.clone(),
|
||||
&mut widget.event_listeners,
|
||||
params.tooltip,
|
||||
anim_mult,
|
||||
),
|
||||
register_event_mouse_leave(data.clone(), state.clone(), &mut widget.event_listeners, anim_mult),
|
||||
register_event_mouse_motion(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_press(data.clone(), state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_release(state.clone(), &mut widget.event_listeners),
|
||||
register_event_mouse_enter(data.clone(), state.clone(), listeners, params.tooltip, anim_mult),
|
||||
register_event_mouse_leave(data.clone(), state.clone(), listeners, anim_mult),
|
||||
register_event_mouse_motion(data.clone(), state.clone(), listeners),
|
||||
register_event_mouse_press(data.clone(), state.clone(), listeners),
|
||||
register_event_mouse_release(state.clone(), listeners),
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
let slider = Rc::new(ComponentSlider { base, data, state });
|
||||
|
||||
ess.layout.register_component_refresh(Component(slider.clone()));
|
||||
ess.layout.register_component_refresh(&Component(slider.clone()));
|
||||
Ok((root, slider))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,6 @@ use crate::{
|
|||
|
||||
use super::{layout::Layout, widget::DrawState};
|
||||
|
||||
pub struct ImageHandle {
|
||||
// to be implemented, will contain pixel data (RGB or RGBA) loaded via "ImageBank" or something by the gui
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct Boundary {
|
||||
pub pos: Vec2,
|
||||
|
|
@ -254,14 +250,19 @@ pub fn has_overflow_clip(style: &taffy::Style) -> bool {
|
|||
style.overflow.x != taffy::Overflow::Visible || style.overflow.y != taffy::Overflow::Visible
|
||||
}
|
||||
|
||||
fn primitive_debug_rect(boundary: &Boundary, transform: &Mat4, color: drawing::Color) -> drawing::RenderPrimitive {
|
||||
fn primitive_debug_rect(
|
||||
boundary: &Boundary,
|
||||
transform: &Mat4,
|
||||
color: drawing::Color,
|
||||
border: f32,
|
||||
) -> drawing::RenderPrimitive {
|
||||
drawing::RenderPrimitive::Rectangle(
|
||||
PrimitiveExtent {
|
||||
boundary: *boundary,
|
||||
transform: *transform,
|
||||
},
|
||||
Rectangle {
|
||||
border: 1.0,
|
||||
border,
|
||||
border_color: color,
|
||||
color: Color::new(0.0, 0.0, 0.0, 0.0),
|
||||
..Default::default()
|
||||
|
|
@ -277,12 +278,16 @@ pub fn push_transform_stack(
|
|||
) {
|
||||
let raw_dim = Vec2::new(l.size.width, l.size.height);
|
||||
let visual_dim = raw_dim + scroll_shift;
|
||||
let content_dim = Vec2::new(l.content_box_width(), l.content_box_height());
|
||||
let content_rel_pos = Vec2::new(l.content_box_x(), l.content_box_y());
|
||||
|
||||
transform_stack.push(stack::Transform {
|
||||
rel_pos: Vec2::new(l.location.x, l.location.y) - scroll_shift,
|
||||
transform: widget_state.data.transform,
|
||||
raw_dim,
|
||||
visual_dim,
|
||||
content_dim,
|
||||
content_rel_pos,
|
||||
abs_pos: Default::default(),
|
||||
transform_rel: Default::default(),
|
||||
});
|
||||
|
|
@ -301,29 +306,33 @@ impl PushScissorStackResult {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if scissor has been pushed.
|
||||
/// Returns Some() if scissor has been pushed.
|
||||
pub fn push_scissor_stack(
|
||||
transform_stack: &mut TransformStack,
|
||||
scissor_stack: &mut ScissorStack,
|
||||
scroll_shift: Vec2,
|
||||
info: &Option<ScrollbarInfo>,
|
||||
style: &taffy::Style,
|
||||
) -> PushScissorStackResult {
|
||||
) -> Option<PushScissorStackResult> {
|
||||
let mut boundary_absolute = drawing::Boundary::construct_absolute(transform_stack);
|
||||
boundary_absolute.pos += scroll_shift;
|
||||
|
||||
let do_clip = info.is_some() && has_overflow_clip(style);
|
||||
|
||||
if !do_clip {
|
||||
return None; // Don't care
|
||||
}
|
||||
|
||||
scissor_stack.push(ScissorBoundary(boundary_absolute));
|
||||
|
||||
if scissor_stack.is_out_of_bounds() {
|
||||
return PushScissorStackResult::OutOfBounds;
|
||||
return Some(PushScissorStackResult::OutOfBounds);
|
||||
}
|
||||
|
||||
if do_clip {
|
||||
PushScissorStackResult::VisibleAndClip
|
||||
Some(PushScissorStackResult::VisibleAndClip)
|
||||
} else {
|
||||
PushScissorStackResult::VisibleDontClip
|
||||
Some(PushScissorStackResult::VisibleDontClip)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,14 +377,16 @@ fn draw_widget(
|
|||
&boundary,
|
||||
&state.transform_stack.get().transform,
|
||||
Color::new(0.0, 1.0, 1.0, 0.5),
|
||||
1.0,
|
||||
));
|
||||
}
|
||||
|
||||
let starting_scissor_set_count = internal.scissor_set_count;
|
||||
|
||||
let scissor_result = push_scissor_stack(state.transform_stack, state.scissor_stack, scroll_shift, &info, style);
|
||||
|
||||
if scissor_result == PushScissorStackResult::VisibleAndClip {
|
||||
if let Some(scissor_result) = &scissor_result
|
||||
&& *scissor_result == PushScissorStackResult::VisibleAndClip
|
||||
{
|
||||
if params.debug_draw {
|
||||
let mut boundary_relative = drawing::Boundary::construct_relative(state.transform_stack);
|
||||
boundary_relative.pos += scroll_shift;
|
||||
|
|
@ -383,6 +394,7 @@ fn draw_widget(
|
|||
&boundary_relative,
|
||||
&state.transform_stack.get().transform,
|
||||
Color::new(1.0, 0.0, 1.0, 1.0),
|
||||
3.0,
|
||||
));
|
||||
}
|
||||
state
|
||||
|
|
@ -397,12 +409,17 @@ fn draw_widget(
|
|||
style,
|
||||
};
|
||||
|
||||
if scissor_result.should_display() {
|
||||
if scissor_result
|
||||
.as_ref()
|
||||
.is_none_or(PushScissorStackResult::should_display)
|
||||
{
|
||||
widget_state.draw_all(state, &draw_params);
|
||||
draw_children(params, state, node_id, internal, false);
|
||||
}
|
||||
|
||||
state.scissor_stack.pop();
|
||||
if scissor_result.is_some() {
|
||||
state.scissor_stack.pop();
|
||||
}
|
||||
|
||||
let current_scissor_set_count = internal.scissor_set_count;
|
||||
|
||||
|
|
@ -412,12 +429,12 @@ fn draw_widget(
|
|||
.push(drawing::RenderPrimitive::ScissorSet(*state.scissor_stack.get()));
|
||||
}
|
||||
|
||||
state.transform_stack.pop();
|
||||
|
||||
if let Some(info) = &info {
|
||||
widget_state.draw_scrollbars(state, &draw_params, info);
|
||||
}
|
||||
|
||||
state.transform_stack.pop();
|
||||
|
||||
if wants_redraw {
|
||||
state.alterables.mark_redraw();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::{
|
|||
any::{Any, TypeId},
|
||||
cell::{Ref, RefMut},
|
||||
collections::HashSet,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use glam::Vec2;
|
||||
|
|
@ -9,9 +10,10 @@ use slotmap::{DenseSlotMap, new_key_type};
|
|||
|
||||
use crate::{
|
||||
animation::{self, Animation},
|
||||
components::{ComponentTrait, ComponentWeak},
|
||||
globals,
|
||||
i18n::I18n,
|
||||
layout::{LayoutState, LayoutTask, WidgetID},
|
||||
layout::{LayoutDispatchFunc, LayoutState, LayoutTask, WidgetID},
|
||||
sound::WguiSoundType,
|
||||
stack::{ScissorStack, Transform, TransformStack},
|
||||
widget::{EventResult, WidgetData, WidgetObj},
|
||||
|
|
@ -25,7 +27,7 @@ pub enum MouseButtonIndex {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MouseButton {
|
||||
pub struct MouseButtonEvent {
|
||||
pub index: MouseButtonIndex,
|
||||
pub pos: Vec2,
|
||||
pub device: usize,
|
||||
|
|
@ -37,12 +39,6 @@ pub struct MousePosition {
|
|||
pub device: usize,
|
||||
}
|
||||
|
||||
pub struct MouseDownEvent {
|
||||
pub pos: Vec2,
|
||||
pub index: MouseButtonIndex,
|
||||
pub device: usize,
|
||||
}
|
||||
|
||||
pub struct MouseLeaveEvent {
|
||||
pub device: usize,
|
||||
}
|
||||
|
|
@ -52,29 +48,29 @@ pub struct MouseMotionEvent {
|
|||
pub device: usize,
|
||||
}
|
||||
|
||||
pub struct MouseUpEvent {
|
||||
pub pos: Vec2,
|
||||
pub index: MouseButtonIndex,
|
||||
pub device: usize,
|
||||
}
|
||||
|
||||
pub struct MouseWheelEvent {
|
||||
pub pos: Vec2, /* mouse position */
|
||||
pub delta: Vec2, /* wheel delta */
|
||||
pub device: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextInputEvent {
|
||||
pub text: Option<Rc<str>>,
|
||||
}
|
||||
|
||||
pub struct InternalStateChangeEvent {
|
||||
pub metadata: usize,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
InternalStateChange(InternalStateChangeEvent),
|
||||
MouseDown(MouseDownEvent),
|
||||
MouseDown(MouseButtonEvent),
|
||||
MouseLeave(MouseLeaveEvent),
|
||||
MouseMotion(MouseMotionEvent),
|
||||
MouseUp(MouseUpEvent),
|
||||
MouseUp(MouseButtonEvent),
|
||||
MouseWheel(MouseWheelEvent),
|
||||
TextInput(TextInputEvent),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
|
|
@ -101,12 +97,14 @@ pub enum StyleSetRequest {
|
|||
Margin(taffy::Rect<taffy::LengthPercentageAuto>),
|
||||
Width(taffy::Dimension),
|
||||
Height(taffy::Dimension),
|
||||
Size(taffy::Size<taffy::Dimension>),
|
||||
}
|
||||
|
||||
// alterables which will be dispatched in the next loop iteration phase
|
||||
#[derive(Default)]
|
||||
pub struct EventAlterables {
|
||||
pub dirty_widgets: Vec<WidgetID>,
|
||||
pub components_to_refresh_once: Vec<ComponentWeak>,
|
||||
pub style_set_requests: Vec<(WidgetID, StyleSetRequest)>,
|
||||
pub animations: Vec<animation::Animation>,
|
||||
pub widgets_to_tick: HashSet<WidgetID>, // widgets which needs to be ticked in the next `Layout::update()` fn
|
||||
|
|
@ -147,8 +145,20 @@ impl EventAlterables {
|
|||
self.tasks.push(LayoutTask::PlaySound(sound_type));
|
||||
}
|
||||
|
||||
pub fn dispatch(&mut self, func: Box<dyn FnOnce(&mut CallbackDataCommon) -> anyhow::Result<()>>) {
|
||||
self.tasks.push(LayoutTask::Dispatch(func))
|
||||
pub fn dispatch(&mut self, func: LayoutDispatchFunc) {
|
||||
self.tasks.push(LayoutTask::Dispatch(func));
|
||||
}
|
||||
|
||||
pub fn focus<ComponentType: ComponentTrait>(&mut self, component: &Weak<ComponentType>) {
|
||||
self.tasks.push(LayoutTask::SetFocus(component.clone()));
|
||||
}
|
||||
|
||||
pub fn unfocus(&mut self) {
|
||||
self.tasks.push(LayoutTask::Unfocus);
|
||||
}
|
||||
|
||||
pub fn refresh_component_once<ComponentType: ComponentTrait>(&mut self, component: &Weak<ComponentType>) {
|
||||
self.components_to_refresh_once.push(component.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,8 +197,9 @@ pub struct CallbackData<'a> {
|
|||
|
||||
pub enum CallbackMetadata {
|
||||
None,
|
||||
MouseButton(MouseButton),
|
||||
MouseButton(MouseButtonEvent),
|
||||
MousePosition(MousePosition),
|
||||
TextInput(TextInputEvent),
|
||||
Custom(usize),
|
||||
}
|
||||
|
||||
|
|
@ -197,9 +208,10 @@ impl CallbackMetadata {
|
|||
|
||||
pub const fn get_mouse_pos_absolute(&self) -> Option<Vec2> {
|
||||
match *self {
|
||||
Self::MouseButton(b) => Some(b.pos),
|
||||
Self::MousePosition(b) => Some(b.pos),
|
||||
Self::Custom(_) | Self::None => None,
|
||||
CallbackMetadata::MouseButton(b) => Some(b.pos),
|
||||
CallbackMetadata::MousePosition(b) => Some(b.pos),
|
||||
CallbackMetadata::Custom(_) | CallbackMetadata::None => None,
|
||||
CallbackMetadata::TextInput(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,6 +228,7 @@ pub enum EventListenerKind {
|
|||
MouseEnter,
|
||||
MouseMotion,
|
||||
MouseLeave,
|
||||
TextInput,
|
||||
InternalStateChange,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ pub struct Defaults {
|
|||
pub danger_color: drawing::Color,
|
||||
pub faded_color: drawing::Color,
|
||||
pub bg_color: drawing::Color,
|
||||
pub editbox_color: drawing::Color,
|
||||
pub translucent_alpha: f32,
|
||||
pub animation_mult: f32,
|
||||
pub rounding_mult: f32,
|
||||
|
|
@ -42,6 +43,7 @@ impl Default for Defaults {
|
|||
danger_color: drawing::Color::new(0.9, 0.0, 0.0, 1.0),
|
||||
faded_color: drawing::Color::new(0.67, 0.74, 0.80, 1.0),
|
||||
bg_color: drawing::Color::new(0.0, 0.07, 0.1, 0.75),
|
||||
editbox_color: drawing::Color::new(0.15, 0.25, 0.35, 0.95),
|
||||
translucent_alpha: 0.5,
|
||||
animation_mult: 1.0,
|
||||
rounding_mult: 1.0,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
animation::Animations,
|
||||
components::{Component, RefreshData},
|
||||
drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack},
|
||||
components::{self, Component, ComponentWeak, FocusChangeData, RefreshData},
|
||||
drawing::{
|
||||
self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, PushScissorStackResult, push_scissor_stack, push_transform_stack,
|
||||
},
|
||||
event::{self, CallbackDataCommon, EventAlterables},
|
||||
globals::WguiGlobals,
|
||||
sound::WguiSoundType,
|
||||
|
|
@ -18,7 +20,7 @@ use crate::{
|
|||
|
||||
use anyhow::Context;
|
||||
use glam::{Vec2, vec2};
|
||||
use slotmap::{HopSlotMap, SecondaryMap, new_key_type};
|
||||
use slotmap::{SecondaryMap, SlotMap, new_key_type};
|
||||
use taffy::{NodeId, TaffyTree, TraversePartialTree};
|
||||
|
||||
new_key_type! {
|
||||
|
|
@ -57,7 +59,7 @@ impl WeakWidget {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WidgetMap(HopSlotMap<WidgetID, Widget>);
|
||||
pub struct WidgetMap(SlotMap<WidgetID, Widget>);
|
||||
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -68,7 +70,7 @@ pub struct WidgetPair {
|
|||
|
||||
impl WidgetMap {
|
||||
fn new() -> Self {
|
||||
Self(HopSlotMap::with_key())
|
||||
Self(SlotMap::<WidgetID, Widget>::with_key())
|
||||
}
|
||||
|
||||
pub fn get_as<T: 'static>(&self, handle: WidgetID) -> Option<RefMut<'_, T>> {
|
||||
|
|
@ -134,14 +136,17 @@ pub struct LayoutUpdateResult {
|
|||
pub sounds_to_play: Vec<WguiSoundType>,
|
||||
}
|
||||
|
||||
pub type ModifyLayoutStateFunc = Box<dyn FnOnce(ModifyLayoutStateData) -> anyhow::Result<()>>;
|
||||
pub type LayoutModifyStateFunc = Box<dyn FnOnce(ModifyLayoutStateData) -> anyhow::Result<()>>;
|
||||
pub type LayoutDispatchFunc = Box<dyn FnOnce(&mut CallbackDataCommon) -> anyhow::Result<()>>;
|
||||
|
||||
pub enum LayoutTask {
|
||||
RemoveWidget(WidgetID),
|
||||
SetWidgetStyle(WidgetID, event::StyleSetRequest),
|
||||
ModifyLayoutState(ModifyLayoutStateFunc),
|
||||
ModifyLayoutState(LayoutModifyStateFunc),
|
||||
PlaySound(WguiSoundType),
|
||||
Dispatch(Box<dyn FnOnce(&mut CallbackDataCommon) -> anyhow::Result<()>>),
|
||||
Dispatch(LayoutDispatchFunc),
|
||||
SetFocus(ComponentWeak),
|
||||
Unfocus,
|
||||
}
|
||||
|
||||
pub type LayoutTasks = Tasks<LayoutTask>;
|
||||
|
|
@ -152,8 +157,9 @@ pub struct Layout {
|
|||
pub tasks: LayoutTasks,
|
||||
|
||||
components_to_refresh_once: HashSet<Component>,
|
||||
registered_components_to_refresh: HashMap<taffy::NodeId, Component>,
|
||||
registered_components_to_refresh: HashMap<taffy::NodeId, ComponentWeak>,
|
||||
sounds_to_play_once: Vec<WguiSoundType>,
|
||||
focused_component: Option<ComponentWeak>,
|
||||
|
||||
pub widgets_to_tick: Vec<WidgetID>,
|
||||
|
||||
|
|
@ -358,14 +364,14 @@ impl Layout {
|
|||
}
|
||||
|
||||
// call ComponentTrait::refresh() *every time time* the layout is dirty
|
||||
pub fn register_component_refresh(&mut self, component: Component) {
|
||||
pub fn register_component_refresh(&mut self, component: &Component) {
|
||||
let widget_id = component.0.base().get_id();
|
||||
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
||||
debug_assert!(false);
|
||||
return;
|
||||
};
|
||||
|
||||
self.registered_components_to_refresh.insert(*node_id, component);
|
||||
self.registered_components_to_refresh.insert(*node_id, component.weak());
|
||||
}
|
||||
|
||||
/// Convenience function to avoid repeated `WidgetID` → `WidgetState` lookups.
|
||||
|
|
@ -455,7 +461,10 @@ impl Layout {
|
|||
style,
|
||||
);
|
||||
|
||||
if scissor_result.should_display() {
|
||||
if scissor_result
|
||||
.as_ref()
|
||||
.is_none_or(PushScissorStackResult::should_display)
|
||||
{
|
||||
// check children first
|
||||
self.push_event_children(node_id, event, event_result, alterables, user_data)?;
|
||||
|
||||
|
|
@ -472,7 +481,9 @@ impl Layout {
|
|||
}
|
||||
}
|
||||
|
||||
alterables.scissor_stack.pop();
|
||||
if scissor_result.is_some() {
|
||||
alterables.scissor_stack.pop();
|
||||
}
|
||||
alterables.transform_stack.pop();
|
||||
|
||||
Ok(())
|
||||
|
|
@ -581,6 +592,7 @@ impl Layout {
|
|||
widgets_to_tick: Vec::new(),
|
||||
tasks: LayoutTasks::new(),
|
||||
sounds_to_play_once: Vec::new(),
|
||||
focused_component: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -590,8 +602,10 @@ impl Layout {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(component) = self.registered_components_to_refresh.get(&node_id) {
|
||||
to_refresh.push(component.clone());
|
||||
if let Some(component) = self.registered_components_to_refresh.get(&node_id)
|
||||
&& let Some(c) = component.upgrade()
|
||||
{
|
||||
to_refresh.push(Component(c));
|
||||
}
|
||||
|
||||
for child_id in self.state.tree.child_ids(node_id) {
|
||||
|
|
@ -708,15 +722,48 @@ impl Layout {
|
|||
c.finish()?;
|
||||
}
|
||||
LayoutTask::SetWidgetStyle(widget_id, style_request) => {
|
||||
self.set_style_request(widget_id, style_request);
|
||||
self.set_style_request(widget_id, &style_request);
|
||||
}
|
||||
LayoutTask::SetFocus(weak) => {
|
||||
if let Some(c) = weak.upgrade() {
|
||||
self.set_focus(Some(&components::Component(c)))?;
|
||||
}
|
||||
}
|
||||
LayoutTask::Unfocus => self.set_focus(None)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_style_request(&mut self, widget_id: WidgetID, style_request: event::StyleSetRequest) {
|
||||
pub fn set_focus(&mut self, to_focus: Option<&Component>) -> anyhow::Result<()> {
|
||||
let mut c = self.start_common();
|
||||
|
||||
if let Some(focused) = &c.layout.focused_component
|
||||
&& let Some(focused) = focused.upgrade()
|
||||
{
|
||||
// Unfocus
|
||||
focused.on_focus_change(&mut FocusChangeData {
|
||||
common: &mut c.common(),
|
||||
focused: false,
|
||||
});
|
||||
c.layout.focused_component = None;
|
||||
}
|
||||
|
||||
if let Some(to_focus) = to_focus {
|
||||
to_focus.0.on_focus_change(&mut FocusChangeData {
|
||||
common: &mut c.common(),
|
||||
focused: true,
|
||||
});
|
||||
|
||||
c.layout.focused_component = Some(to_focus.weak());
|
||||
}
|
||||
|
||||
c.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_style_request(&mut self, widget_id: WidgetID, style_request: &event::StyleSetRequest) {
|
||||
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -728,22 +775,26 @@ impl Layout {
|
|||
match style_request {
|
||||
event::StyleSetRequest::Display(display) => {
|
||||
// refresh the component in case if visibility/display mode has changed
|
||||
if cur_style.display != display
|
||||
if cur_style.display != *display
|
||||
&& let Some(component) = self.registered_components_to_refresh.get(node_id)
|
||||
&& let Some(c) = component.upgrade()
|
||||
{
|
||||
self.components_to_refresh_once.insert(component.clone());
|
||||
self.components_to_refresh_once.insert(Component(c));
|
||||
}
|
||||
|
||||
cur_style.display = display;
|
||||
cur_style.display = *display;
|
||||
}
|
||||
event::StyleSetRequest::Margin(margin) => {
|
||||
cur_style.margin = margin;
|
||||
cur_style.margin = *margin;
|
||||
}
|
||||
event::StyleSetRequest::Width(val) => {
|
||||
cur_style.size.width = val;
|
||||
cur_style.size.width = *val;
|
||||
}
|
||||
event::StyleSetRequest::Height(val) => {
|
||||
cur_style.size.height = val;
|
||||
cur_style.size.height = *val;
|
||||
}
|
||||
event::StyleSetRequest::Size(size) => {
|
||||
cur_style.size = *size;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -786,8 +837,14 @@ impl Layout {
|
|||
}
|
||||
}
|
||||
|
||||
for (widget_id, style_request) in alterables.style_set_requests {
|
||||
self.set_style_request(widget_id, style_request);
|
||||
for c in alterables.components_to_refresh_once {
|
||||
if let Some(c) = c.upgrade() {
|
||||
self.defer_component_refresh(components::Component(c));
|
||||
}
|
||||
}
|
||||
|
||||
for (widget_id, style_request) in &alterables.style_set_requests {
|
||||
self.set_style_request(*widget_id, style_request);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
clippy::needless_pass_by_ref_mut,
|
||||
clippy::use_self,
|
||||
clippy::match_same_arms,
|
||||
clippy::too_many_lines
|
||||
clippy::too_many_lines,
|
||||
clippy::struct_excessive_bools
|
||||
)]
|
||||
|
||||
pub mod animation;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
use crate::{
|
||||
components::{Component, editbox},
|
||||
layout::WidgetID,
|
||||
parser::{AttribPair, ParserContext, process_component, style::parse_style},
|
||||
widget::ConstructEssentials,
|
||||
};
|
||||
|
||||
pub fn parse_component_editbox(
|
||||
ctx: &mut ParserContext,
|
||||
parent_id: WidgetID,
|
||||
attribs: &[AttribPair],
|
||||
tag_name: &str,
|
||||
) -> anyhow::Result<WidgetID> {
|
||||
let mut initial_text = String::new();
|
||||
|
||||
let style = parse_style(ctx, attribs, tag_name);
|
||||
|
||||
for pair in attribs {
|
||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||
#[allow(clippy::single_match)]
|
||||
match key {
|
||||
"text" => {
|
||||
initial_text = String::from(value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let (widget, component) = editbox::construct(
|
||||
&mut ConstructEssentials {
|
||||
layout: ctx.layout,
|
||||
parent: parent_id,
|
||||
},
|
||||
editbox::Params { style, initial_text },
|
||||
)?;
|
||||
|
||||
process_component(ctx, Component(component), widget.id, attribs);
|
||||
|
||||
Ok(widget.id)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod component_button;
|
||||
mod component_checkbox;
|
||||
mod component_editbox;
|
||||
mod component_radio_group;
|
||||
mod component_slider;
|
||||
mod component_tabs;
|
||||
|
|
@ -22,6 +23,7 @@ use crate::{
|
|||
parser::{
|
||||
component_button::parse_component_button,
|
||||
component_checkbox::{CheckboxKind, parse_component_checkbox},
|
||||
component_editbox::parse_component_editbox,
|
||||
component_radio_group::parse_component_radio_group,
|
||||
component_slider::parse_component_slider,
|
||||
component_tabs::parse_component_tabs,
|
||||
|
|
@ -1053,6 +1055,7 @@ fn parse_child<'a>(
|
|||
file, ctx, child_node, parent_id, &attribs, tag_name,
|
||||
)?);
|
||||
}
|
||||
"EditBox" => new_widget_id = Some(parse_component_editbox(ctx, parent_id, &attribs, tag_name)?),
|
||||
"Tabs" => {
|
||||
new_widget_id = Some(parse_component_tabs(ctx, child_node, parent_id, &attribs, tag_name)?);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ impl<T: StackItem<T>, const STACK_MAX: usize> GenericStack<T, STACK_MAX> {
|
|||
pub const fn get(&self) -> &T {
|
||||
&self.stack[(self.top - 1) as usize]
|
||||
}
|
||||
|
||||
pub const fn parent(&self) -> &T {
|
||||
debug_assert!(self.top >= 2);
|
||||
&self.stack[(self.top - 2) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StackItem<T>, const STACK_MAX: usize> Default for GenericStack<T, STACK_MAX> {
|
||||
|
|
@ -54,9 +59,11 @@ impl<T: StackItem<T>, const STACK_MAX: usize> Default for GenericStack<T, STACK_
|
|||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Transform {
|
||||
pub rel_pos: Vec2,
|
||||
pub visual_dim: Vec2, // for convenience
|
||||
pub raw_dim: Vec2, // for convenience
|
||||
pub abs_pos: Vec2, // for convenience, will be set after pushing
|
||||
pub visual_dim: Vec2,
|
||||
pub raw_dim: Vec2,
|
||||
pub content_rel_pos: Vec2,
|
||||
pub content_dim: Vec2,
|
||||
pub abs_pos: Vec2, // for convenience, will be set after pushing
|
||||
pub transform: glam::Mat4,
|
||||
pub transform_rel: glam::Mat4,
|
||||
}
|
||||
|
|
@ -68,6 +75,8 @@ impl Default for Transform {
|
|||
rel_pos: Default::default(),
|
||||
visual_dim: Default::default(),
|
||||
raw_dim: Default::default(),
|
||||
content_dim: Default::default(),
|
||||
content_rel_pos: Default::default(),
|
||||
transform: Mat4::IDENTITY,
|
||||
transform_rel: Default::default(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,6 +311,8 @@ impl WidgetState {
|
|||
}
|
||||
|
||||
pub fn get_scroll_shift_smooth(&self, info: &ScrollbarInfo, l: &taffy::Layout, timestep_alpha: f32) -> (Vec2, bool) {
|
||||
//return (self.get_scroll_shift_raw(info, l), false);
|
||||
|
||||
let currently_animating = self.data.scrolling_cur != self.data.scrolling_cur_prev;
|
||||
|
||||
let scrolling = self
|
||||
|
|
@ -368,7 +370,8 @@ impl WidgetState {
|
|||
return;
|
||||
}
|
||||
|
||||
let transform = state.transform_stack.get();
|
||||
let transform_widget = state.transform_stack.get();
|
||||
let transform_parent = state.transform_stack.parent();
|
||||
|
||||
let thickness = 6.0;
|
||||
let margin = 6.0;
|
||||
|
|
@ -385,12 +388,15 @@ impl WidgetState {
|
|||
PrimitiveExtent {
|
||||
boundary: drawing::Boundary::from_pos_size(
|
||||
Vec2::new(
|
||||
transform.rel_pos.x + transform.raw_dim.x * (1.0 - info.handle_size.x) * self.data.scrolling_cur.x,
|
||||
transform.rel_pos.y + transform.raw_dim.y - thickness - margin,
|
||||
// X
|
||||
transform_widget.content_rel_pos.x
|
||||
+ transform_parent.raw_dim.x * (1.0 - info.handle_size.x) * self.data.scrolling_cur.x,
|
||||
// Y
|
||||
transform_widget.content_rel_pos.y + transform_widget.raw_dim.y - thickness - margin,
|
||||
),
|
||||
Vec2::new(transform.raw_dim.x * info.handle_size.x, thickness),
|
||||
Vec2::new(transform_widget.raw_dim.x * info.handle_size.x, thickness),
|
||||
),
|
||||
transform: transform.transform,
|
||||
transform: transform_parent.transform,
|
||||
},
|
||||
rect_params,
|
||||
));
|
||||
|
|
@ -402,12 +408,15 @@ impl WidgetState {
|
|||
PrimitiveExtent {
|
||||
boundary: drawing::Boundary::from_pos_size(
|
||||
Vec2::new(
|
||||
transform.rel_pos.x + transform.raw_dim.x - thickness - margin,
|
||||
transform.rel_pos.y + transform.raw_dim.y * (1.0 - info.handle_size.y) * self.data.scrolling_cur.y,
|
||||
// X
|
||||
transform_widget.content_rel_pos.x + transform_widget.raw_dim.x - thickness - margin,
|
||||
// Y
|
||||
transform_widget.content_rel_pos.y
|
||||
+ transform_parent.raw_dim.y * (1.0 - info.handle_size.y) * self.data.scrolling_cur.y,
|
||||
),
|
||||
Vec2::new(thickness, transform.raw_dim.y * info.handle_size.y),
|
||||
Vec2::new(thickness, transform_widget.raw_dim.y * info.handle_size.y),
|
||||
),
|
||||
transform: transform.transform,
|
||||
transform: transform_parent.transform,
|
||||
},
|
||||
rect_params,
|
||||
));
|
||||
|
|
@ -489,16 +498,19 @@ impl WidgetState {
|
|||
let mut res: Option<InvokeListenersResult> = None;
|
||||
|
||||
match &event {
|
||||
Event::TextInput(e) => {
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::TextInput,
|
||||
CallbackMetadata::TextInput(e.clone()),
|
||||
)?);
|
||||
}
|
||||
Event::MouseDown(e) => {
|
||||
if hovered && self.data.set_device_pressed(e.device, true) {
|
||||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::MousePress,
|
||||
CallbackMetadata::MouseButton(event::MouseButton {
|
||||
index: e.index,
|
||||
pos: e.pos,
|
||||
device: e.device,
|
||||
}),
|
||||
CallbackMetadata::MouseButton(*e),
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
|
@ -507,11 +519,7 @@ impl WidgetState {
|
|||
res = Some(self.invoke_listeners(
|
||||
&mut invoke_data,
|
||||
EventListenerKind::MouseRelease,
|
||||
CallbackMetadata::MouseButton(event::MouseButton {
|
||||
index: e.index,
|
||||
pos: e.pos,
|
||||
device: e.device,
|
||||
}),
|
||||
CallbackMetadata::MouseButton(*e),
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue