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.
This commit is contained in:
parent
18785ba5c3
commit
3a33b8ea08
Notes:
sideshowbarker
2024-07-19 14:39:32 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/3a33b8ea081
13 changed files with 123 additions and 46 deletions
|
@ -61,6 +61,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
int to_int(bool& ok) const;
|
||||
unsigned to_uint(bool& ok) const;
|
||||
|
||||
String to_lowercase() const
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& setter)
|
||||
: m_name(name)
|
||||
VBProperty::VBProperty(VBWidget& widget, const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& 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<GVariant(const GWidget&)>&&
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
#include <LibGUI/GVariant.h>
|
||||
|
||||
class GWidget;
|
||||
class VBWidget;
|
||||
|
||||
class VBProperty {
|
||||
friend class VBWidget;
|
||||
public:
|
||||
VBProperty(const String& name, const GVariant& value);
|
||||
VBProperty(const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& setter);
|
||||
VBProperty(VBWidget&, const String& name, const GVariant& value);
|
||||
VBProperty(VBWidget&, const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& 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<GVariant(const GWidget&)> m_getter;
|
||||
|
|
|
@ -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<void(VBProperty&)> callback)
|
|||
}
|
||||
}
|
||||
|
||||
void VBWidget::synchronize_properties()
|
||||
void VBWidget::add_property(const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& 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<GLabel*>(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<GButton*>(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<GScrollBar*>(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<GSpinBox*>(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<GProgressBar*>(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<GTextEditor*>(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<VBProperty>(name, GVariant()));
|
||||
m_properties.append(make<VBProperty>(*this, name, GVariant()));
|
||||
return *m_properties.last();
|
||||
}
|
||||
|
||||
void VBWidget::property_did_change()
|
||||
{
|
||||
m_form.update();
|
||||
}
|
||||
|
|
|
@ -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<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& setter);
|
||||
|
||||
VBWidgetType m_type { VBWidgetType::None };
|
||||
VBForm& m_form;
|
||||
GWidget* m_gwidget { nullptr };
|
||||
|
|
|
@ -73,16 +73,16 @@ static GWidget* build_gwidget(VBWidgetType type, GWidget* parent)
|
|||
}
|
||||
}
|
||||
|
||||
GWidget* VBWidgetRegistry::build_gwidget(VBWidgetType type, GWidget* parent, Vector<OwnPtr<VBProperty>>& properties)
|
||||
GWidget* VBWidgetRegistry::build_gwidget(VBWidget& widget, VBWidgetType type, GWidget* parent, Vector<OwnPtr<VBProperty>>& properties)
|
||||
{
|
||||
auto* gwidget = ::build_gwidget(type, parent);
|
||||
auto add_readonly_property = [&properties] (const String& name, const GVariant& value) {
|
||||
auto property = make<VBProperty>(name, value);
|
||||
auto add_readonly_property = [&] (const String& name, const GVariant& value) {
|
||||
auto property = make<VBProperty>(widget, name, value);
|
||||
property->set_readonly(true);
|
||||
properties.append(move(property));
|
||||
};
|
||||
auto add_property = [&properties] (const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& setter) {
|
||||
auto property = make<VBProperty>(name, move(getter), move(setter));
|
||||
auto add_property = [&] (const String& name, Function<GVariant(const GWidget&)>&& getter, Function<void(GWidget&, const GVariant&)>&& setter) {
|
||||
auto property = make<VBProperty>(widget, name, move(getter), move(setter));
|
||||
properties.append(move(property));
|
||||
};
|
||||
add_readonly_property("class", to_class_name(type));
|
||||
|
|
|
@ -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<OwnPtr<VBProperty>>&);
|
||||
static GWidget* build_gwidget(VBWidget&, VBWidgetType, GWidget* parent, Vector<OwnPtr<VBProperty>>&);
|
||||
};
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<void(int value)> on_change;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue