From 529c286b8f2cd3652ce85568e9b855fd76b3a87d Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Wed, 9 Mar 2022 20:02:18 +0200 Subject: [PATCH] dispatch repaint events and lay yet more groundwork --- src/Box.cpp | 18 ++++++++++--- src/Box.hpp | 2 +- src/Button.cpp | 13 +++++++++ src/Button.hpp | 22 +++++++++++++++ src/Events.hpp | 8 +++++- src/Forward.hpp | 11 ++++++++ src/Painter.cpp | 22 +++++++++++++++ src/Painter.hpp | 3 +++ src/Widget.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- src/Widget.hpp | 30 ++++++++++++++++----- src/Window.cpp | 35 ++++++++++++++++++------ src/Window.hpp | 14 +++++++--- src/main.cpp | 10 +++++-- 13 files changed, 234 insertions(+), 25 deletions(-) create mode 100644 src/Button.cpp create mode 100644 src/Button.hpp create mode 100644 src/Forward.hpp diff --git a/src/Box.cpp b/src/Box.cpp index 05c890f..9e67b73 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -6,9 +6,21 @@ bool Box::contains_point(double x, double y) { return x >= m_x && m_x + m_width >= x && y >= m_y && m_y + m_height >= y; } -bool Box::contains_box(const Box &other) { - // FIXME - return false; +bool Box::contains_box(Box &other) const { + double ax1 = m_x; + double ax2 = m_x + m_width; + double ay1 = m_y; + double ay2 = m_y + m_height; + + double bx1 = other.get_x(); + double bx2 = other.get_x() + other.get_width(); + double by1 = other.get_y(); + double by2 = other.get_y() + other.get_height(); + + // https://stackoverflow.com/a/306332 + bool boxes_overlap = (ax1 < bx2 && ax2 > bx1 && ay1 < by2 && ay2 > by1); + + return boxes_overlap; } } diff --git a/src/Box.hpp b/src/Box.hpp index 34aead3..30fc523 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -30,7 +30,7 @@ public: void set_height(double height) { m_height = height; } bool contains_point(double x, double y); - bool contains_box(const Box &other); + bool contains_box(Box &other) const; }; } diff --git a/src/Button.cpp b/src/Button.cpp new file mode 100644 index 0000000..2bf5ca5 --- /dev/null +++ b/src/Button.cpp @@ -0,0 +1,13 @@ +#include "Button.hpp" +#include "Window.hpp" + +namespace Raven { + +void Button::on_paint() { + auto painter = get_window()->get_painter(); + auto ctx = painter.get_cairo(); + + +} + +} diff --git a/src/Button.hpp b/src/Button.hpp new file mode 100644 index 0000000..d264d7d --- /dev/null +++ b/src/Button.hpp @@ -0,0 +1,22 @@ +#include +#include "Widget.hpp" + +#pragma once + +namespace Raven { + +class Button : public Widget { +private: + std::string m_text; +public: + Button(std::string text) + : Widget() + , m_text(text) {} + + void set_text(std::string text) { m_text = text; wants_repaint(); } + std::string &get_text() { return m_text; } + + void on_paint(); +}; + +} diff --git a/src/Events.hpp b/src/Events.hpp index 81b60d4..1e3c50d 100644 --- a/src/Events.hpp +++ b/src/Events.hpp @@ -2,6 +2,7 @@ #include #include "Point.hpp" +#include "Box.hpp" namespace Raven { @@ -57,11 +58,16 @@ public: }; class WidgetRepaintRequestedEvent : public Event { +private: + Box m_repaint_area; public: - WidgetRepaintRequestedEvent() {} + WidgetRepaintRequestedEvent(Box repaint_area) + : m_repaint_area(repaint_area) {} EventType get_type() { return EventType::WidgetRepaintRequested; } const char *get_name() { return "WidgetRepaintRequested"; } + + Box &get_repaint_area() { return m_repaint_area; } }; } diff --git a/src/Forward.hpp b/src/Forward.hpp new file mode 100644 index 0000000..759a3c2 --- /dev/null +++ b/src/Forward.hpp @@ -0,0 +1,11 @@ +namespace Raven { + class Box; + class Button; + class Events; + class Painter; + class Point; + class Widget; + class Window; + + enum class EventType; +} diff --git a/src/Painter.cpp b/src/Painter.cpp index e69de29..ffa6f54 100644 --- a/src/Painter.cpp +++ b/src/Painter.cpp @@ -0,0 +1,22 @@ +#include "Painter.hpp" + +namespace Raven { + +void Painter::rounded_rectangle(Box &geometry, double border_radius) { + double aspect = 1.0; + double radius = border_radius / aspect; + double degrees = M_PI / 180.0; + double x = geometry.get_x(); + double y = geometry.get_y(); + double w = geometry.get_width(); + double h = geometry.get_height(); + + m_cairo->begin_new_sub_path(); + m_cairo->arc(x + w - radius, y + radius, radius, -90 * degrees, 0 * degrees); + m_cairo->arc(x + w - radius, y + h - radius, radius, 0 * degrees, 90 * degrees); + m_cairo->arc(x + radius, y + h - radius, radius, 90 * degrees, 180 * degrees); + m_cairo->arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees); + m_cairo->close_path(); +} + +} diff --git a/src/Painter.hpp b/src/Painter.hpp index b3446d1..a6658e8 100644 --- a/src/Painter.hpp +++ b/src/Painter.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Box.hpp" #include namespace Raven { @@ -12,6 +13,8 @@ public: Cairo::RefPtr get_cairo() { return m_cairo; } void set_cairo(Cairo::RefPtr cairo) { m_cairo = cairo; } + + void rounded_rectangle(Box &geometry, double border_radius); }; } diff --git a/src/Widget.cpp b/src/Widget.cpp index f762550..f223960 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -1,9 +1,78 @@ #include +#include #include "Widget.hpp" #include "Events.hpp" +#include "Window.hpp" namespace Raven { +void Widget::wants_repaint() { + if (!m_window) + return; + + m_window->dispatch_repaint_on_box(m_current_geometry); +} + +bool Widget::add_child(Widget *child) { + if (child->get_parent()) { + return false; + } + m_children.push_back(child); + child->set_parent(this); + // children inherit the window from the parent + // TODO?: what happens when the parent changes its window? + child->set_window(m_window); + return true; +} + +void Widget::remove_child(Widget *child) { + m_children.erase(std::remove(m_children.begin(), m_children.end(), child), m_children.end()); +} + +void Widget::handle_repaint_requested(WidgetRepaintRequestedEvent &event) { + std::cout << "-------\n"; + + std::cout << "-- repaint_area: " + << event.get_repaint_area().get_x() + << ", " + << event.get_repaint_area().get_y() + << ", " + << event.get_repaint_area().get_width() + << ", " + << event.get_repaint_area().get_height() + << " --\n"; + + std::cout << "-- m_current_geometry: " + << m_current_geometry.get_x() + << ", " + << m_current_geometry.get_y() + << ", " + << m_current_geometry.get_width() + << ", " + << m_current_geometry.get_height() + << " --\n"; + + std::cout << "-------\n"; + + // widgets contain all of thier children inside their geometry + // we will check if the event's repaint area (the area that needs to be repainted :)) + // overlaps with our geometry. If it does, we will proceed with the repaint process + if (!event.get_repaint_area().contains_box(m_current_geometry)) { + return; + } + + std::cout << "--- PASSED\n"; + + on_paint(); // we're going to call this widget's paint function + + // tell all children about this event + // the repaint area matching the geometry is checked at the start of + // this function, which means they'll repaint as well if it is needed + for (auto& child : m_children) { + child->dispatch_event(event); + } +} + void Widget::dispatch_event(Event &event) { process_event(event); } @@ -19,7 +88,7 @@ void Widget::process_event(Event &event) { break; } case EventType::WidgetRepaintRequested: { - on_paint(); + handle_repaint_requested(reinterpret_cast(event)); break; } case EventType::NoneEvent: { diff --git a/src/Widget.hpp b/src/Widget.hpp index 151f7bc..0a813e5 100644 --- a/src/Widget.hpp +++ b/src/Widget.hpp @@ -1,29 +1,47 @@ #pragma once +#include #include "Box.hpp" #include "Events.hpp" +#include "Forward.hpp" namespace Raven { class Widget { private: Box m_current_geometry {}; + std::vector m_children; + Widget *m_parent { nullptr }; + Window *m_window { nullptr }; public: - Widget() { + Widget() {} - } + Box &get_current_geometry() { return m_current_geometry; } + void set_current_geometry(Box current_geometry) { m_current_geometry = current_geometry; wants_repaint(); } + + std::vector &get_children() { return m_children; } + bool add_child(Widget *child); + void remove_child(Widget *child); + + Widget *get_parent() { return m_parent; } + void set_parent(Widget *parent) { m_parent = parent; } + + Window *get_window() { return m_window; } + void set_window(Window *window) { m_window = window; } - const Box &get_current_geometry() const { return m_current_geometry; } void dispatch_event(Event &event); + void wants_repaint(); + Widget *walk_until_top_level(); - virtual void on_mouse_button(MouseButtonEvent &event) = 0; - virtual void on_mouse_move(MouseMoveEvent &event) = 0; - virtual void on_paint() = 0; + virtual void on_mouse_button(MouseButtonEvent &event) {}; + virtual void on_mouse_move(MouseMoveEvent &event) {}; + virtual void on_paint() {}; virtual ~Widget() = default; private: void process_event(Event &event); + void handle_repaint_requested(WidgetRepaintRequestedEvent &event); }; } \ No newline at end of file diff --git a/src/Window.cpp b/src/Window.cpp index 2ab0cae..6b0131d 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -1,6 +1,5 @@ -#include #include -#include "Window.hpp" +#include #include #include #include @@ -8,8 +7,16 @@ #include #include +#include "Window.hpp" +#include "Events.hpp" + namespace Raven { +void Window::set_main_widget(Widget *main_widget) { + m_main_widget = main_widget; + m_main_widget->set_window(this); +} + bool Window::spawn_window() { Display *dsp = XOpenDisplay(NULL); if (dsp == NULL) { @@ -38,6 +45,19 @@ bool Window::spawn_window() { return true; } +void Window::dispatch_repaint_on_box(Box box) { + if (!m_main_widget) + return; + + auto event = WidgetRepaintRequestedEvent(std::move(box)); + + m_main_widget->dispatch_event(event); +} + +void Window::dispatch_full_repaint() { + dispatch_repaint_on_box(m_current_geometry); +} + void Window::run(bool block) { XEvent e; @@ -48,14 +68,13 @@ void Window::run(bool block) { break; switch (e.type) { - case MapNotify: { - std::cout << "MapNotify\n"; + case MapNotify: + case Expose: + std::cout << "gotta repaint!\n"; + dispatch_full_repaint(); break; - } - case Expose: { - std::cout << "we'd probably repaint the window right now\n"; + default: break; - } } } } diff --git a/src/Window.hpp b/src/Window.hpp index b450cc6..ef77a06 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -8,7 +8,8 @@ namespace Raven { class Window { private: - Widget *m_active_widget { nullptr }; + Widget *m_hovered_widget { nullptr }; + Widget *m_main_widget { nullptr }; Box m_current_geometry { 0, 0, 800, 600 }; Painter m_painter {}; @@ -18,8 +19,15 @@ public: Painter &get_painter() { return m_painter; } - Widget *get_active_widget() { return m_active_widget; } - void set_active_widget(Widget *active_widget) { m_active_widget = active_widget; } + Widget *get_hovered_widget() { return m_hovered_widget; } + void set_hovered_widget(Widget *hovered_widget) { m_hovered_widget = hovered_widget; } + + Widget *get_main_widget() { return m_main_widget; } + void set_main_widget(Widget *main_widget); + + + void dispatch_repaint_on_box(Box box); + void dispatch_full_repaint(); Box &get_current_geometry() { return m_current_geometry; } diff --git a/src/main.cpp b/src/main.cpp index befbfae..638b76a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,17 @@ #include #include "Window.hpp" +#include "Widget.hpp" +#include "Box.hpp" int main() { Raven::Window window {}; + Raven::Widget main_widget {}; + + window.spawn_window(); + + main_widget.set_current_geometry(Raven::Box(10, 10, 100, 30)); + window.set_main_widget(&main_widget); - window.spawn_window(); window.run(true); - return 0; } \ No newline at end of file