wgui: Range sliders, fix swipe scrolling <-> slider conflict

This commit is contained in:
Aleksander 2026-05-03 18:54:31 +02:00
parent 10b14794ed
commit 45af52ddf9
9 changed files with 336 additions and 144 deletions

View File

@ -283,7 +283,7 @@ impl SubtabGeneralSettings {
// get brightness // get brightness
let slider_brightness = state.fetch_component_as::<ComponentSlider>("slider_brightness")?; let slider_brightness = state.fetch_component_as::<ComponentSlider>("slider_brightness")?;
if let Some(brightness) = frontend.interface.monado_brightness_get(data) { if let Some(brightness) = frontend.interface.monado_brightness_get(data) {
slider_brightness.set_value(&mut frontend.layout.common(), brightness * 100.0); slider_brightness.set_value_primary(&mut frontend.layout.common(), brightness * 100.0);
slider_brightness.on_value_changed({ slider_brightness.on_value_changed({
let tasks = tasks.clone(); let tasks = tasks.clone();
@ -308,11 +308,11 @@ impl SubtabGeneralSettings {
// set initial values // set initial values
let (rgb, range_h, range_s, range_v) = config.chroma_key_params.get_rgb_and_hsv_ranges(); let (rgb, range_h, range_s, range_v) = config.chroma_key_params.get_rgb_and_hsv_ranges();
slider_keying_curve.set_value(&mut common, config.chroma_key_params.curve * SLIDER_MULTIPLIER); slider_keying_curve.set_value_primary(&mut common, config.chroma_key_params.curve * SLIDER_MULTIPLIER);
slider_keying_despill.set_value(&mut common, config.chroma_key_params.despill * SLIDER_MULTIPLIER); slider_keying_despill.set_value_primary(&mut common, config.chroma_key_params.despill * SLIDER_MULTIPLIER);
slider_keying_hue_range.set_value(&mut common, range_h * SLIDER_MULTIPLIER); slider_keying_hue_range.set_value_primary(&mut common, range_h * SLIDER_MULTIPLIER);
slider_keying_saturation_range.set_value(&mut common, range_s * SLIDER_MULTIPLIER); slider_keying_saturation_range.set_value_primary(&mut common, range_s * SLIDER_MULTIPLIER);
slider_keying_value_range.set_value(&mut common, range_v * SLIDER_MULTIPLIER); slider_keying_value_range.set_value_primary(&mut common, range_v * SLIDER_MULTIPLIER);
cs_keying.set_color(&mut common, rgb); cs_keying.set_color(&mut common, rgb);
// prepare callbacks // prepare callbacks
@ -353,11 +353,11 @@ impl SubtabGeneralSettings {
} }
fn chroma_update(&mut self, config: &mut GeneralConfig) { fn chroma_update(&mut self, config: &mut GeneralConfig) {
let val_curve = self.slider_keying_curve.get_value(); let val_curve = self.slider_keying_curve.get_value_primary();
let val_despill = self.slider_keying_despill.get_value(); let val_despill = self.slider_keying_despill.get_value_primary();
let val_range_h = self.slider_keying_hue_range.get_value(); let val_range_h = self.slider_keying_hue_range.get_value_primary();
let val_range_s = self.slider_keying_saturation_range.get_value(); let val_range_s = self.slider_keying_saturation_range.get_value_primary();
let val_range_v = self.slider_keying_value_range.get_value(); let val_range_v = self.slider_keying_value_range.get_value_primary();
let val_rgb = self.cs_keying.get_color(); let val_rgb = self.cs_keying.get_color();
config.chroma_key_params.despill = val_despill / SLIDER_MULTIPLIER; config.chroma_key_params.despill = val_despill / SLIDER_MULTIPLIER;

View File

@ -804,7 +804,7 @@ impl View {
let btn_mute = data.fetch_component_as::<ComponentButton>("btn_mute")?; let btn_mute = data.fetch_component_as::<ComponentButton>("btn_mute")?;
let slider = data.fetch_component_as::<ComponentSlider>("slider")?; let slider = data.fetch_component_as::<ComponentSlider>("slider")?;
slider.set_value(&mut common, params.control.on_volume_request()? / VOLUME_MULT); slider.set_value_primary(&mut common, params.control.on_volume_request()? / VOLUME_MULT);
checkbox.set_checked(&mut common, params.checked); checkbox.set_checked(&mut common, params.checked);

View File

@ -94,6 +94,11 @@
<CheckBox text="i'm checked by default" checked="1" /> <CheckBox text="i'm checked by default" checked="1" />
</rectangle> </rectangle>
<rectangle macro="rect" flex_direction="row" gap="16">
<label text="range slider test" weight="bold" />
<Slider value="0.5" value2="1.0" min_value="-5.0" max_value="20.0" width="200" height="24"/>
</rectangle>
<rectangle macro="rect" flex_direction="row" align_items="start"> <rectangle macro="rect" flex_direction="row" align_items="start">
<div flex_direction="column" gap="8"> <div flex_direction="column" gap="8">
<label text="height 16" weight="bold" /> <label text="height 16" weight="bold" />

View File

@ -581,7 +581,7 @@ pub fn apply_custom_command<T>(
.fetch_component_as::<ComponentSlider>(element) .fetch_component_as::<ComponentSlider>(element)
{ {
let value_f32 = value_str.parse::<f32>().context("Not a valid number")?; let value_f32 = value_str.parse::<f32>().context("Not a valid number")?;
slider.set_value(&mut com, value_f32); slider.set_value_primary(&mut com, value_f32);
} else if let Ok(radio) = panel } else if let Ok(radio) = panel
.parser_state .parser_state
.fetch_component_as::<ComponentRadioGroup>(element) .fetch_component_as::<ComponentRadioGroup>(element)

View File

@ -476,17 +476,17 @@ fn reset_panel(
let c = panel let c = panel
.parser_state .parser_state
.fetch_component_as::<ComponentSlider>("lerp_slider")?; .fetch_component_as::<ComponentSlider>("lerp_slider")?;
c.set_value(&mut com, state.positioning.get_lerp().unwrap_or(1.0)); c.set_value_primary(&mut com, state.positioning.get_lerp().unwrap_or(1.0));
let c = panel let c = panel
.parser_state .parser_state
.fetch_component_as::<ComponentSlider>("alpha_slider")?; .fetch_component_as::<ComponentSlider>("alpha_slider")?;
c.set_value(&mut com, state.alpha); c.set_value_primary(&mut com, state.alpha);
let c = panel let c = panel
.parser_state .parser_state
.fetch_component_as::<ComponentSlider>("curve_slider")?; .fetch_component_as::<ComponentSlider>("curve_slider")?;
c.set_value(&mut com, state.curvature.unwrap_or(0.0)); c.set_value_primary(&mut com, state.curvature.unwrap_or(0.0));
let c = panel let c = panel
.parser_state .parser_state

View File

@ -163,9 +163,9 @@ impl ComponentColorSelector {
{ {
let mut common = layout.common(); let mut common = layout.common();
slider_r.set_value(&mut common, state.color.r * 255.0); slider_r.set_value_primary(&mut common, state.color.r * 255.0);
slider_g.set_value(&mut common, state.color.g * 255.0); slider_g.set_value_primary(&mut common, state.color.g * 255.0);
slider_b.set_value(&mut common, state.color.b * 255.0); slider_b.set_value_primary(&mut common, state.color.b * 255.0);
} }
slider_r.on_value_changed(self.gen_slider_callback(ColorIndex::Red)); slider_r.on_value_changed(self.gen_slider_callback(ColorIndex::Red));

View File

@ -29,31 +29,29 @@ use crate::{
}, },
}; };
#[derive(Default, Clone)]
pub struct Value(pub f32);
#[derive(Default)] #[derive(Default)]
pub struct ValuesMinMax { pub struct Limits {
pub value: f32,
pub min_value: f32, pub min_value: f32,
pub max_value: f32, pub max_value: f32,
pub step: f32, pub step: f32,
} }
impl ValuesMinMax { impl Value {
fn to_normalized(&self) -> f32 { pub fn get(&self) -> f32 {
(self.value - self.min_value) / (self.max_value - self.min_value) self.0
} }
fn get_from_normalized(&self, normalized: f32) -> f32 { pub fn set(&mut self, limits: &Limits, new_value: f32) {
normalized * (self.max_value - self.min_value) + self.min_value let span = limits.max_value - limits.min_value;
} let clamped = new_value.max(limits.min_value).min(limits.max_value);
fn set_value(&mut self, new_value: f32) -> &mut Self {
let span = self.max_value - self.min_value;
let clamped = new_value.max(self.min_value).min(self.max_value);
// get the step index from min // get the step index from min
let mut k = ((clamped - self.min_value) / self.step).round(); let mut k = ((clamped - limits.min_value) / limits.step).round();
let k_max = (span / self.step).floor(); let k_max = (span / limits.step).floor();
if k < 0.0 { if k < 0.0 {
k = 0.0; k = 0.0;
} }
@ -61,25 +59,50 @@ impl ValuesMinMax {
k = k_max; k = k_max;
} }
let snapped = self.min_value + k * self.step; let snapped = limits.min_value + k * limits.step;
self.value = snapped.max(self.min_value).min(self.max_value); self.0 = snapped.max(limits.min_value).min(limits.max_value);
}
}
self impl Limits {
fn to_normalized(&self, value: f32) -> f32 {
(value - self.min_value) / (self.max_value - self.min_value)
}
fn get_from_normalized(&self, normalized: f32) -> f32 {
normalized * (self.max_value - self.min_value) + self.min_value
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct Params { pub struct Params {
pub style: taffy::Style, pub style: taffy::Style,
pub values: ValuesMinMax, pub limits: Limits,
pub value1: Value,
pub value2: Option<Value>, // range slider support
pub show_value: bool, pub show_value: bool,
pub tooltip: Option<tooltip::TooltipInfo>, pub tooltip: Option<tooltip::TooltipInfo>,
} }
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ValueIndex {
Primary,
Secondary, /* for range sliders */
}
struct DraggedBy {
index: ValueIndex,
device: DeviceBitmask,
}
struct State { struct State {
dragged_by: Option<DeviceBitmask>, dragged_by: Option<DraggedBy>,
hovered: bool, hovered_body: bool,
values: ValuesMinMax, hovered1: bool,
hovered2: bool,
value1: Value,
value2: Option<Value>,
limits: Limits,
on_value_changed: Option<SliderValueChangedCallback>, on_value_changed: Option<SliderValueChangedCallback>,
active_tooltip: Option<Rc<ComponentTooltip>>, active_tooltip: Option<Rc<ComponentTooltip>>,
} }
@ -90,14 +113,20 @@ impl TooltipTrait for State {
} }
} }
struct SliderHandleData {
id_handle_rect: WidgetID, // Rectangle
id_text: Option<WidgetID>, // Text
id_handle: WidgetID,
}
struct Data { struct Data {
body_node: taffy::NodeId, body_node: taffy::NodeId,
slider_handle_rect_id: WidgetID, // Rectangle handle1: SliderHandleData,
slider_text_id: Option<WidgetID>, // Text handle2: Option<SliderHandleData>,
slider_handle_id: WidgetID,
} }
pub struct SliderValueChangedEvent { pub struct SliderValueChangedEvent {
pub index: ValueIndex,
pub value: f32, pub value: f32,
} }
@ -113,8 +142,13 @@ impl ComponentTrait for ComponentSlider {
fn refresh(&self, data: &mut RefreshData) { fn refresh(&self, data: &mut RefreshData) {
let mut common = data.layout.common(); let mut common = data.layout.common();
let mut state = self.state.borrow_mut(); let mut state = self.state.borrow_mut();
let value = state.values.value;
state.set_value(&mut common, &self.data, value); let value1 = state.value1.get();
state.set_value(&mut common, &self.data, ValueIndex::Primary, value1);
if let Some(value2) = state.value2.as_ref().map(|v| v.get()) {
state.set_value(&mut common, &self.data, ValueIndex::Secondary, value2);
}
} }
fn base(&self) -> &ComponentBase { fn base(&self) -> &ComponentBase {
@ -127,13 +161,25 @@ impl ComponentTrait for ComponentSlider {
} }
impl ComponentSlider { impl ComponentSlider {
pub fn get_value(&self) -> f32 { pub fn get_value_primary(&self) -> f32 {
self.state.borrow().values.value self.get_value(ValueIndex::Primary).unwrap() /* safe */
} }
pub fn set_value(&self, common: &mut CallbackDataCommon, value: f32) { pub fn get_value(&self, index: ValueIndex) -> Option<f32> {
let state = self.state.borrow();
match index {
ValueIndex::Primary => Some(state.value1.get()),
ValueIndex::Secondary => state.value2.as_ref().map(|v| v.get()),
}
}
pub fn set_value(&self, common: &mut CallbackDataCommon, index: ValueIndex, new_value: f32) {
let mut state = self.state.borrow_mut(); let mut state = self.state.borrow_mut();
state.set_value(common, &self.data, value); state.set_value(common, &self.data, index, new_value);
}
pub fn set_value_primary(&self, common: &mut CallbackDataCommon, new_value: f32) {
self.set_value(common, ValueIndex::Primary, new_value);
} }
pub fn on_value_changed(&self, func: SliderValueChangedCallback) { pub fn on_value_changed(&self, func: SliderValueChangedCallback) {
@ -153,14 +199,15 @@ fn get_width(slider_body_node: taffy::NodeId, tree: &taffy::tree::TaffyTree<Widg
fn conf_handle_style( fn conf_handle_style(
alterables: &mut EventAlterables, alterables: &mut EventAlterables,
values: &ValuesMinMax, limits: &Limits,
value: f32,
slider_handle_id: WidgetID, slider_handle_id: WidgetID,
body_node: taffy::NodeId, body_node: taffy::NodeId,
slider_handle_style: &taffy::Style, slider_handle_style: &taffy::Style,
tree: &taffy::tree::TaffyTree<WidgetID>, tree: &taffy::tree::TaffyTree<WidgetID>,
) -> bool { ) -> bool {
/* returns false if nothing changed */ /* returns false if nothing has changed */
let norm = values.to_normalized(); let norm = limits.to_normalized(value);
// convert normalized value to taffy percentage margin in percent // convert normalized value to taffy percentage margin in percent
let width = get_width(body_node, tree); let width = get_width(body_node, tree);
@ -184,11 +231,22 @@ const HANDLE_WIDTH: f32 = 32.0;
const HANDLE_HEIGHT: f32 = 24.0; const HANDLE_HEIGHT: f32 = 24.0;
impl State { impl State {
fn get_hovered_index(&self) -> Option<ValueIndex> {
if self.hovered1 {
Some(ValueIndex::Primary)
} else if self.hovered2 {
Some(ValueIndex::Secondary)
} else {
None
}
}
fn update_value_to_mouse( fn update_value_to_mouse(
&mut self, &mut self,
event_data: &event::CallbackData<'_>, event_data: &event::CallbackData<'_>,
data: &Data, data: &Data,
common: &mut CallbackDataCommon, common: &mut CallbackDataCommon,
index: ValueIndex,
) { ) {
let mouse_pos = event_data let mouse_pos = event_data
.metadata .metadata
@ -200,10 +258,10 @@ impl State {
get_width(data.body_node, &common.state.tree) - HANDLE_WIDTH, get_width(data.body_node, &common.state.tree) - HANDLE_WIDTH,
); );
let target_value = self.values.get_from_normalized(norm); let target_value = self.limits.get_from_normalized(norm);
let val = target_value; let val = target_value;
self.set_value(common, data, val); self.set_value(common, data, index, val);
} }
fn update_text(common: &mut CallbackDataCommon, text: &mut WidgetLabel, value: f32) { fn update_text(common: &mut CallbackDataCommon, text: &mut WidgetLabel, value: f32) {
@ -217,23 +275,47 @@ impl State {
text.set_text(common, Translation::from_raw_text(&pretty)); text.set_text(common, Translation::from_raw_text(&pretty));
} }
fn set_value(&mut self, common: &mut CallbackDataCommon, data: &Data, value: f32) { fn set_value(&mut self, common: &mut CallbackDataCommon, data: &Data, index: ValueIndex, new_value: f32) {
let before = self.values.value; let val1 = self.value1.get();
self.values.set_value(value);
let changed = self.values.value != before; let Some(value) = (match index {
ValueIndex::Primary => Some(&mut self.value1),
ValueIndex::Secondary => self.value2.as_mut(),
}) else {
return;
};
let Some(slider_handle_node_id) = common.state.nodes.get(data.slider_handle_id) else { // Slider handle widget
let Some(handle_data) = (match index {
ValueIndex::Primary => Some(&data.handle1),
ValueIndex::Secondary => data.handle2.as_ref(),
}) else {
unreachable!();
};
let before = value.get();
if index == ValueIndex::Secondary {
value.set(&self.limits, new_value.max(val1));
} else {
value.set(&self.limits, new_value);
}
let has_changed = value.get() != before;
let Some(slider_handle_node_id) = common.state.nodes.get(handle_data.id_handle) else {
return; return;
}; };
let Ok(style) = common.state.tree.style(*slider_handle_node_id) else { let Ok(style) = common.state.tree.style(*slider_handle_node_id) else {
return; return;
}; };
if !conf_handle_style( if !conf_handle_style(
common.alterables, common.alterables,
&self.values, &self.limits,
data.slider_handle_id, value.get(),
handle_data.id_handle,
data.body_node, data.body_node,
style, style,
&common.state.tree, &common.state.tree,
@ -241,20 +323,21 @@ impl State {
return; // nothing changed visually return; // nothing changed visually
} }
common.alterables.mark_dirty(data.slider_handle_id); common.alterables.mark_dirty(handle_data.id_handle);
common.alterables.mark_redraw(); common.alterables.mark_redraw();
if let Some(slider_text_id) = data.slider_text_id if let Some(id_text) = handle_data.id_text
&& let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(slider_text_id) && let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(id_text)
{ {
Self::update_text(common, &mut label, self.values.value); Self::update_text(common, &mut label, value.get());
} }
if changed && let Some(on_value_changed) = &self.on_value_changed { if has_changed && let Some(on_value_changed) = &self.on_value_changed {
on_value_changed( on_value_changed(
common, common,
SliderValueChangedEvent { SliderValueChangedEvent {
value: self.values.value, index: index,
value: value.get(),
}, },
) )
} }
@ -310,18 +393,16 @@ fn on_leave_anim(common: &mut event::CallbackDataCommon, handle_id: WidgetID, an
} }
fn register_event_mouse_enter( fn register_event_mouse_enter(
data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection, listeners: &mut EventListenerCollection,
tooltip_info: Option<tooltip::TooltipInfo>, tooltip_info: Option<tooltip::TooltipInfo>,
anim_mult: f32,
) -> event::EventListenerID { ) -> event::EventListenerID {
listeners.register( listeners.register(
EventListenerKind::MouseEnter, EventListenerKind::MouseEnter,
Box::new(move |common, event_data, (), ()| { Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics(); common.alterables.trigger_haptics();
state.borrow_mut().hovered = true; state.borrow_mut().hovered_body = 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()); ComponentTooltip::register_hover_in(common, &tooltip_info, event_data.widget_id, state.clone());
Ok(EventResult::Pass) Ok(EventResult::Pass)
@ -330,10 +411,8 @@ fn register_event_mouse_enter(
} }
fn register_event_mouse_leave( fn register_event_mouse_leave(
data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection, listeners: &mut EventListenerCollection,
anim_mult: f32,
) -> event::EventListenerID { ) -> event::EventListenerID {
listeners.register( listeners.register(
EventListenerKind::MouseLeave, EventListenerKind::MouseLeave,
@ -342,36 +421,110 @@ fn register_event_mouse_leave(
{ {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
state.hovered = false; state.hovered_body = false;
state.active_tooltip = None; state.active_tooltip = None;
} }
on_leave_anim(common, data.slider_handle_rect_id, anim_mult);
Ok(EventResult::Pass) Ok(EventResult::Pass)
}), }),
) )
} }
fn get_handle_dist(common: &mut CallbackDataCommon, handle: &SliderHandleData, mouse_pos: Vec2) -> f32 {
let center = common.state.get_widget_boundary(handle.id_handle).center();
Vec2::distance(center, mouse_pos)
}
const MAX_HOVER_DIST: f32 = 64.0;
fn update_handle_hovers(
common: &mut CallbackDataCommon,
data: &Data,
state: &mut State,
anim_mult: f32,
mouse_pos: Vec2,
) {
let hovered1_prev = state.hovered1;
let hovered2_prev = state.hovered2;
if !state.hovered_body {
state.hovered1 = false;
state.hovered2 = false;
} else {
let dist1 = get_handle_dist(common, &data.handle1, mouse_pos);
let dist2 = data
.handle2
.as_ref()
.map(|h| get_handle_dist(common, h, mouse_pos))
.unwrap_or(std::f32::MAX);
state.hovered1 = dist1 <= MAX_HOVER_DIST;
state.hovered2 = dist2 <= MAX_HOVER_DIST;
// both of them are hovered, hover the closest one
if state.hovered1 && state.hovered2 {
if dist1 < dist2 {
state.hovered2 = false;
} else {
state.hovered1 = false;
}
}
}
// hover state changed, run animations
if state.hovered1 != hovered1_prev {
if state.hovered1 && !hovered1_prev {
on_enter_anim(common, data.handle1.id_handle_rect, anim_mult);
} else {
on_leave_anim(common, data.handle1.id_handle_rect, anim_mult);
}
}
if state.hovered2 != hovered2_prev
&& let Some(handle2) = data.handle2.as_ref()
{
if state.hovered2 && !hovered2_prev {
on_enter_anim(common, handle2.id_handle_rect, anim_mult);
} else {
on_leave_anim(common, handle2.id_handle_rect, anim_mult);
}
}
}
fn register_event_mouse_motion( fn register_event_mouse_motion(
data: Rc<Data>, data: Rc<Data>,
state: Rc<RefCell<State>>, state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection, listeners: &mut EventListenerCollection,
anim_mult: f32,
) -> event::EventListenerID { ) -> event::EventListenerID {
listeners.register( listeners.register(
EventListenerKind::MouseMotion, EventListenerKind::MouseMotion,
Box::new(move |common, event_data, (), ()| { Box::new(move |common, event_data, (), ()| {
let mut state = state.borrow_mut(); let mut state = state.borrow_mut();
let CallbackMetadata::MousePosition(pos) = event_data.metadata else { let Some(pos_relative) = event_data
.metadata
.get_mouse_pos_relative(&common.alterables.transform_stack)
else {
unreachable!(); unreachable!();
}; };
if state.dragged_by.is_some_and(|device| device == pos.device) { let CallbackMetadata::MousePosition(pos) = &event_data.metadata else {
state.update_value_to_mouse(event_data, &data, common); unreachable!();
Ok(EventResult::Consumed) };
} else {
Ok(EventResult::Pass) update_handle_hovers(common, &data, &mut state, anim_mult, pos_relative);
if let Some(dragged_by) = &state.dragged_by {
if dragged_by.device == pos.device {
let index = dragged_by.index;
state.update_value_to_mouse(event_data, &data, common, index);
return Ok(EventResult::Consumed);
}
} }
Ok(EventResult::Pass)
}), }),
) )
} }
@ -392,13 +545,18 @@ fn register_event_mouse_press(
unreachable!(); unreachable!();
}; };
if state.hovered { if !state.hovered_body {
state.dragged_by = Some(btn.device); // this slider isn't hovered at all?
state.update_value_to_mouse(event_data, &data, common); return Ok(EventResult::Pass);
Ok(EventResult::Consumed)
} else {
Ok(EventResult::Pass)
} }
let hovered_index = state.get_hovered_index().unwrap_or(ValueIndex::Primary);
state.dragged_by = Some(DraggedBy {
device: btn.device,
index: hovered_index,
});
state.update_value_to_mouse(event_data, &data, common, hovered_index);
Ok(EventResult::Consumed)
}), }),
) )
} }
@ -423,36 +581,11 @@ fn register_event_mouse_release(
) )
} }
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> { fn mount_slider_handle(
let mut style = params.style; ess: &mut ConstructEssentials,
style.position = taffy::Position::Relative; body_id: WidgetID,
style.min_size = style.size; show_value: bool,
style.max_size = style.size; ) -> anyhow::Result<SliderHandleData> {
let (root, slider_body_node) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;
let body_id = root.id;
let (_background_id, _) = ess.layout.add_child(
body_id,
WidgetRectangle::create(WidgetRectangleParams {
color: BODY_COLOR,
round: WLength::Percent(1.0),
border_color: BODY_BORDER_COLOR,
border: 2.0,
..Default::default()
}),
taffy::Style {
size: taffy::Size {
width: percent(1.0),
height: percent(PAD_PERCENT),
},
position: taffy::Position::Absolute,
align_self: Some(taffy::AlignItems::Center),
justify_self: Some(taffy::JustifySelf::Center),
..Default::default()
},
)?;
let slider_handle_style = taffy::Style { let slider_handle_style = taffy::Style {
size: taffy::Size { size: taffy::Size {
width: length(0.0), width: length(0.0),
@ -488,15 +621,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
}, },
)?; )?;
let state = State { let slider_text: Option<(WidgetPair, taffy::NodeId)> = if show_value {
dragged_by: None,
hovered: false,
values: params.values,
on_value_changed: None,
active_tooltip: None,
};
let slider_text: Option<(WidgetPair, taffy::NodeId)> = if params.show_value {
let label = WidgetLabel::create( let label = WidgetLabel::create(
&mut ess.layout.state, &mut ess.layout.state,
WidgetLabelParams { WidgetLabelParams {
@ -514,11 +639,66 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
None None
}; };
Ok(SliderHandleData {
id_handle_rect: slider_handle_rect.id,
id_text: slider_text.map(|s| s.0.id),
id_handle: slider_handle.id,
})
}
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
let mut style = params.style;
style.position = taffy::Position::Relative;
style.min_size = style.size;
style.max_size = style.size;
let (root, slider_body_node) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;
let body_id = root.id;
let (_background_id, _) = ess.layout.add_child(
body_id,
WidgetRectangle::create(WidgetRectangleParams {
color: BODY_COLOR,
round: WLength::Percent(1.0),
border_color: BODY_BORDER_COLOR,
border: 2.0,
..Default::default()
}),
taffy::Style {
size: taffy::Size {
width: percent(1.0),
height: percent(PAD_PERCENT),
},
position: taffy::Position::Absolute,
align_self: Some(taffy::AlignItems::Center),
justify_self: Some(taffy::JustifySelf::Center),
..Default::default()
},
)?;
let slider_handle1 = mount_slider_handle(ess, body_id, params.show_value)?;
let slider_handle2 = if params.value2.is_some() {
Some(mount_slider_handle(ess, body_id, params.show_value)?)
} else {
None
};
let state = State {
dragged_by: None,
hovered_body: false,
hovered1: false,
hovered2: false,
value1: params.value1,
value2: params.value2,
limits: params.limits,
on_value_changed: None,
active_tooltip: None,
};
let data = Rc::new(Data { let data = Rc::new(Data {
slider_handle_rect_id: slider_handle_rect.id,
body_node: slider_body_node, body_node: slider_body_node,
slider_handle_id: slider_handle.id, handle1: slider_handle1,
slider_text_id: slider_text.map(|s| s.0.id), handle2: slider_handle2,
}); });
let state = Rc::new(RefCell::new(state)); let state = Rc::new(RefCell::new(state));
@ -529,9 +709,9 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
let listeners = &mut root.widget.state().event_listeners; let listeners = &mut root.widget.state().event_listeners;
let anim_mult = ess.layout.state.theme.animation_mult; let anim_mult = ess.layout.state.theme.animation_mult;
vec![ vec![
register_event_mouse_enter(data.clone(), state.clone(), listeners, params.tooltip, anim_mult), register_event_mouse_enter(state.clone(), listeners, params.tooltip),
register_event_mouse_leave(data.clone(), state.clone(), listeners, anim_mult), register_event_mouse_leave(state.clone(), listeners),
register_event_mouse_motion(data.clone(), state.clone(), listeners), register_event_mouse_motion(data.clone(), state.clone(), listeners, anim_mult),
register_event_mouse_press(data.clone(), state.clone(), listeners), register_event_mouse_press(data.clone(), state.clone(), listeners),
register_event_mouse_release(state.clone(), listeners), register_event_mouse_release(state.clone(), listeners),
] ]

View File

@ -18,7 +18,8 @@ pub fn parse_component_slider(
) -> anyhow::Result<WidgetID> { ) -> anyhow::Result<WidgetID> {
let mut min_value = 0.0; let mut min_value = 0.0;
let mut max_value = 1.0; let mut max_value = 1.0;
let mut initial_value = 0.5; let mut initial_value1 = 0.5;
let mut initial_value2: Option<f32> = None;
let mut step = 1.0; let mut step = 1.0;
let mut show_value = 1; let mut show_value = 1;
let mut tooltip = TooltipAttribs::default(); let mut tooltip = TooltipAttribs::default();
@ -35,7 +36,13 @@ pub fn parse_component_slider(
ctx.parse_check_f32(tag_name, key, value, &mut max_value); ctx.parse_check_f32(tag_name, key, value, &mut max_value);
} }
"value" => { "value" => {
ctx.parse_check_f32(tag_name, key, value, &mut initial_value); ctx.parse_check_f32(tag_name, key, value, &mut initial_value1);
}
"value2" => {
let mut val = 0.0;
if ctx.parse_check_f32(tag_name, key, value, &mut val) {
initial_value2 = Some(val);
}
} }
"step" => { "step" => {
ctx.parse_check_f32(tag_name, key, value, &mut step); ctx.parse_check_f32(tag_name, key, value, &mut step);
@ -56,12 +63,13 @@ pub fn parse_component_slider(
}, },
slider::Params { slider::Params {
style, style,
values: slider::ValuesMinMax { limits: slider::Limits {
min_value, min_value,
max_value, max_value,
value: initial_value,
step, step,
}, },
value1: slider::Value(initial_value1),
value2: initial_value2.map(|v| slider::Value(v)),
show_value: show_value != 0, show_value: show_value != 0,
tooltip: tooltip.get_info(), tooltip: tooltip.get_info(),
}, },

View File

@ -568,14 +568,6 @@ impl WidgetState {
event: &Event, event: &Event,
) -> anyhow::Result<EventResult> { ) -> anyhow::Result<EventResult> {
match &event { match &event {
Event::MouseDown(e) => {
// firstly, check if this widget is scrollable at all
let (active_x, active_y) = get_scroll_active_axis(&params.style, &params.taffy_layout);
if active_x || active_y {
self.data.press_down_start_mouse_pos = Some(e.pos);
self.data.swipe_scroll_start = self.data.scrolling_target;
}
}
Event::MouseUp(_e) => { Event::MouseUp(_e) => {
if self.data.swipe_running { if self.data.swipe_running {
self.data.swipe_running = false; self.data.swipe_running = false;
@ -654,6 +646,13 @@ impl WidgetState {
res = Some(self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseCancel, CallbackMetadata::None)?); res = Some(self.invoke_listeners(&mut invoke_data, EventListenerKind::MouseCancel, CallbackMetadata::None)?);
} }
Event::MouseDown(e) => { Event::MouseDown(e) => {
// firstly, check if this widget is scrollable at all
let (active_x, active_y) = get_scroll_active_axis(&invoke_data.params.style, &invoke_data.params.taffy_layout);
if active_x || active_y {
self.data.press_down_start_mouse_pos = Some(e.pos);
self.data.swipe_scroll_start = self.data.scrolling_target;
}
if hovered && self.data.set_device_pressed(e.device, true) { if hovered && self.data.set_device_pressed(e.device, true) {
res = Some(self.invoke_listeners( res = Some(self.invoke_listeners(
&mut invoke_data, &mut invoke_data,