dispatch repaint events and lay yet more groundwork

This commit is contained in:
hippoz 2022-03-09 20:02:18 +02:00
parent b4cfe309f3
commit 529c286b8f
Signed by: hippoz
GPG key ID: 7C52899193467641
13 changed files with 234 additions and 25 deletions

View file

@ -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;
}
}

View file

@ -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;
};
}

13
src/Button.cpp Normal file
View file

@ -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();
}
}

22
src/Button.hpp Normal file
View file

@ -0,0 +1,22 @@
#include <string>
#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();
};
}

View file

@ -2,6 +2,7 @@
#include <stdint.h>
#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; }
};
}

11
src/Forward.hpp Normal file
View file

@ -0,0 +1,11 @@
namespace Raven {
class Box;
class Button;
class Events;
class Painter;
class Point;
class Widget;
class Window;
enum class EventType;
}

View file

@ -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();
}
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "Box.hpp"
#include <cairomm/cairomm.h>
namespace Raven {
@ -12,6 +13,8 @@ public:
Cairo::RefPtr<Cairo::Context> get_cairo() { return m_cairo; }
void set_cairo(Cairo::RefPtr<Cairo::Context> cairo) { m_cairo = cairo; }
void rounded_rectangle(Box &geometry, double border_radius);
};
}

View file

@ -1,9 +1,78 @@
#include <iostream>
#include <algorithm>
#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<WidgetRepaintRequestedEvent&>(event));
break;
}
case EventType::NoneEvent: {

View file

@ -1,29 +1,47 @@
#pragma once
#include <vector>
#include "Box.hpp"
#include "Events.hpp"
#include "Forward.hpp"
namespace Raven {
class Widget {
private:
Box m_current_geometry {};
std::vector<Widget*> 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<Widget*> &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);
};
}

View file

@ -1,6 +1,5 @@
#include <X11/X.h>
#include <iostream>
#include "Window.hpp"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <cairomm/cairomm.h>
@ -8,8 +7,16 @@
#include <cairomm/surface.h>
#include <cairomm/xlib_surface.h>
#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;
}
}
}
}

View file

@ -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; }

View file

@ -1,11 +1,17 @@
#include <iostream>
#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;
}