mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 17:58:18 +03: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
Notes:
sideshowbarker
2024-07-19 14:45:43 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/c6ffb3e2b87
@ -1,8 +1,6 @@
|
||||
OBJS = \
|
||||
VBForm.o \
|
||||
VBWidget.o \
|
||||
VBButtonWidget.o \
|
||||
VBWidgetFactory.o \
|
||||
main.o
|
||||
|
||||
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 "VBWidget.h"
|
||||
#include "VBWidgetFactory.h"
|
||||
#include <LibGUI/GPainter.h>
|
||||
|
||||
VBForm::VBForm(const String& name, GWidget* parent)
|
||||
: GWidget(parent)
|
||||
, m_name(name)
|
||||
{
|
||||
set_fill_with_background_color(true);
|
||||
set_background_color(Color::LightGray);
|
||||
set_greedy_for_hits(true);
|
||||
|
||||
auto box1 = VBWidget::create(*this);
|
||||
box1->set_rect({ 10, 10, 61, 41 });
|
||||
auto box1 = VBWidget::create(WidgetType::GSpinBox, *this);
|
||||
box1->set_rect({ 10, 10, 61, 21 });
|
||||
m_widgets.append(move(box1));
|
||||
|
||||
auto box2 = VBWidget::create(*this);
|
||||
auto box2 = VBWidget::create(WidgetType::GTextEditor, *this);
|
||||
box2->set_rect({ 100, 100, 161, 141 });
|
||||
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 });
|
||||
m_widgets.append(move(button1));
|
||||
}
|
||||
@ -36,9 +37,14 @@ void VBForm::paint_event(GPaintEvent& event)
|
||||
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) {
|
||||
widget->paint(painter);
|
||||
if (widget->is_selected()) {
|
||||
for_each_direction([&] (Direction direction) {
|
||||
painter.fill_rect(widget->grabber_rect(direction), Color::Black);
|
||||
|
@ -9,6 +9,9 @@ public:
|
||||
explicit VBForm(const String& name, GWidget* parent = nullptr);
|
||||
virtual ~VBForm() override;
|
||||
|
||||
String name() const { return m_name; }
|
||||
void set_name(const String& name) { m_name = name; }
|
||||
|
||||
bool is_selected(const VBWidget&) const;
|
||||
VBWidget* widget_at(const Point&);
|
||||
|
||||
@ -17,6 +20,7 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void paint_event(GPaintEvent&) override;
|
||||
virtual void second_paint_event(GPaintEvent&) override;
|
||||
virtual void mousedown_event(GMouseEvent&) override;
|
||||
virtual void mousemove_event(GMouseEvent&) override;
|
||||
virtual void mouseup_event(GMouseEvent&) override;
|
||||
|
@ -1,16 +1,51 @@
|
||||
#include "VBWidget.h"
|
||||
#include "VBForm.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)
|
||||
: m_form(form)
|
||||
static GWidget* build_gwidget(WidgetType type, GWidget* parent)
|
||||
{
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return m_form.is_selected(*this);
|
||||
@ -22,21 +57,21 @@ Rect VBWidget::grabber_rect(Direction direction) const
|
||||
int half_grabber_size = grabber_size / 2;
|
||||
switch (direction) {
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
@ -51,9 +86,3 @@ Direction VBWidget::grabber_at(const Point& position) const
|
||||
});
|
||||
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>
|
||||
|
||||
class GPainter;
|
||||
class GWidget;
|
||||
class VBForm;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
enum class WidgetType {
|
||||
None,
|
||||
GWidget,
|
||||
GButton,
|
||||
GLabel,
|
||||
GSpinBox,
|
||||
GTextEditor,
|
||||
};
|
||||
|
||||
class VBWidget : public Retainable<VBWidget>, public Weakable<VBWidget> {
|
||||
public:
|
||||
static Retained<VBWidget> create(VBForm& form) { return adopt(*new VBWidget(form)); }
|
||||
virtual ~VBWidget();
|
||||
static Retained<VBWidget> create(WidgetType type, VBForm& form) { return adopt(*new VBWidget(type, form)); }
|
||||
~VBWidget();
|
||||
|
||||
bool is_selected() const;
|
||||
|
||||
Rect rect() const { return m_rect; }
|
||||
void set_rect(const Rect& rect) { m_rect = rect; }
|
||||
Rect rect() const;
|
||||
void set_rect(const Rect&);
|
||||
|
||||
Rect grabber_rect(Direction) const;
|
||||
Direction grabber_at(const Point&) const;
|
||||
|
||||
virtual void paint(GPainter&);
|
||||
|
||||
virtual const char* gwidget_name() const { return "GWidget"; }
|
||||
GWidget* gwidget() { return m_gwidget; }
|
||||
|
||||
protected:
|
||||
explicit VBWidget(VBForm&);
|
||||
VBWidget(WidgetType, VBForm&);
|
||||
|
||||
private:
|
||||
WidgetType m_type { WidgetType::None };
|
||||
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));
|
||||
|
||||
auto* window = new GWindow;
|
||||
window->set_title("Form1");
|
||||
window->set_title(form1->name());
|
||||
window->set_rect(20, 200, 640, 400);
|
||||
window->set_main_widget(form1);
|
||||
window->set_should_exit_event_loop_on_close(true);
|
||||
|
@ -115,6 +115,7 @@ void GWidget::handle_paint_event(GPaintEvent& event)
|
||||
child->event(local_event);
|
||||
}
|
||||
}
|
||||
second_paint_event(event);
|
||||
}
|
||||
|
||||
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&)
|
||||
{
|
||||
}
|
||||
@ -282,6 +287,8 @@ Rect GWidget::screen_relative_rect() const
|
||||
|
||||
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) {
|
||||
if (!children()[i]->is_widget())
|
||||
continue;
|
||||
|
@ -56,6 +56,9 @@ public:
|
||||
virtual void leave_event(CEvent&);
|
||||
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; }
|
||||
Point relative_position() const { return m_relative_rect.location(); }
|
||||
|
||||
@ -147,6 +150,9 @@ public:
|
||||
|
||||
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:
|
||||
virtual bool is_widget() const final { return true; }
|
||||
|
||||
@ -173,6 +179,7 @@ private:
|
||||
|
||||
bool m_fill_with_background_color { false };
|
||||
bool m_visible { true };
|
||||
bool m_greedy_for_hits { false };
|
||||
|
||||
CElapsedTimer m_click_clock;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user