mirror of https://github.com/wayvr-org/wayvr.git
wgui: Editbox basics, component focus, minor refactoring
This commit is contained in:
parent
c902f0cd44
commit
841241a7e6
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -59,10 +59,17 @@
|
|||
</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." />
|
||||
<EditBox />
|
||||
</rectangle>
|
||||
</div>
|
||||
|
||||
<rectangle macro="rect">
|
||||
<label text="visibility test" weight="bold" />
|
||||
|
|
|
|||
|
|
@ -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,334 @@
|
|||
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, 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, // Rectangle
|
||||
id_rect_bottom: WidgetID, // Rectangle
|
||||
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.9.lerp(1.0, pos_bidir)),
|
||||
height: length(2.0 + pos_bidir * 2.0),
|
||||
}),
|
||||
);
|
||||
|
||||
common.alterables.set_style(
|
||||
data.widget_id,
|
||||
StyleSetRequest::Margin(taffy::Rect {
|
||||
bottom: length(0.0),
|
||||
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.1), editbox_color.add_rgb(0.75))
|
||||
} 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentEditBox {
|
||||
pub fn set_text(&self, common: &mut CallbackDataCommon, text: Translation) {
|
||||
let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(self.data.id_label) else {
|
||||
return;
|
||||
};
|
||||
|
||||
label.set_text(common, text);
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> Ref<'_, String> {
|
||||
Ref::map(self.state.borrow(), |x| &x.text)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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::Scroll,
|
||||
};
|
||||
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()
|
||||
},
|
||||
},
|
||||
),
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
let data = Rc::new(Data {
|
||||
id_rect_container: id_container,
|
||||
id_label: label.id,
|
||||
id_rect_bottom: rect_bottom.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),
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::{
|
|||
any::{Any, TypeId},
|
||||
cell::{Ref, RefMut},
|
||||
collections::HashSet,
|
||||
rc::Weak,
|
||||
};
|
||||
|
||||
use glam::Vec2;
|
||||
|
|
@ -9,9 +10,10 @@ use slotmap::{DenseSlotMap, new_key_type};
|
|||
|
||||
use crate::{
|
||||
animation::{self, Animation},
|
||||
components::{Component, ComponentTrait, ComponentWeak},
|
||||
globals,
|
||||
i18n::I18n,
|
||||
layout::{LayoutState, LayoutTask, WidgetID},
|
||||
layout::{LayoutDispatchFunc, LayoutState, LayoutTask, WidgetID},
|
||||
sound::WguiSoundType,
|
||||
stack::{ScissorStack, Transform, TransformStack},
|
||||
widget::{EventResult, WidgetData, WidgetObj},
|
||||
|
|
@ -101,12 +103,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 +151,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,7 +7,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
animation::Animations,
|
||||
components::{Component, RefreshData},
|
||||
components::{self, Component, ComponentWeak, FocusChangeData, RefreshData},
|
||||
drawing::{self, ANSI_BOLD_CODE, ANSI_RESET_CODE, Boundary, push_scissor_stack, push_transform_stack},
|
||||
event::{self, CallbackDataCommon, EventAlterables},
|
||||
globals::WguiGlobals,
|
||||
|
|
@ -134,14 +134,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 +155,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 +362,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.
|
||||
|
|
@ -581,6 +585,7 @@ impl Layout {
|
|||
widgets_to_tick: Vec::new(),
|
||||
tasks: LayoutTasks::new(),
|
||||
sounds_to_play_once: Vec::new(),
|
||||
focused_component: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -590,8 +595,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 +715,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 +768,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 +830,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)?);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue