Skip to content

Commit

Permalink
Overcome a limitation of binding data to checkboxes
Browse files Browse the repository at this point in the history
  • Loading branch information
ZombieRaccoon committed Aug 19, 2021
1 parent 85bbd93 commit f91d354
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 73 deletions.
61 changes: 11 additions & 50 deletions Source/Core/DataControllerDefault.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,17 @@

namespace Rml {

DataControllerValue::DataControllerValue(Element* element) : DataController(element)
DataControllerAttributeBased::DataControllerAttributeBased(Element* element, String attribute)
: DataController(element), attribute(std::move(attribute))
{}

DataControllerValue::~DataControllerValue()
DataControllerAttributeBased::~DataControllerAttributeBased()
{
if (Element* element = GetElement())
{
element->RemoveEventListener(EventId::Change, this);
}
}

bool DataControllerValue::Initialize(DataModel& model, Element* element, const String& variable_name, const String& /*modifier*/)
bool DataControllerAttributeBased::Initialize(DataModel& model, Element* element, const String& variable_name, const String& /*modifier*/)
{
RMLUI_ASSERT(element);

Expand All @@ -62,16 +61,16 @@ bool DataControllerValue::Initialize(DataModel& model, Element* element, const S
return true;
}

void DataControllerValue::ProcessEvent(Event& event)
void DataControllerAttributeBased::ProcessEvent(Event& event)
{
if (Element* element = GetElement())
if (const Element* element = GetElement())
{
const auto& parameters = event.GetParameters();

auto it = parameters.find("value");
const auto it = parameters.find(attribute);
if (it == parameters.end())
{
Log::Message(Log::LT_WARNING, "A 'change' event was received, but it did not contain a value. During processing of 'data-value' in %s", element->GetAddress().c_str());
Log::Message(Log::LT_WARNING, "A 'change' event was received, but it did not contain the attribute '%s' when processing 'data-%s' in %s",
attribute.c_str(), attribute.c_str(), element->GetAddress().c_str());
return;
}

Expand All @@ -81,55 +80,17 @@ void DataControllerValue::ProcessEvent(Event& event)

if (DataVariable variable = model->GetVariable(address))
{
if (SetValue(it->second, variable))
if (variable.Set(it->second))
model->DirtyVariable(address.front().name);
}
}
}

void DataControllerValue::Release()
void DataControllerAttributeBased::Release()
{
delete this;
}

bool DataControllerValue::SetValue(const Variant& value, DataVariable variable)
{
return variable.Set(value);
}


DataControllerChecked::DataControllerChecked(Element* element) : DataControllerValue(element)
{}


bool DataControllerChecked::SetValue(const Variant& value, DataVariable variable)
{
bool result = false;
Variant old_value;

if (variable.Get(old_value))
{
// Value will be empty if the button was just unchecked, otherwise it will take the 'value' attribute.
const String new_value = value.Get<String>();

if (old_value.GetType() == Variant::BOOL)
{
// If the client variable is a boolean type, we assume the button acts like a checkbox, and set the new checked state.
result = variable.Set(Variant(!new_value.empty()));
}
else
{
// Otherwise, we assume the button acts like a radio box. Then, we do nothing if the box was unchecked,
// and instead let only the newly checked box set the new value.
if (!new_value.empty())
result = variable.Set(value);
}
}

return result;
}



DataControllerEvent::DataControllerEvent(Element* element) : DataController(element)
{}
Expand Down
26 changes: 15 additions & 11 deletions Source/Core/DataControllerDefault.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,37 @@ class DataModel;
class DataExpression;
using DataExpressionPtr = UniquePtr<DataExpression>;


class DataControllerValue : public DataController, private EventListener {
class DataControllerAttributeBased : public DataController, private EventListener {
public:
DataControllerValue(Element* element);
~DataControllerValue();
DataControllerAttributeBased(Element* element, String attribute);
~DataControllerAttributeBased();

bool Initialize(DataModel& model, Element* element, const String& expression, const String& modifier) override;

protected:
private:
// Responds to 'Change' events.
void ProcessEvent(Event& event) override;

// Delete this.
void Release() override;

// Set the new value on the variable, returns true if it should be dirtied.
virtual bool SetValue(const Variant& new_value, DataVariable variable);

DataAddress address;
String attribute;
};


class DataControllerChecked final : public DataControllerValue {
class DataControllerAttributeBasedInstancer final : public DataControllerInstancer {
public:
DataControllerChecked(Element* element);
DataControllerAttributeBasedInstancer(String attribute)
: attribute(std::move(attribute))
{}

DataControllerPtr InstanceController(Element* element) override {
return DataControllerPtr(new DataControllerAttributeBased(element, attribute));
}

bool SetValue(const Variant& new_value, DataVariable variable) override;
private:
String attribute;
};


Expand Down
16 changes: 8 additions & 8 deletions Source/Core/Elements/InputTypeCheckbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@ bool InputTypeCheckbox::IsSubmitted()
// Checks for necessary functional changes in the control as a result of changed attributes.
bool InputTypeCheckbox::OnAttributeChange(const ElementAttributes& changed_attributes)
{
// Check if maxlength has been defined.
if (changed_attributes.find("checked") != changed_attributes.end())
if (changed_attributes.count("checked"))
{
bool checked = element->HasAttribute("checked");
const bool checked = element->HasAttribute("checked");
element->SetPseudoClass("checked", checked);

Dictionary parameters;
parameters["value"] = String(checked ? GetValue() : "");
element->DispatchEvent(EventId::Change, parameters);
const auto value = GetValue();
element->DispatchEvent(EventId::Change, {
{ "checked", Variant(checked) },
{ "value", Variant(checked ? (value.empty() ? "on" : value) : "") }
});
}

return true;
Expand All @@ -65,8 +66,7 @@ bool InputTypeCheckbox::OnAttributeChange(const ElementAttributes& changed_attri
// Checks for necessary functional changes in the control as a result of the event.
void InputTypeCheckbox::ProcessDefaultAction(Event& event)
{
if (event == EventId::Click &&
!element->IsDisabled())
if (event == EventId::Click && !element->IsDisabled())
{
if (element->HasAttribute("checked"))
element->RemoveAttribute("checked");
Expand Down
8 changes: 4 additions & 4 deletions Source/Core/Factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ struct DefaultInstancers {
DataViewInstancerDefault<DataViewFor> structural_data_view_for;

// Data binding controllers
DataControllerInstancerDefault<DataControllerValue> data_controller_value;
DataControllerAttributeBasedInstancer data_controller_checked { "checked" };
DataControllerInstancerDefault<DataControllerEvent> data_controller_event;
DataControllerInstancerDefault<DataControllerChecked> data_controller_checked;
DataControllerAttributeBasedInstancer data_controller_value { "value" };
};

static UniquePtr<DefaultInstancers> default_instancers;
Expand Down Expand Up @@ -279,9 +279,9 @@ bool Factory::Initialise()
RegisterDataViewInstancer(&default_instancers->structural_data_view_for, "for", true );

// Data binding controllers
RegisterDataControllerInstancer(&default_instancers->data_controller_value, "value");
RegisterDataControllerInstancer(&default_instancers->data_controller_event, "event");
RegisterDataControllerInstancer(&default_instancers->data_controller_checked, "checked");
RegisterDataControllerInstancer(&default_instancers->data_controller_event, "event");
RegisterDataControllerInstancer(&default_instancers->data_controller_value, "value");

// XML node handlers
XMLParser::RegisterNodeHandler("", MakeShared<XMLNodeHandlerDefault>());
Expand Down

0 comments on commit f91d354

Please sign in to comment.