-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Core slider #19584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Core slider #19584
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor suggestions, but this is looking good. Like always, please push back against me where you disagree.
if let Ok((slider, value, range, step, disabled)) = q_slider.get(trigger.target().unwrap()) { | ||
let event = &trigger.event().input; | ||
if !disabled && event.state == ButtonState::Pressed { | ||
let new_value = match event.key_code { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could also have a shift + left/right big step here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How big? A fixed multiplier of StepSize (say 5x), or do we want a more complex StepSize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the multiplier would need to be configurable, depending on the range it can easily be way too big or too small otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would like to punt on this for now:
- I don't want to get too elaborate.
- This is outside the bounds of use cases I'm familiar with - I want to stay grounded and not add features based on speculation.
- I just checked the behavior of range sliders in HTML and they don't do this.
That being said, if someone comes along and has a practical need for this, I'm happy to reconsider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm fine with leaving this for now, it's not essential.
width: Val::Px(12.0), | ||
height: Val::Px(12.0), | ||
position_type: PositionType::Absolute, | ||
left: Val::Percent(0.0), // This will be updated by the slider's value | ||
..default() | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use UI transform so the thumb is positioned at its center, then the parent doesn't need to know the thumb's size.
}, | |
}, | |
UiTransform::from_translation(Val2::percent(-50., -50.)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In response to this, I've updated the comment on CoreSlider with a long explanation of thumb positioning and travel. The goal here is to ensure that during dragging, the visible offset between pointer position and the thumb remains constant. This means that both the absolute positioning of the thumb, and the calculation of the slider value during dragging, need to use the same divisor - otherwise the thumb will move either slower or faster than the mouse. This is even more critical in "snap" clicking mode.
Positioning from the center, like you suggest, will work, but does not remove the responsibility of the stylist to account for the reduced travel to accommodate the thumb width. Note that it is perfectly acceptable to have a zero-width thumb with additional absolutely positioned decorative elements, if you want the thumb to overhang the ends. But for something like an editor, where sliders are often going to be lined up in a column with other widgets (like your text editor) we will want to avoid overhang so that the ends of the sliders line up nicely with the other widgets and produce a straight margin.
display: Display::Flex, | ||
position_type: PositionType::Absolute, | ||
left: Val::Px(0.0), | ||
// Track is short by 12px to accommodate the thumb. This should match thumb_size | ||
right: Val::Px(12.0), | ||
top: Val::Px(0.0), | ||
bottom: Val::Px(0.0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
display: Display::Flex, | |
position_type: PositionType::Absolute, | |
left: Val::Px(0.0), | |
// Track is short by 12px to accommodate the thumb. This should match thumb_size | |
right: Val::Px(12.0), | |
top: Val::Px(0.0), | |
bottom: Val::Px(0.0), | |
position_type: PositionType::Absolute, | |
left: Val::Px(3.0), | |
right: Val::Px(3.0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be possible to simplify things further and move all these constraints to the background rail so we don't need to specify left and right here, but I've not been doing much CSS lately 😓.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I just didn't realise that the track wasn't a child of the visible rail. With the rail parenting the track it all composes nicely:
// Slider background rail
Spawn((
Node {
flex_direction: FlexDirection::Column,
padding: UiRect::all(Val::Px(3.)),
..default()
},
BackgroundColor(SLIDER_TRACK), // Border color for the checkbox
BorderRadius::MAX,
children![(
// Invisible track to allow absolute placement of thumb entity. This is narrower than
// the actual slider, which allows us to position the thumb entity using simple
// percentages, without having to measure the actual width of the slider thumb.
Node::default(),
children![(
// Thumb
DemoSliderThumb,
CoreSliderThumb,
Node {
position_type: PositionType::Absolute,
width: Val::Px(12.0),
aspect_ratio: Some(1.),
left: Val::Percent(0.0), // This will be updated by the slider's value
..default()
},
UiTransform::from_translation(Val2::percent(-50., -50.)),
BorderRadius::MAX,
BackgroundColor(SLIDER_THUMB),
)],
)],
)),
/// shorctuts. Defaults to 1.0. | ||
#[derive(Component, Debug, PartialEq, Clone)] | ||
#[component(immutable)] | ||
pub struct SliderStep(pub f32); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name might be confusing if we want to add support for discrete ranges later on (or would that be a separate widget?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question of discrete ranges is something I have given a lot of thought to.
First, I'm not confident that I could build a slider that is generic in the value type (integer vs. float) due to the fact that we have so many different components and observers that all need to work together.
One solution is to quantize after the fact: that is, the callback can truncate or modify the value as it wishes, and display the value as an integer. This also permits other kinds of trickery, like log-scale sliders.
That being said I have ideas for other kinds of widgets, some of which would edit discrete values - such as a "game difficulty" chooser which lets you pick between a set of text labels.
display: Display::Flex, | ||
flex_direction: FlexDirection::Column, | ||
justify_content: JustifyContent::Center, | ||
align_items: AlignItems::Stretch, | ||
justify_items: JustifyItems::Center, | ||
column_gap: Val::Px(4.0), | ||
height: Val::Px(12.0), | ||
width: Val::Percent(30.0), | ||
..default() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
display: Display::Flex, | |
flex_direction: FlexDirection::Column, | |
justify_content: JustifyContent::Center, | |
align_items: AlignItems::Stretch, | |
justify_items: JustifyItems::Center, | |
column_gap: Val::Px(4.0), | |
height: Val::Px(12.0), | |
width: Val::Percent(30.0), | |
..default() | |
flex_direction: FlexDirection::Column, | |
height: Val::Px(12.0), | |
width: Val::Percent(30.0), | |
..default() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With a UiScale
!= 1 slider dragging breaks. Hover isn't affected and the sliders seem to work correctly with different window scale factors though so I think it isn't anything wrong here. The culprit is probably a bug in the picking backend somewhere.
That's strange. I'm developing on a MacBook Pro, which normally has a scaling factor of 2 (assuming we're talking about the same thing). |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these changes should fix the UiScale bugs for now.
Widgets are now uncontrolled if there is no callback, controlled otherwise.
Accommodate UiScale. Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
I had to do an unpleasant rebase and force push because of all the changes to Trigger. |
Don't allow track click while disabled. Don't set focus to thumb.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Played around with this a lot, the code is great, LGTM
# Objective This is part of the "core widgets" effort: bevyengine#19236. ## Solution This PR adds the "core slider" widget to the collection. ## Testing Tested using examples `core_widgets` and `core_widgets_observers`. --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Objective
This is part of the "core widgets" effort: #19236.
Solution
This PR adds the "core slider" widget to the collection.
Testing
Tested using examples
core_widgets
andcore_widgets_observers
.