mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 08:08:12 +00:00
VisualBuilder: Use real GWidgets instead of pretend VBWidgets.
That first design was the wrong idea. Instead, have VBWidget instantiate a GWidget of the appropriate type and parent it to the VBForm. We then use a new "greedy hit-testing" mechanism in GWidget to prevent any mouse events from reaching the VBForm's children. To paint the grabbers above the child widgets, I added a slightly hackish but kind of neat second_paint_event() that is called after a widget has painted all of his children. :^)
This commit is contained in:
parent
af070324db
commit
c6ffb3e2b8
12 changed files with 94 additions and 87 deletions
|
@ -1,8 +1,6 @@
|
||||||
OBJS = \
|
OBJS = \
|
||||||
VBForm.o \
|
VBForm.o \
|
||||||
VBWidget.o \
|
VBWidget.o \
|
||||||
VBButtonWidget.o \
|
|
||||||
VBWidgetFactory.o \
|
|
||||||
main.o
|
main.o
|
||||||
|
|
||||||
APP = VisualBuilder
|
APP = VisualBuilder
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
#include "VBButtonWidget.h"
|
|
||||||
#include <SharedGraphics/StylePainter.h>
|
|
||||||
#include <LibGUI/GPainter.h>
|
|
||||||
|
|
||||||
VBButtonWidget::VBButtonWidget(VBForm& form)
|
|
||||||
: VBWidget(form)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VBButtonWidget::~VBButtonWidget()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void VBButtonWidget::paint(GPainter& painter)
|
|
||||||
{
|
|
||||||
StylePainter::paint_button(painter, rect(), ButtonStyle::Normal, false);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "VBWidget.h"
|
|
||||||
|
|
||||||
class VBButtonWidget : public VBWidget {
|
|
||||||
public:
|
|
||||||
static Retained<VBButtonWidget> create(VBForm& form) { return adopt(*new VBButtonWidget(form)); }
|
|
||||||
virtual ~VBButtonWidget() override;
|
|
||||||
|
|
||||||
virtual void paint(GPainter&) override;
|
|
||||||
virtual const char* gwidget_name() const { return "GButton"; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit VBButtonWidget(VBForm&);
|
|
||||||
};
|
|
|
@ -1,23 +1,24 @@
|
||||||
#include "VBForm.h"
|
#include "VBForm.h"
|
||||||
#include "VBWidget.h"
|
#include "VBWidget.h"
|
||||||
#include "VBWidgetFactory.h"
|
|
||||||
#include <LibGUI/GPainter.h>
|
#include <LibGUI/GPainter.h>
|
||||||
|
|
||||||
VBForm::VBForm(const String& name, GWidget* parent)
|
VBForm::VBForm(const String& name, GWidget* parent)
|
||||||
: GWidget(parent)
|
: GWidget(parent)
|
||||||
|
, m_name(name)
|
||||||
{
|
{
|
||||||
set_fill_with_background_color(true);
|
set_fill_with_background_color(true);
|
||||||
set_background_color(Color::LightGray);
|
set_background_color(Color::LightGray);
|
||||||
|
set_greedy_for_hits(true);
|
||||||
|
|
||||||
auto box1 = VBWidget::create(*this);
|
auto box1 = VBWidget::create(WidgetType::GSpinBox, *this);
|
||||||
box1->set_rect({ 10, 10, 61, 41 });
|
box1->set_rect({ 10, 10, 61, 21 });
|
||||||
m_widgets.append(move(box1));
|
m_widgets.append(move(box1));
|
||||||
|
|
||||||
auto box2 = VBWidget::create(*this);
|
auto box2 = VBWidget::create(WidgetType::GTextEditor, *this);
|
||||||
box2->set_rect({ 100, 100, 161, 141 });
|
box2->set_rect({ 100, 100, 161, 141 });
|
||||||
m_widgets.append(move(box2));
|
m_widgets.append(move(box2));
|
||||||
|
|
||||||
auto button1 = VBWidgetFactory::create("GButton", *this);
|
auto button1 = VBWidget::create(WidgetType::GButton, *this);
|
||||||
button1->set_rect({ 200, 50, 101, 21 });
|
button1->set_rect({ 200, 50, 101, 21 });
|
||||||
m_widgets.append(move(button1));
|
m_widgets.append(move(button1));
|
||||||
}
|
}
|
||||||
|
@ -36,9 +37,14 @@ void VBForm::paint_event(GPaintEvent& event)
|
||||||
painter.set_pixel({ x, y }, Color::Black);
|
painter.set_pixel({ x, y }, Color::Black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VBForm::second_paint_event(GPaintEvent& event)
|
||||||
|
{
|
||||||
|
GPainter painter(*this);
|
||||||
|
painter.add_clip_rect(event.rect());
|
||||||
|
|
||||||
for (auto& widget : m_widgets) {
|
for (auto& widget : m_widgets) {
|
||||||
widget->paint(painter);
|
|
||||||
if (widget->is_selected()) {
|
if (widget->is_selected()) {
|
||||||
for_each_direction([&] (Direction direction) {
|
for_each_direction([&] (Direction direction) {
|
||||||
painter.fill_rect(widget->grabber_rect(direction), Color::Black);
|
painter.fill_rect(widget->grabber_rect(direction), Color::Black);
|
||||||
|
|
|
@ -9,6 +9,9 @@ public:
|
||||||
explicit VBForm(const String& name, GWidget* parent = nullptr);
|
explicit VBForm(const String& name, GWidget* parent = nullptr);
|
||||||
virtual ~VBForm() override;
|
virtual ~VBForm() override;
|
||||||
|
|
||||||
|
String name() const { return m_name; }
|
||||||
|
void set_name(const String& name) { m_name = name; }
|
||||||
|
|
||||||
bool is_selected(const VBWidget&) const;
|
bool is_selected(const VBWidget&) const;
|
||||||
VBWidget* widget_at(const Point&);
|
VBWidget* widget_at(const Point&);
|
||||||
|
|
||||||
|
@ -17,6 +20,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paint_event(GPaintEvent&) override;
|
virtual void paint_event(GPaintEvent&) override;
|
||||||
|
virtual void second_paint_event(GPaintEvent&) override;
|
||||||
virtual void mousedown_event(GMouseEvent&) override;
|
virtual void mousedown_event(GMouseEvent&) override;
|
||||||
virtual void mousemove_event(GMouseEvent&) override;
|
virtual void mousemove_event(GMouseEvent&) override;
|
||||||
virtual void mouseup_event(GMouseEvent&) override;
|
virtual void mouseup_event(GMouseEvent&) override;
|
||||||
|
|
|
@ -1,16 +1,51 @@
|
||||||
#include "VBWidget.h"
|
#include "VBWidget.h"
|
||||||
#include "VBForm.h"
|
#include "VBForm.h"
|
||||||
#include <LibGUI/GPainter.h>
|
#include <LibGUI/GPainter.h>
|
||||||
|
#include <LibGUI/GLabel.h>
|
||||||
|
#include <LibGUI/GButton.h>
|
||||||
|
#include <LibGUI/GSpinBox.h>
|
||||||
|
#include <LibGUI/GTextEditor.h>
|
||||||
|
|
||||||
VBWidget::VBWidget(VBForm& form)
|
static GWidget* build_gwidget(WidgetType type, GWidget* parent)
|
||||||
: m_form(form)
|
|
||||||
{
|
{
|
||||||
|
switch (type) {
|
||||||
|
case WidgetType::GWidget:
|
||||||
|
return new GWidget(parent);
|
||||||
|
case WidgetType::GLabel:
|
||||||
|
return new GLabel(parent);
|
||||||
|
case WidgetType::GButton:
|
||||||
|
return new GButton(parent);
|
||||||
|
case WidgetType::GSpinBox:
|
||||||
|
return new GSpinBox(parent);
|
||||||
|
case WidgetType::GTextEditor:
|
||||||
|
return new GTextEditor(GTextEditor::Type::MultiLine, parent);
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VBWidget::VBWidget(WidgetType type, VBForm& form)
|
||||||
|
: m_type(type)
|
||||||
|
, m_form(form)
|
||||||
|
{
|
||||||
|
m_gwidget = build_gwidget(type, &form);
|
||||||
}
|
}
|
||||||
|
|
||||||
VBWidget::~VBWidget()
|
VBWidget::~VBWidget()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect VBWidget::rect() const
|
||||||
|
{
|
||||||
|
return m_gwidget->relative_rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VBWidget::set_rect(const Rect& rect)
|
||||||
|
{
|
||||||
|
m_gwidget->set_relative_rect(rect);
|
||||||
|
}
|
||||||
|
|
||||||
bool VBWidget::is_selected() const
|
bool VBWidget::is_selected() const
|
||||||
{
|
{
|
||||||
return m_form.is_selected(*this);
|
return m_form.is_selected(*this);
|
||||||
|
@ -22,21 +57,21 @@ Rect VBWidget::grabber_rect(Direction direction) const
|
||||||
int half_grabber_size = grabber_size / 2;
|
int half_grabber_size = grabber_size / 2;
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case Direction::Left:
|
case Direction::Left:
|
||||||
return { m_rect.x() - half_grabber_size, m_rect.center().y() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().x() - half_grabber_size, rect().center().y() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::UpLeft:
|
case Direction::UpLeft:
|
||||||
return { m_rect.x() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().x() - half_grabber_size, rect().y() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::Up:
|
case Direction::Up:
|
||||||
return { m_rect.center().x() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().center().x() - half_grabber_size, rect().y() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::UpRight:
|
case Direction::UpRight:
|
||||||
return { m_rect.right() - half_grabber_size, m_rect.y() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().right() - half_grabber_size, rect().y() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::Right:
|
case Direction::Right:
|
||||||
return { m_rect.right() - half_grabber_size, m_rect.center().y() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().right() - half_grabber_size, rect().center().y() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::DownLeft:
|
case Direction::DownLeft:
|
||||||
return { m_rect.x() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().x() - half_grabber_size, rect().bottom() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::Down:
|
case Direction::Down:
|
||||||
return { m_rect.center().x() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().center().x() - half_grabber_size, rect().bottom() - half_grabber_size, grabber_size, grabber_size };
|
||||||
case Direction::DownRight:
|
case Direction::DownRight:
|
||||||
return { m_rect.right() - half_grabber_size, m_rect.bottom() - half_grabber_size, grabber_size, grabber_size };
|
return { rect().right() - half_grabber_size, rect().bottom() - half_grabber_size, grabber_size, grabber_size };
|
||||||
default:
|
default:
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -51,9 +86,3 @@ Direction VBWidget::grabber_at(const Point& position) const
|
||||||
});
|
});
|
||||||
return found_grabber;
|
return found_grabber;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VBWidget::paint(GPainter& painter)
|
|
||||||
{
|
|
||||||
painter.fill_rect(m_rect, Color::White);
|
|
||||||
painter.draw_rect(m_rect, Color::Black);
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <AK/Weakable.h>
|
#include <AK/Weakable.h>
|
||||||
|
|
||||||
class GPainter;
|
class GPainter;
|
||||||
|
class GWidget;
|
||||||
class VBForm;
|
class VBForm;
|
||||||
|
|
||||||
enum class Direction { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft };
|
enum class Direction { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft };
|
||||||
|
@ -22,27 +23,35 @@ inline void for_each_direction(Callback callback)
|
||||||
callback(Direction::DownLeft);
|
callback(Direction::DownLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class WidgetType {
|
||||||
|
None,
|
||||||
|
GWidget,
|
||||||
|
GButton,
|
||||||
|
GLabel,
|
||||||
|
GSpinBox,
|
||||||
|
GTextEditor,
|
||||||
|
};
|
||||||
|
|
||||||
class VBWidget : public Retainable<VBWidget>, public Weakable<VBWidget> {
|
class VBWidget : public Retainable<VBWidget>, public Weakable<VBWidget> {
|
||||||
public:
|
public:
|
||||||
static Retained<VBWidget> create(VBForm& form) { return adopt(*new VBWidget(form)); }
|
static Retained<VBWidget> create(WidgetType type, VBForm& form) { return adopt(*new VBWidget(type, form)); }
|
||||||
virtual ~VBWidget();
|
~VBWidget();
|
||||||
|
|
||||||
bool is_selected() const;
|
bool is_selected() const;
|
||||||
|
|
||||||
Rect rect() const { return m_rect; }
|
Rect rect() const;
|
||||||
void set_rect(const Rect& rect) { m_rect = rect; }
|
void set_rect(const Rect&);
|
||||||
|
|
||||||
Rect grabber_rect(Direction) const;
|
Rect grabber_rect(Direction) const;
|
||||||
Direction grabber_at(const Point&) const;
|
Direction grabber_at(const Point&) const;
|
||||||
|
|
||||||
virtual void paint(GPainter&);
|
GWidget* gwidget() { return m_gwidget; }
|
||||||
|
|
||||||
virtual const char* gwidget_name() const { return "GWidget"; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit VBWidget(VBForm&);
|
VBWidget(WidgetType, VBForm&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
WidgetType m_type { WidgetType::None };
|
||||||
VBForm& m_form;
|
VBForm& m_form;
|
||||||
Rect m_rect;
|
GWidget* m_gwidget { nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#include "VBWidgetFactory.h"
|
|
||||||
#include "VBButtonWidget.h"
|
|
||||||
|
|
||||||
Retained<VBWidget> VBWidgetFactory::create(const String& widget_name, VBForm& form)
|
|
||||||
{
|
|
||||||
if (widget_name == "GButton")
|
|
||||||
return VBButtonWidget::create(form);
|
|
||||||
return VBWidget::create(form);
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <AK/AKString.h>
|
|
||||||
#include <AK/Retained.h>
|
|
||||||
|
|
||||||
class VBForm;
|
|
||||||
class VBWidget;
|
|
||||||
|
|
||||||
class VBWidgetFactory {
|
|
||||||
public:
|
|
||||||
static Retained<VBWidget> create(const String& widget_name, VBForm&);
|
|
||||||
};
|
|
|
@ -46,7 +46,7 @@ int main(int argc, char** argv)
|
||||||
app.set_menubar(move(menubar));
|
app.set_menubar(move(menubar));
|
||||||
|
|
||||||
auto* window = new GWindow;
|
auto* window = new GWindow;
|
||||||
window->set_title("Form1");
|
window->set_title(form1->name());
|
||||||
window->set_rect(20, 200, 640, 400);
|
window->set_rect(20, 200, 640, 400);
|
||||||
window->set_main_widget(form1);
|
window->set_main_widget(form1);
|
||||||
window->set_should_exit_event_loop_on_close(true);
|
window->set_should_exit_event_loop_on_close(true);
|
||||||
|
|
|
@ -115,6 +115,7 @@ void GWidget::handle_paint_event(GPaintEvent& event)
|
||||||
child->event(local_event);
|
child->event(local_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
second_paint_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GWidget::set_layout(OwnPtr<GLayout>&& layout)
|
void GWidget::set_layout(OwnPtr<GLayout>&& layout)
|
||||||
|
@ -207,6 +208,10 @@ void GWidget::paint_event(GPaintEvent&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GWidget::second_paint_event(GPaintEvent&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void GWidget::show_event(GShowEvent&)
|
void GWidget::show_event(GShowEvent&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -282,6 +287,8 @@ Rect GWidget::screen_relative_rect() const
|
||||||
|
|
||||||
GWidget::HitTestResult GWidget::hit_test(int x, int y)
|
GWidget::HitTestResult GWidget::hit_test(int x, int y)
|
||||||
{
|
{
|
||||||
|
if (is_greedy_for_hits())
|
||||||
|
return { this, x, y };
|
||||||
for (int i = children().size() - 1; i >= 0; --i) {
|
for (int i = children().size() - 1; i >= 0; --i) {
|
||||||
if (!children()[i]->is_widget())
|
if (!children()[i]->is_widget())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -56,6 +56,9 @@ public:
|
||||||
virtual void leave_event(CEvent&);
|
virtual void leave_event(CEvent&);
|
||||||
virtual void child_event(CChildEvent&) override;
|
virtual void child_event(CChildEvent&) override;
|
||||||
|
|
||||||
|
// This is called after children have been painted.
|
||||||
|
virtual void second_paint_event(GPaintEvent&);
|
||||||
|
|
||||||
Rect relative_rect() const { return m_relative_rect; }
|
Rect relative_rect() const { return m_relative_rect; }
|
||||||
Point relative_position() const { return m_relative_rect.location(); }
|
Point relative_position() const { return m_relative_rect.location(); }
|
||||||
|
|
||||||
|
@ -147,6 +150,9 @@ public:
|
||||||
|
|
||||||
bool spans_entire_window_horizontally() const;
|
bool spans_entire_window_horizontally() const;
|
||||||
|
|
||||||
|
bool is_greedy_for_hits() const { return m_greedy_for_hits; }
|
||||||
|
void set_greedy_for_hits(bool b) { m_greedy_for_hits = b; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool is_widget() const final { return true; }
|
virtual bool is_widget() const final { return true; }
|
||||||
|
|
||||||
|
@ -173,6 +179,7 @@ private:
|
||||||
|
|
||||||
bool m_fill_with_background_color { false };
|
bool m_fill_with_background_color { false };
|
||||||
bool m_visible { true };
|
bool m_visible { true };
|
||||||
|
bool m_greedy_for_hits { false };
|
||||||
|
|
||||||
CElapsedTimer m_click_clock;
|
CElapsedTimer m_click_clock;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue