Skip to content

Implement ability to style progress of a range input #736

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

Merged
merged 4 commits into from
Mar 22, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Samples/assets/invader.rcss
Original file line number Diff line number Diff line change
Expand Up @@ -529,12 +529,19 @@ input.range slidertrack {
input.range:focus-visible slidertrack {
decorator: ninepatch( range-track-focus, range-track-focus-inner, 1.0 );
}
input.range sliderprogress {
background: rgba(100, 0, 0, 80);
height: 7dp;
position: relative;
top: 8dp;
}
input.range sliderbar {
margin-left: -8dp;
margin-right: -7dp;
margin-top: -3dp;
width: 34dp;
height: 23dp;
z-index: 1;
decorator: image( range-bar );
}
input.range sliderbar:hover, input.range slidertrack:hover + sliderbar {
Expand Down
49 changes: 47 additions & 2 deletions Source/Core/Elements/WidgetSlider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ WidgetSlider::WidgetSlider(ElementFormControl* _parent)

track = nullptr;
bar = nullptr;
progress = nullptr;
arrows[0] = nullptr;
arrows[1] = nullptr;

Expand All @@ -71,6 +72,8 @@ WidgetSlider::~WidgetSlider()
parent->RemoveChild(bar);
if (track)
parent->RemoveChild(track);
if (progress)
parent->RemoveChild(progress);

parent->RemoveEventListener(EventId::Blur, this);
parent->RemoveEventListener(EventId::Focus, this);
Expand All @@ -94,15 +97,17 @@ bool WidgetSlider::Initialise()
// Create all of our child elements as standard elements, and abort if we can't create them.
ElementPtr track_element = Factory::InstanceElement(parent, "*", "slidertrack", XMLAttributes());
ElementPtr bar_element = Factory::InstanceElement(parent, "*", "sliderbar", XMLAttributes());
ElementPtr progress_element = Factory::InstanceElement(parent, "*", "sliderprogress", XMLAttributes());
ElementPtr arrow0_element = Factory::InstanceElement(parent, "*", "sliderarrowdec", XMLAttributes());
ElementPtr arrow1_element = Factory::InstanceElement(parent, "*", "sliderarrowinc", XMLAttributes());

if (!track_element || !bar_element || !arrow0_element || !arrow1_element)
if (!track_element || !bar_element || !progress_element || !arrow0_element || !arrow1_element)
return false;

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

Expand Down Expand Up @@ -155,6 +160,7 @@ void WidgetSlider::SetBarPosition(float _bar_position)
{
bar_position = Math::Clamp(_bar_position, 0.0f, 1.0f);
PositionBar();
ResizeProgress();
}

float WidgetSlider::GetBarPosition()
Expand Down Expand Up @@ -291,6 +297,7 @@ void WidgetSlider::FormatElements(const Vector2f containing_block, float slider_
offset.y += arrows[0]->GetBox().GetSize(BoxArea::Border).y + arrows[0]->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Bottom) +
track->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Top);
track->SetOffset(offset, parent);
progress->SetOffset(offset, parent);

offset.x = arrows[1]->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Left);
offset.y += track->GetBox().GetSize(BoxArea::Border).y + track->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Bottom) +
Expand All @@ -306,6 +313,7 @@ void WidgetSlider::FormatElements(const Vector2f containing_block, float slider_
track->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Left);
offset.y = track->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Top);
track->SetOffset(offset, parent);
progress->SetOffset(offset, parent);

offset.x += track->GetBox().GetSize(BoxArea::Border).x + track->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Right) +
arrows[1]->GetBox().GetEdge(BoxArea::Margin, BoxEdge::Left);
Expand All @@ -314,12 +322,14 @@ void WidgetSlider::FormatElements(const Vector2f containing_block, float slider_
}

FormatBar();
FormatProgress();

if (parent->IsDisabled())
{
// Propagate disabled state to child elements
bar->SetPseudoClass("disabled", true);
track->SetPseudoClass("disabled", true);
progress->SetPseudoClass("disabled", true);
arrows[0]->SetPseudoClass("disabled", true);
arrows[1]->SetPseudoClass("disabled", true);
}
Expand All @@ -346,6 +356,26 @@ void WidgetSlider::FormatBar()
PositionBar();
}

void WidgetSlider::FormatProgress()
{
Box progress_box;
ElementUtilities::BuildBox(progress_box, parent->GetBox().GetSize(), progress);
auto& computed = progress->GetComputedValues();

Vector2f progress_box_content = progress_box.GetSize();
if (orientation == HORIZONTAL)
{
if (computed.height().type == Style::Height::Auto)
progress_box_content.y = track->GetBox().GetSize().y;
}

// Set the new dimensions on the progress element to re-decorate it.
progress_box.SetContent(progress_box_content);
progress->SetBox(progress_box);

ResizeProgress();
}

Element* WidgetSlider::GetParent() const
{
return parent;
Expand All @@ -364,7 +394,7 @@ void WidgetSlider::ProcessEvent(Event& event)
if (event.GetParameter("button", -1) != 0)
break;

if (event.GetTargetElement() == track)
if (event.GetTargetElement() == track || event.GetTargetElement() == progress)
{
float mouse_position, bar_halfsize;

Expand Down Expand Up @@ -559,6 +589,21 @@ void WidgetSlider::PositionBar()
}
}

void WidgetSlider::ResizeProgress()
{
Box progress_box = progress->GetBox();
Vector2f new_size = progress_box.GetSize();

if (orientation == VERTICAL) {
new_size.y = bar->GetOffsetTop();
} else {
new_size.x = bar->GetOffsetLeft();
}

progress_box.SetContent(new_size);
progress->SetBox(progress_box);
}

float WidgetSlider::SetValueInternal(float new_value, bool force_submit_change_event)
{
if (min_value < max_value)
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Elements/WidgetSlider.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class WidgetSlider final : public EventListener {
/// Lays out and positions the bar element.
void FormatBar();

/// Lays out and positions the progress element.
void FormatProgress();

/// Returns the widget's parent element.
Element* GetParent() const;

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

void PositionBar();
void ResizeProgress();

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

Expand Down