Skip to content

Range input with dynamic min/max ignores initial value #806

@leximayfield

Description

@leximayfield

I'm using RmlUi 6.1.

I am attempting to make a slider with a minimum/maximum that is provided by the data model. The RML:

<input type="range" name="max_players" data-value="selected_max_players"
    data-attr-min="min_players" data-attr-max="max_players" step="1" kex-menu-nav />

The code:

template<typename T>
bool BindAddress(Rml::DataModelConstructor &cCtor, std::string_view strName, T *pAddr)
{
    return cCtor.BindFunc(
        Rml::String{strName},                     //
        [pAddr](Rml::Variant &v) { v = *pAddr; }, //
        [pAddr](const Rml::Variant &v) { *pAddr = v.Get<T>(); });
}

// In the binding function...
BindAddress(ctor, "min_players", &this->m_iMinPlayers);
BindAddress(ctor, "max_players", &this->m_iMaxPlayers);
BindAddress(ctor, "selected_max_players", &this->m_iSelectedMaxPlayers);

However, when I open the page with the range input, the minimum and maximum values are correct, but the value itself is set to 2. If I get rid of the data-attr-min/data-attr-max the value is set correctly.

I think I know why this is happening. When DataViewAttribute::Update is called, the attributes are set one at a time. When the code hits InputTypeRange::OnAttributeChange it turns out that the first thing that gets changed is min, which calls this method:

void WidgetSlider::SetMinValue(float _min_value)
{
	if (_min_value != min_value)
	{
		min_value = _min_value;
		SetBarPosition(SetValueInternal(value, false));
	}
}

The default value of a range input is 0, so it gets clamped to 2, which in turn dispatches a EventId::Change event, which sets the actual value to 2. This blows up whatever value was seeded in the model ahead of time.

I believe this is due to the way data views work. It does updates one at a time, instead of all at once, thus if you have multiple views on a single element, and changing one value observed by the data model mutates something else touched by the data model, things go sideways.

It seems like the cromulent thing to do would be to queue all of the changes for a given element, then execute the changes all in one go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions