diff --git a/AK/AKString.h b/AK/AKString.h index cd13f7f722..27efb80051 100644 --- a/AK/AKString.h +++ b/AK/AKString.h @@ -61,6 +61,7 @@ public: { } + int to_int(bool& ok) const; unsigned to_uint(bool& ok) const; String to_lowercase() const diff --git a/AK/String.cpp b/AK/String.cpp index fbdaa350ef..0e1fce18c7 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -131,6 +131,14 @@ String String::from_byte_buffer(const ByteBuffer& buffer, ShouldChomp should_cho return String((const char*)buffer.pointer(), buffer.size(), should_chomp); } +// FIXME: Duh. +int String::to_int(bool& ok) const +{ + unsigned value = to_uint(ok); + ASSERT(ok); + return (int)value; +} + unsigned String::to_uint(bool& ok) const { unsigned value = 0; diff --git a/Applications/VisualBuilder/VBProperty.cpp b/Applications/VisualBuilder/VBProperty.cpp index e8c5ab20d6..1ce4ba0dca 100644 --- a/Applications/VisualBuilder/VBProperty.cpp +++ b/Applications/VisualBuilder/VBProperty.cpp @@ -1,13 +1,16 @@ #include "VBProperty.h" +#include "VBWidget.h" -VBProperty::VBProperty(const String& name, const GVariant& value) - : m_name(name) +VBProperty::VBProperty(VBWidget& widget, const String& name, const GVariant& value) + : m_widget(widget) + , m_name(name) , m_value(value) { } -VBProperty::VBProperty(const String& name, Function&& getter, Function&& setter) - : m_name(name) +VBProperty::VBProperty(VBWidget& widget, const String& name, Function&& getter, Function&& setter) + : m_widget(widget) + , m_name(name) , m_getter(move(getter)) , m_setter(move(setter)) { @@ -18,3 +21,13 @@ VBProperty::VBProperty(const String& name, Function&& VBProperty::~VBProperty() { } + +void VBProperty::set_value(const GVariant& value) +{ + if (m_value == value) + return; + m_value = value; + if (m_setter) + m_setter(*m_widget.gwidget(), value); + m_widget.property_did_change(); +} diff --git a/Applications/VisualBuilder/VBProperty.h b/Applications/VisualBuilder/VBProperty.h index 8ad14747d3..9d70c66c8e 100644 --- a/Applications/VisualBuilder/VBProperty.h +++ b/Applications/VisualBuilder/VBProperty.h @@ -5,16 +5,18 @@ #include class GWidget; +class VBWidget; class VBProperty { + friend class VBWidget; public: - VBProperty(const String& name, const GVariant& value); - VBProperty(const String& name, Function&& getter, Function&& setter); + VBProperty(VBWidget&, const String& name, const GVariant& value); + VBProperty(VBWidget&, const String& name, Function&& getter, Function&& setter); ~VBProperty(); String name() const { return m_name; } const GVariant& value() const { return m_value; } - void set_value(const GVariant& value) { m_value = value; } + void set_value(const GVariant&); bool is_readonly() const { return m_readonly; } void set_readonly(bool b) { m_readonly = b; } @@ -22,6 +24,7 @@ public: void sync(); private: + VBWidget& m_widget; String m_name; GVariant m_value; Function m_getter; diff --git a/Applications/VisualBuilder/VBWidget.cpp b/Applications/VisualBuilder/VBWidget.cpp index af473b6d09..162ef65d08 100644 --- a/Applications/VisualBuilder/VBWidget.cpp +++ b/Applications/VisualBuilder/VBWidget.cpp @@ -17,8 +17,9 @@ VBWidget::VBWidget(VBWidgetType type, VBForm& form) , m_form(form) , m_property_model(VBWidgetPropertyModel::create(*this)) { - m_gwidget = VBWidgetRegistry::build_gwidget(type, &form, m_properties); + m_gwidget = VBWidgetRegistry::build_gwidget(*this, type, &form, m_properties); m_form.m_gwidget_map.set(m_gwidget, this); + setup_properties(); } VBWidget::~VBWidget() @@ -87,54 +88,69 @@ void VBWidget::for_each_property(Function callback) } } -void VBWidget::synchronize_properties() +void VBWidget::add_property(const String& name, Function&& getter, Function&& setter) { - property("width").set_value(m_gwidget->width()); - property("height").set_value(m_gwidget->height()); - property("x").set_value(m_gwidget->x()); - property("y").set_value(m_gwidget->y()); - property("visible").set_value(m_gwidget->is_visible()); - property("enabled").set_value(m_gwidget->is_enabled()); - property("tooltip").set_value(m_gwidget->tooltip()); - property("background_color").set_value(m_gwidget->background_color()); - property("foreground_color").set_value(m_gwidget->foreground_color()); + auto& prop = property(name); + prop.m_getter = move(getter); + prop.m_setter = move(setter); +} + +#define VB_ADD_PROPERTY(gclass, name, getter, setter, variant_type) \ + add_property(name, \ + [] (auto& widget) -> GVariant { return ((const gclass&)widget).getter(); }, \ + [] (auto& widget, auto& value) { ((gclass&)widget).setter(value.to_ ## variant_type()); } \ + ) + +void VBWidget::setup_properties() +{ + VB_ADD_PROPERTY(GWidget, "width", width, set_width, int); + VB_ADD_PROPERTY(GWidget, "height", height, set_height, int); + VB_ADD_PROPERTY(GWidget, "x", x, set_x, int); + VB_ADD_PROPERTY(GWidget, "y", y, set_y, int); + VB_ADD_PROPERTY(GWidget, "visible", is_visible, set_visible, bool); + VB_ADD_PROPERTY(GWidget, "enabled", is_enabled, set_enabled, bool); + VB_ADD_PROPERTY(GWidget, "tooltip", tooltip, set_tooltip, string); + VB_ADD_PROPERTY(GWidget, "background_color", background_color, set_background_color, color); + VB_ADD_PROPERTY(GWidget, "foreground_color", foreground_color, set_foreground_color, color); if (m_type == VBWidgetType::GLabel) { - auto& widget = *static_cast(m_gwidget); - property("text").set_value(widget.text()); + VB_ADD_PROPERTY(GLabel, "text", text, set_text, string); } if (m_type == VBWidgetType::GButton) { - auto& widget = *static_cast(m_gwidget); - property("caption").set_value(widget.caption()); + VB_ADD_PROPERTY(GButton, "caption", caption, set_caption, string); } if (m_type == VBWidgetType::GScrollBar) { - auto& widget = *static_cast(m_gwidget); - property("min").set_value(widget.min()); - property("max").set_value(widget.max()); - property("value").set_value(widget.value()); - property("step").set_value(widget.step()); + VB_ADD_PROPERTY(GScrollBar, "min", min, set_min, int); + VB_ADD_PROPERTY(GScrollBar, "max", max, set_max, int); + VB_ADD_PROPERTY(GScrollBar, "value", value, set_value, int); + VB_ADD_PROPERTY(GScrollBar, "step", step, set_step, int); } if (m_type == VBWidgetType::GSpinBox) { - auto& widget = *static_cast(m_gwidget); - property("min").set_value(widget.min()); - property("max").set_value(widget.max()); - property("value").set_value(widget.value()); + VB_ADD_PROPERTY(GSpinBox, "min", min, set_min, int); + VB_ADD_PROPERTY(GSpinBox, "max", max, set_max, int); + VB_ADD_PROPERTY(GSpinBox, "value", value, set_value, int); } if (m_type == VBWidgetType::GProgressBar) { - auto& widget = *static_cast(m_gwidget); - property("min").set_value(widget.min()); - property("max").set_value(widget.max()); - property("value").set_value(widget.value()); + VB_ADD_PROPERTY(GProgressBar, "min", min, set_min, int); + VB_ADD_PROPERTY(GProgressBar, "max", max, set_max, int); + VB_ADD_PROPERTY(GProgressBar, "value", value, set_value, int); } if (m_type == VBWidgetType::GTextEditor) { - auto& widget = *static_cast(m_gwidget); - property("text").set_value(widget.text()); - property("ruler_visible").set_value(widget.is_ruler_visible()); + VB_ADD_PROPERTY(GTextEditor, "text", text, set_text, string); + VB_ADD_PROPERTY(GTextEditor, "ruler_visible", is_ruler_visible, set_ruler_visible, bool); + } +} + +void VBWidget::synchronize_properties() +{ + for (auto& prop : m_properties) { + if (prop->m_getter) + prop->m_value = prop->m_getter(*gwidget()); } m_property_model->update(); @@ -146,6 +162,11 @@ VBProperty& VBWidget::property(const String& name) if (prop->name() == name) return *prop; } - m_properties.append(make(name, GVariant())); + m_properties.append(make(*this, name, GVariant())); return *m_properties.last(); } + +void VBWidget::property_did_change() +{ + m_form.update(); +} diff --git a/Applications/VisualBuilder/VBWidget.h b/Applications/VisualBuilder/VBWidget.h index 1bcf5755d2..b865296209 100644 --- a/Applications/VisualBuilder/VBWidget.h +++ b/Applications/VisualBuilder/VBWidget.h @@ -9,6 +9,7 @@ #include "VBWidgetType.h" class GPainter; +class GVariant; class GWidget; class VBForm; class VBProperty; @@ -50,11 +51,16 @@ public: VBWidgetPropertyModel& property_model() { return *m_property_model; } + void setup_properties(); void synchronize_properties(); + void property_did_change(); + private: VBWidget(VBWidgetType, VBForm&); + void add_property(const String& name, Function&& getter, Function&& setter); + VBWidgetType m_type { VBWidgetType::None }; VBForm& m_form; GWidget* m_gwidget { nullptr }; diff --git a/Applications/VisualBuilder/VBWidgetRegistry.cpp b/Applications/VisualBuilder/VBWidgetRegistry.cpp index 85bc6cb444..14c27e8e81 100644 --- a/Applications/VisualBuilder/VBWidgetRegistry.cpp +++ b/Applications/VisualBuilder/VBWidgetRegistry.cpp @@ -73,16 +73,16 @@ static GWidget* build_gwidget(VBWidgetType type, GWidget* parent) } } -GWidget* VBWidgetRegistry::build_gwidget(VBWidgetType type, GWidget* parent, Vector>& properties) +GWidget* VBWidgetRegistry::build_gwidget(VBWidget& widget, VBWidgetType type, GWidget* parent, Vector>& properties) { auto* gwidget = ::build_gwidget(type, parent); - auto add_readonly_property = [&properties] (const String& name, const GVariant& value) { - auto property = make(name, value); + auto add_readonly_property = [&] (const String& name, const GVariant& value) { + auto property = make(widget, name, value); property->set_readonly(true); properties.append(move(property)); }; - auto add_property = [&properties] (const String& name, Function&& getter, Function&& setter) { - auto property = make(name, move(getter), move(setter)); + auto add_property = [&] (const String& name, Function&& getter, Function&& setter) { + auto property = make(widget, name, move(getter), move(setter)); properties.append(move(property)); }; add_readonly_property("class", to_class_name(type)); diff --git a/Applications/VisualBuilder/VBWidgetRegistry.h b/Applications/VisualBuilder/VBWidgetRegistry.h index d89611f2c8..d2af1f941e 100644 --- a/Applications/VisualBuilder/VBWidgetRegistry.h +++ b/Applications/VisualBuilder/VBWidgetRegistry.h @@ -7,6 +7,7 @@ class GWidget; class VBProperty; +class VBWidget; class VBWidgetRegistry { public: @@ -16,5 +17,5 @@ public: callback((VBWidgetType)i); } - static GWidget* build_gwidget(VBWidgetType, GWidget* parent, Vector>&); + static GWidget* build_gwidget(VBWidget&, VBWidgetType, GWidget* parent, Vector>&); }; diff --git a/LibGUI/GProgressBar.h b/LibGUI/GProgressBar.h index c3f0dd671b..2429dcf0ae 100644 --- a/LibGUI/GProgressBar.h +++ b/LibGUI/GProgressBar.h @@ -8,6 +8,8 @@ public: virtual ~GProgressBar() override; void set_range(int min, int max); + void set_min(int min) { set_range(min, max()); } + void set_max(int max) { set_range(min(), max); } void set_value(int); int value() const { return m_value; } diff --git a/LibGUI/GScrollBar.h b/LibGUI/GScrollBar.h index 4d13af190d..659a2b09d2 100644 --- a/LibGUI/GScrollBar.h +++ b/LibGUI/GScrollBar.h @@ -16,6 +16,8 @@ public: int step() const { return m_step; } int big_step() const { return m_big_step; } + void set_min(int min) { set_range(min, max()); } + void set_max(int max) { set_range(min(), max); } void set_range(int min, int max); void set_value(int value); void set_step(int step) { m_step = step; } diff --git a/LibGUI/GSpinBox.h b/LibGUI/GSpinBox.h index a8e5d79a1e..7bce28ce7f 100644 --- a/LibGUI/GSpinBox.h +++ b/LibGUI/GSpinBox.h @@ -15,6 +15,8 @@ public: int min() const { return m_min; } int max() const { return m_max; } + void set_min(int min) { set_range(min, max()); } + void set_max(int max) { set_range(min(), max); } void set_range(int min, int max); Function on_change; diff --git a/LibGUI/GVariant.cpp b/LibGUI/GVariant.cpp index b61f08953d..e8c07e98e9 100644 --- a/LibGUI/GVariant.cpp +++ b/LibGUI/GVariant.cpp @@ -101,6 +101,17 @@ GVariant& GVariant::operator=(const GVariant& other) return *this; } +GVariant& GVariant::operator=(GVariant&& other) +{ + if (&other == this) + return *this; + // FIXME: Move, not copy! + clear(); + copy_from(other); + other.clear(); + return *this; +} + GVariant::GVariant(const GVariant& other) { copy_from(other); diff --git a/LibGUI/GVariant.h b/LibGUI/GVariant.h index b4855de43a..5fee08ef9d 100644 --- a/LibGUI/GVariant.h +++ b/LibGUI/GVariant.h @@ -22,7 +22,7 @@ public: GVariant& operator=(const GVariant&); GVariant(GVariant&&) = delete; - GVariant& operator=(GVariant&&) = delete; + GVariant& operator=(GVariant&&); void clear(); ~GVariant(); @@ -91,6 +91,13 @@ public: return as_bool() ? 1 : 0; if (is_float()) return (int)as_float(); + if (is_string()) { + bool ok; + int value = as_string().to_int(ok); + if (!ok) + return 0; + return value; + } return 0; }