From 3a33b8ea0813ab769f0bfa7348428dfccefb0898 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 19 Apr 2019 01:05:59 +0200 Subject: [PATCH] VisualBuilder: Hook up everything needed for widget property editing. It's now possible to edit widget properties inline in the properties window. We're currently relying on the basic GVariant conversion functions to do all the "parsing" but that's not gonna be good enough. --- AK/AKString.h | 1 + AK/String.cpp | 8 ++ Applications/VisualBuilder/VBProperty.cpp | 21 ++++- Applications/VisualBuilder/VBProperty.h | 9 +- Applications/VisualBuilder/VBWidget.cpp | 85 ++++++++++++------- Applications/VisualBuilder/VBWidget.h | 6 ++ .../VisualBuilder/VBWidgetRegistry.cpp | 10 +-- Applications/VisualBuilder/VBWidgetRegistry.h | 3 +- LibGUI/GProgressBar.h | 2 + LibGUI/GScrollBar.h | 2 + LibGUI/GSpinBox.h | 2 + LibGUI/GVariant.cpp | 11 +++ LibGUI/GVariant.h | 9 +- 13 files changed, 123 insertions(+), 46 deletions(-) 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; }