diff --git a/meson.build b/meson.build index 40b314e..7da0c32 100644 --- a/meson.build +++ b/meson.build @@ -7,10 +7,11 @@ xlib_dep = dependency('x11') executable( 'ravenapp', './src/Box.cpp', - './src/Widget.cpp', - './src/Button.cpp', './src/Painter.cpp', './src/Window.cpp', + './src/Widget.cpp', + './src/Button.cpp', + './src/Label.cpp', './src/main.cpp', dependencies : [cairomm_dep, xlib_dep, pangocairo_dep] ) diff --git a/src/Events.hpp b/src/Events.hpp index 42fb5b6..26df59c 100644 --- a/src/Events.hpp +++ b/src/Events.hpp @@ -66,15 +66,16 @@ public: class WidgetRepaintRequestedEvent : public Event { private: - Box m_repaint_area; + bool m_should_do_group { false }; public: - WidgetRepaintRequestedEvent(Box repaint_area) - : m_repaint_area(repaint_area) {} + WidgetRepaintRequestedEvent(bool should_do_group) + : m_should_do_group(should_do_group) {} EventType get_type() { return EventType::WidgetRepaintRequested; } const char *get_name() { return "WidgetRepaintRequested"; } - Box &get_repaint_area() { return m_repaint_area; } + bool should_do_group() { return m_should_do_group; } + void set_should_do_group(bool should_do_group) { m_should_do_group = should_do_group; } }; class FocusUpdateEvent : public Event { diff --git a/src/Label.cpp b/src/Label.cpp new file mode 100644 index 0000000..e35b784 --- /dev/null +++ b/src/Label.cpp @@ -0,0 +1,25 @@ +#include "Label.hpp" +#include "Window.hpp" + +namespace Raven { + +void Label::on_init() { + // the label will inherit the background color properties from the parent + if (Widget *parent = get_parent()) { + set_do_background_fill(parent->get_do_background_fill()); + set_background_fill_color(parent->get_background_fill_color()); + } + set_did_init(true); +} + +void Label::on_paint() { + auto painter = get_window()->get_painter(); + auto text_color = get_styles()->get_label_text_color(); + + painter.source_rgb(text_color); + painter.set_pango_font_description(get_styles()->get_controls_font_description()); + painter.text(get_current_geometry(), m_text, PaintTextAlign::Center); + painter.fill(); +} + +} \ No newline at end of file diff --git a/src/Label.hpp b/src/Label.hpp new file mode 100644 index 0000000..c2d9fb8 --- /dev/null +++ b/src/Label.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "Widget.hpp" +#include "PropMacros.hpp" + +namespace Raven { + +class Label : public Widget { +DEF_WIDGET_STYLE_PROP(text, std::string, "") + +private: +public: + Label(std::string text) + : Widget() + , m_text(text) {} + + ~Label() {} +protected: + void on_paint(); + void on_init(); +}; + +} diff --git a/src/Painter.cpp b/src/Painter.cpp index c745d28..9a6290a 100644 --- a/src/Painter.cpp +++ b/src/Painter.cpp @@ -76,4 +76,13 @@ void Painter::fill() { m_cairo->fill(); } +void Painter::begin_paint_group() { + m_cairo->push_group(); +} + +void Painter::end_paint_group() { + m_cairo->pop_group_to_source(); + m_cairo->paint(); +} + } diff --git a/src/Painter.hpp b/src/Painter.hpp index b22451d..5c8c307 100644 --- a/src/Painter.hpp +++ b/src/Painter.hpp @@ -3,6 +3,7 @@ #include "Box.hpp" #include "Point.hpp" #include "RGB.hpp" +#include "cairomm/xlib_surface.h" #include #include @@ -15,8 +16,8 @@ enum class PaintTextAlign { class Painter { private: - Cairo::RefPtr m_cairo; - PangoFontDescription *m_pango_font_description; + Cairo::RefPtr m_cairo { nullptr }; + PangoFontDescription *m_pango_font_description { nullptr }; public: Painter() {} @@ -32,6 +33,9 @@ public: void source_rgb(RGB &source_rgb); void fill(); + + void begin_paint_group(); + void end_paint_group(); }; } diff --git a/src/TopLevelStyles.hpp b/src/TopLevelStyles.hpp index 0f4d47e..dbbc59a 100644 --- a/src/TopLevelStyles.hpp +++ b/src/TopLevelStyles.hpp @@ -25,6 +25,8 @@ DEF_WIDGET_STYLE_PROP(button_focused_color, RGB, m_accent_color_darker) DEF_WIDGET_STYLE_PROP(button_active_color, RGB, m_accent_color_darkest) DEF_WIDGET_STYLE_PROP(button_border_radius, double, 8.0) +DEF_WIDGET_STYLE_PROP(label_text_color, RGB, m_text_color) + private: Window *m_window; public: diff --git a/src/Widget.cpp b/src/Widget.cpp index c051095..59ab9ee 100644 --- a/src/Widget.cpp +++ b/src/Widget.cpp @@ -13,13 +13,6 @@ void Widget::set_window(Window *window) { on_init(); } -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; @@ -51,34 +44,42 @@ void Widget::do_generic_paint() { } } -void Widget::handle_repaint_requested(WidgetRepaintRequestedEvent &event) { +void Widget::wants_repaint() { + // TODOO: not necessary + m_window->widget_repaint(this); +} + +void Widget::handle_repaint(WidgetRepaintRequestedEvent &event) { + // immediately accept the event - we will do our own propagation logic + event.accept(); + if (!m_did_init) return; - // 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)) { - event.accept(); // consume this event so that it won't bubble down to the other children - return; - } - - // before calling this widget's paint function, we - // will perform generic widget painting behavior (background color, etc) - do_generic_paint(); - - // we're going to call this widget's paint handler function - // we will also save and then restore the cairo context - // to prevent the state (e.g. source rgb) for leaking onto other widgets auto painter = m_window->get_painter(); auto cr = painter.get_cairo(); + auto event_should_do_group = event.should_do_group(); + + if (event_should_do_group) { + painter.begin_paint_group(); + } + + do_generic_paint(); cr->save(); on_paint(); cr->restore(); - // we are not going to call accept() on this event as we want it to bubble down - // to all the other relevant children + // we will propagate this event to all of our children, except it will have + // should_do_group set to false + event.set_should_do_group(false); + for (auto& child : m_children) { + child->dispatch_event(event); + } + + if (event_should_do_group) { + painter.end_paint_group(); + } } void Widget::handle_mouse_move_event(MouseMoveEvent &event) { @@ -137,7 +138,7 @@ void Widget::dispatch_event(Event &event) { break; } case EventType::WidgetRepaintRequested: { - handle_repaint_requested(reinterpret_cast(event)); + handle_repaint(reinterpret_cast(event)); break; } /* these events aren't handled here, as they won't be dispatched to us from other places */ diff --git a/src/Widget.hpp b/src/Widget.hpp index 4eca2ba..70a6279 100644 --- a/src/Widget.hpp +++ b/src/Widget.hpp @@ -62,17 +62,17 @@ public: void dispatch_event(Event &event); void wants_repaint(); + + virtual ~Widget() {}; +protected: virtual void on_init() { set_did_init(true); } virtual void on_mouse_button(MouseButtonEvent &event) {} virtual void on_mouse_move(MouseMoveEvent &event) {} virtual void on_focus_update(FocusUpdateEvent &event) {} virtual void on_activation_update(ActivationUpdateEvent &event) {} virtual void on_paint() {} - - - virtual ~Widget() {}; private: - void handle_repaint_requested(WidgetRepaintRequestedEvent &event); + void handle_repaint(WidgetRepaintRequestedEvent& event); void do_generic_paint(); void handle_mouse_move_event(MouseMoveEvent &event); void handle_mouse_button_event(MouseButtonEvent &event); diff --git a/src/Window.cpp b/src/Window.cpp index 69c2284..1173f3d 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -60,6 +60,11 @@ bool Window::spawn_window() { return true; } +void Window::widget_repaint(Widget *target) { + auto event = WidgetRepaintRequestedEvent(true); + target->dispatch_event(event); +} + bool Window::dispatch_to_main_widget(Event &event) { if (!m_main_widget) return false; @@ -68,13 +73,9 @@ bool Window::dispatch_to_main_widget(Event &event) { return true; } -bool Window::dispatch_repaint_on_box(Box box) { - auto event = WidgetRepaintRequestedEvent(std::move(box)); - return dispatch_to_main_widget(event); -} - bool Window::dispatch_full_repaint() { - return dispatch_repaint_on_box(m_current_geometry); + auto event = WidgetRepaintRequestedEvent(true); + return dispatch_to_main_widget(event); } void Window::run(bool block) { @@ -107,8 +108,9 @@ void Window::run(bool block) { } case Expose: { if (e.xexpose.count == 0) { - auto box = Box(e.xexpose.x, e.xexpose.y, e.xexpose.width, e.xexpose.height); - dispatch_repaint_on_box(std::move(box)); + // TODO: hack + // might run into issues with other X implementations... :( + dispatch_full_repaint(); } break; } diff --git a/src/Window.hpp b/src/Window.hpp index 50e38d7..9368766 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include "Widget.hpp" #include "Painter.hpp" #include @@ -33,7 +34,8 @@ public: Widget *get_main_widget() { return m_main_widget; } void set_main_widget(Widget *main_widget); - // TODO: add setter for top_level_styles + void widget_repaint(Widget *target); + std::shared_ptr get_top_level_styles() { return m_top_level_styles; } diff --git a/src/main.cpp b/src/main.cpp index 17e3edc..918cd63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,25 +1,31 @@ +#include "Label.hpp" #include "Window.hpp" #include "Widget.hpp" #include "Button.hpp" #include "Box.hpp" +#include "Label.hpp" int main() { Raven::Window window {}; Raven::Widget main_widget {}; Raven::Button button {"click me!"}; + Raven::Label label {"you have not yet clicked the button..."}; window.spawn_window(); button.set_current_geometry(Raven::Box(10, 10, 100, 30)); + label.set_current_geometry(Raven::Box(10, 45, 100, 20)); + main_widget.set_background_fill_color(window.get_top_level_styles()->get_background_color()); main_widget.set_do_background_fill(true); window.set_main_widget(&main_widget); - button.on_click = [&button]() { - button.set_text("clicked!"); + button.on_click = [&label]() { + label.set_text("you clicked it!!!!!"); }; main_widget.add_child(&button); + main_widget.add_child(&label); window.run(true); return 0;