Skip to content

Commit 2d9793f

Browse files
authored
Implement ability to style progress of a range input (#736)
This adds the `sliderprogress` sub-element of a range input, which can be used to style the current progress of the range. Similar to `::-moz-range-progress` and `:-webkit-slider-runnable-track`.
1 parent 010765e commit 2d9793f

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

Samples/assets/invader.rcss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ input.range slidertrack {
529529
input.range:focus-visible slidertrack {
530530
decorator: ninepatch( range-track-focus, range-track-focus-inner, 1.0 );
531531
}
532+
input.range sliderprogress {
533+
background: rgba(100, 0, 0, 80);
534+
margin-top: 8dp;
535+
height: 7dp;
536+
}
532537
input.range sliderbar {
533538
margin-left: -8dp;
534539
margin-right: -7dp;

Source/Core/Elements/WidgetSlider.cpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ WidgetSlider::WidgetSlider(ElementFormControl* _parent)
4949

5050
track = nullptr;
5151
bar = nullptr;
52+
progress = nullptr;
5253
arrows[0] = nullptr;
5354
arrows[1] = nullptr;
5455

@@ -69,6 +70,8 @@ WidgetSlider::~WidgetSlider()
6970
{
7071
if (bar)
7172
parent->RemoveChild(bar);
73+
if (track && progress)
74+
track->RemoveChild(progress);
7275
if (track)
7376
parent->RemoveChild(track);
7477

@@ -94,20 +97,23 @@ bool WidgetSlider::Initialise()
9497
// Create all of our child elements as standard elements, and abort if we can't create them.
9598
ElementPtr track_element = Factory::InstanceElement(parent, "*", "slidertrack", XMLAttributes());
9699
ElementPtr bar_element = Factory::InstanceElement(parent, "*", "sliderbar", XMLAttributes());
100+
ElementPtr progress_element = Factory::InstanceElement(parent, "*", "sliderprogress", XMLAttributes());
97101
ElementPtr arrow0_element = Factory::InstanceElement(parent, "*", "sliderarrowdec", XMLAttributes());
98102
ElementPtr arrow1_element = Factory::InstanceElement(parent, "*", "sliderarrowinc", XMLAttributes());
99103

100-
if (!track_element || !bar_element || !arrow0_element || !arrow1_element)
104+
if (!track_element || !bar_element || !progress_element || !arrow0_element || !arrow1_element)
101105
return false;
102106

103107
// Add them as non-DOM elements.
104108
track = parent->AppendChild(std::move(track_element), false);
109+
progress = track->AppendChild(std::move(progress_element), false);
105110
bar = parent->AppendChild(std::move(bar_element), false);
106111
arrows[0] = parent->AppendChild(std::move(arrow0_element), false);
107112
arrows[1] = parent->AppendChild(std::move(arrow1_element), false);
108113

109114
const Property drag_property = Property(Style::Drag::Drag);
110115
track->SetProperty(PropertyId::Drag, drag_property);
116+
progress->SetProperty(PropertyId::Drag, drag_property);
111117
bar->SetProperty(PropertyId::Drag, drag_property);
112118

113119
// Attach the listeners
@@ -155,6 +161,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
155161
{
156162
bar_position = Math::Clamp(_bar_position, 0.0f, 1.0f);
157163
PositionBar();
164+
ResizeProgress();
158165
}
159166

160167
float WidgetSlider::GetBarPosition()
@@ -314,12 +321,14 @@ void WidgetSlider::FormatElements(const Vector2f containing_block, float slider_
314321
}
315322

316323
FormatBar();
324+
FormatProgress();
317325

318326
if (parent->IsDisabled())
319327
{
320328
// Propagate disabled state to child elements
321329
bar->SetPseudoClass("disabled", true);
322330
track->SetPseudoClass("disabled", true);
331+
progress->SetPseudoClass("disabled", true);
323332
arrows[0]->SetPseudoClass("disabled", true);
324333
arrows[1]->SetPseudoClass("disabled", true);
325334
}
@@ -346,6 +355,33 @@ void WidgetSlider::FormatBar()
346355
PositionBar();
347356
}
348357

358+
void WidgetSlider::FormatProgress()
359+
{
360+
Box progress_box;
361+
ElementUtilities::BuildBox(progress_box, parent->GetBox().GetSize(), progress);
362+
auto& computed = progress->GetComputedValues();
363+
364+
Vector2f progress_box_content = progress_box.GetSize();
365+
366+
if (orientation == HORIZONTAL)
367+
{
368+
if (computed.height().type == Style::Height::Auto)
369+
progress_box_content.y = track->GetBox().GetSize().y;
370+
}
371+
372+
// Set the new dimensions on the progress element to re-decorate it.
373+
progress_box.SetContent(progress_box_content);
374+
progress->SetBox(progress_box);
375+
376+
Vector2f offset = track->GetRelativeOffset();
377+
offset.x += progress->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Left);
378+
offset.y += progress->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Top);
379+
380+
progress->SetOffset(offset, parent);
381+
382+
ResizeProgress();
383+
}
384+
349385
Element* WidgetSlider::GetParent() const
350386
{
351387
return parent;
@@ -364,7 +400,7 @@ void WidgetSlider::ProcessEvent(Event& event)
364400
if (event.GetParameter("button", -1) != 0)
365401
break;
366402

367-
if (event.GetTargetElement() == track)
403+
if (event.GetTargetElement() == track || event.GetTargetElement() == progress)
368404
{
369405
float mouse_position, bar_halfsize;
370406

@@ -409,7 +445,7 @@ void WidgetSlider::ProcessEvent(Event& event)
409445

410446
case EventId::Dragstart:
411447
{
412-
if (event.GetTargetElement() == bar || event.GetTargetElement() == track)
448+
if (event.GetTargetElement() == bar || event.GetTargetElement() == track || event.GetTargetElement() == progress)
413449
{
414450
bar->SetPseudoClass("active", true);
415451

@@ -422,7 +458,7 @@ void WidgetSlider::ProcessEvent(Event& event)
422458
break;
423459
case EventId::Drag:
424460
{
425-
if (event.GetTargetElement() == bar || event.GetTargetElement() == track)
461+
if (event.GetTargetElement() == bar || event.GetTargetElement() == track || event.GetTargetElement() == progress)
426462
{
427463
float new_bar_offset = event.GetParameter<float>((orientation == HORIZONTAL ? "mouse_x" : "mouse_y"), 0) - bar_drag_anchor;
428464
float new_bar_position = AbsolutePositionToBarPosition(new_bar_offset);
@@ -433,7 +469,7 @@ void WidgetSlider::ProcessEvent(Event& event)
433469
break;
434470
case EventId::Dragend:
435471
{
436-
if (event.GetTargetElement() == bar || event.GetTargetElement() == track)
472+
if (event.GetTargetElement() == bar || event.GetTargetElement() == track || event.GetTargetElement() == progress)
437473
{
438474
bar->SetPseudoClass("active", false);
439475
}
@@ -559,6 +595,21 @@ void WidgetSlider::PositionBar()
559595
}
560596
}
561597

598+
void WidgetSlider::ResizeProgress()
599+
{
600+
Box progress_box = progress->GetBox();
601+
Vector2f new_size = progress_box.GetSize();
602+
603+
if (orientation == VERTICAL) {
604+
new_size.y = bar->GetOffsetTop();
605+
} else {
606+
new_size.x = bar->GetOffsetLeft();
607+
}
608+
609+
progress_box.SetContent(new_size);
610+
progress->SetBox(progress_box);
611+
}
612+
562613
float WidgetSlider::SetValueInternal(float new_value, bool force_submit_change_event)
563614
{
564615
if (min_value < max_value)

Source/Core/Elements/WidgetSlider.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class WidgetSlider final : public EventListener {
9292
/// Lays out and positions the bar element.
9393
void FormatBar();
9494

95+
/// Lays out and positions the progress element.
96+
void FormatProgress();
97+
9598
/// Returns the widget's parent element.
9699
Element* GetParent() const;
97100

@@ -117,6 +120,7 @@ class WidgetSlider final : public EventListener {
117120
float AbsolutePositionToBarPosition(float absolute_position) const;
118121

119122
void PositionBar();
123+
void ResizeProgress();
120124

121125
// Clamps the new value, sets it on the slider and returns it as a normalized number from 0 to 1.
122126
float SetValueInternal(float new_value, bool force_submit_change_event = true);
@@ -129,6 +133,8 @@ class WidgetSlider final : public EventListener {
129133
Element* track;
130134
// The bar element. This is the element that is dragged across the trough.
131135
Element* bar;
136+
// Element that renders the progress area of the slider.
137+
Element* progress;
132138
// The two (optional) buttons for incrementing and decrementing the slider.
133139
Element* arrows[2];
134140

0 commit comments

Comments
 (0)