add improved style system for widgets

This commit is contained in:
hippoz 2022-06-11 15:52:23 +03:00
parent bfe126ab3c
commit 305279c0f7
Signed by: hippoz
GPG key ID: 7C52899193467641
17 changed files with 144 additions and 139 deletions

View file

@ -7,6 +7,7 @@ xlib_dep = dependency('x11')
executable( executable(
'ravenapp', 'ravenapp',
'./src/Box.cpp', './src/Box.cpp',
'./src/Styles.cpp',
'./src/Painter.cpp', './src/Painter.cpp',
'./src/Window.cpp', './src/Window.cpp',
'./src/Widget.cpp', './src/Widget.cpp',

View file

@ -3,6 +3,7 @@
#include "Box.hpp" #include "Box.hpp"
#include "Window.hpp" #include "Window.hpp"
#include "Painter.hpp" #include "Painter.hpp"
#include "Styles.hpp"
#include "pango/pango-layout.h" #include "pango/pango-layout.h"
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
@ -17,40 +18,21 @@ void Button::set_text(std::string text) {
} }
void Button::on_init() { void Button::on_init() {
set_background_border_radius(styles()->button_border_radius()); set_style(&default_button_style);
set_background_fill_color(styles()->button_normal_color());
set_do_background_fill(true);
fit_text(m_text); fit_text(m_text);
set_did_init(true); set_did_init(true);
} }
void Button::update_color() {
if (is_active()) {
set_background_fill_color(styles()->button_active_color());
} else if (is_focused()) {
set_background_fill_color(styles()->button_focused_color());
} else {
set_background_fill_color(styles()->button_normal_color());
}
}
void Button::on_paint() { void Button::on_paint() {
auto painter = window()->painter(); auto painter = window()->painter();
auto text_color = styles()->button_text_color();
painter.source_rgb(text_color); painter.source_rgb(style()->foreground());
painter.set_pango_font_description(styles()->controls_font_description()); painter.set_pango_font_description(style()->font_description());
painter.text(current_geometry(), m_text, PaintTextAlign::Center, PANGO_ELLIPSIZE_END, true); painter.text(current_geometry(), m_text, PaintTextAlign::Center, PANGO_ELLIPSIZE_END, true);
painter.fill(); painter.fill();
} }
void Button::on_focus_update(FocusUpdateEvent &event) {
update_color();
}
void Button::on_activation_update(ActivationUpdateEvent &event) { void Button::on_activation_update(ActivationUpdateEvent &event) {
update_color();
if (event.activation_status() == false) { if (event.activation_status() == false) {
on_click(); on_click();
} }

View file

@ -22,7 +22,6 @@ public:
protected: protected:
void on_paint(); void on_paint();
void on_init(); void on_init();
void on_focus_update(FocusUpdateEvent &event);
void on_activation_update(ActivationUpdateEvent &event); void on_activation_update(ActivationUpdateEvent &event);
private: private:
void update_color(); void update_color();

View file

@ -1,10 +1,10 @@
namespace Raven { namespace Raven {
class GenericStyle;
class Box; class Box;
class Button; class Button;
class Events; class Events;
class Painter; class Painter;
class Point; class Point;
class TopLevelStyles;
class Layout; class Layout;
class Widget; class Widget;
class Window; class Window;

49
src/GenericStyle.hpp Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include "pango/pango-font.h"
#include "RGB.hpp"
#define __RAVEN_STYLE_PROP(name, type, ...) \
private: \
type m_##name {__VA_ARGS__}; \
public: \
void set_##name(type new_prop_value) { m_##name = new_prop_value; style_prop_updated(); } \
type name() { return m_##name; } \
private:
namespace Raven {
class GenericStyle {
__RAVEN_STYLE_PROP(font_description, PangoFontDescription*, nullptr)
__RAVEN_STYLE_PROP(foreground, RGB, 0.00000, 0.00000, 0.00000)
__RAVEN_STYLE_PROP(background_norm, RGB, 1.00000, 1.00000, 1.00000)
__RAVEN_STYLE_PROP(background_focused, RGB, 1.00000, 1.00000, 1.00000)
__RAVEN_STYLE_PROP(background_active, RGB, 1.00000, 1.00000, 1.00000)
__RAVEN_STYLE_PROP(border_radius, double, 0.0)
__RAVEN_STYLE_PROP(fill_background, bool, true)
__RAVEN_STYLE_PROP(update_background, bool, false)
private:
void style_prop_updated() { }
public:
GenericStyle(
PangoFontDescription* font_description,
RGB foreground,
RGB background_norm,
RGB background_focused,
RGB background_active,
double border_radius,
bool fill_background,
bool update_background
)
: m_font_description(font_description)
, m_foreground(foreground)
, m_background_norm(background_norm)
, m_background_focused(background_focused)
, m_background_active(background_active)
, m_border_radius(border_radius)
, m_fill_background(fill_background)
, m_update_background(update_background) {}
};
}

View file

@ -1,6 +1,7 @@
#include "Label.hpp" #include "Label.hpp"
#include "Window.hpp" #include "Window.hpp"
#include "pango/pango-layout.h" #include "pango/pango-layout.h"
#include "Styles.hpp"
#include <iostream> #include <iostream>
namespace Raven { namespace Raven {
@ -13,18 +14,16 @@ void Label::set_text(std::string text) {
} }
} }
void Label::on_init() { void Label::on_init() {
set_style(&default_label_style);
fit_text(m_text); fit_text(m_text);
set_did_init(true); set_did_init(true);
set_do_background_fill(false);
} }
void Label::on_paint() { void Label::on_paint() {
auto painter = window()->painter(); auto painter = window()->painter();
auto text_color = styles()->label_text_color();
painter.source_rgb(text_color); painter.source_rgb(style()->foreground());
painter.set_pango_font_description(styles()->controls_font_description()); painter.set_pango_font_description(style()->font_description());
painter.text(current_geometry(), m_text, PaintTextAlign::Left, PANGO_ELLIPSIZE_NONE, true); painter.text(current_geometry(), m_text, PaintTextAlign::Left, PANGO_ELLIPSIZE_NONE, true);
painter.fill(); painter.fill();
} }

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "Widget.hpp" #include "Widget.hpp"
#include "PropMacros.hpp"
namespace Raven { namespace Raven {

View file

@ -83,7 +83,7 @@ Point Painter::text(Box &geometry, std::string &text, PaintTextAlign align, Pang
return {pango_units_to_double(pango_width), pango_units_to_double(pango_height)}; return {pango_units_to_double(pango_width), pango_units_to_double(pango_height)};
} }
void Painter::source_rgb(RGB &source_rgb) { void Painter::source_rgb(RGB source_rgb) {
m_cairo->set_source_rgb(source_rgb.r(), source_rgb.g(), source_rgb.b()); m_cairo->set_source_rgb(source_rgb.r(), source_rgb.g(), source_rgb.b());
} }

View file

@ -33,7 +33,7 @@ public:
bool can_paint() { if (m_cairo) return true; else return false; } bool can_paint() { if (m_cairo) return true; else return false; }
void source_rgb(RGB &source_rgb); void source_rgb(RGB source_rgb);
void fill(); void fill();
void begin_paint_group(); void begin_paint_group();

View file

@ -1,9 +0,0 @@
#pragma once
#define DEF_WIDGET_STYLE_PROP(name, type, ...) \
private: \
type m_##name {__VA_ARGS__}; \
public: \
void set_##name(type new_prop_value) { m_##name = new_prop_value; repaint(); } \
type name() { return m_##name; } \
private:

41
src/Styles.cpp Normal file
View file

@ -0,0 +1,41 @@
#include "Styles.hpp"
#include "pango/pango-font.h"
#include "src/RGB.hpp"
namespace Raven {
GenericStyle default_widget_style {
pango_font_description_from_string("sans-serif"),
RGB(0.00000, 0.00000, 0.00000),
RGB(1.00000, 1.00000, 1.00000),
RGB(1.00000, 1.00000, 1.00000),
RGB(1.00000, 1.00000, 1.00000),
0.0,
true,
false
};
GenericStyle default_button_style {
pango_font_description_from_string("sans-serif"),
RGB(0.00000, 0.00000, 0.00000),
RGB(0.73333, 0.40392, 0.89412),
RGB(0.63922, 0.27843, 0.81961),
RGB(0.54118, 0.18039, 0.72157),
6.0,
true,
true
};
GenericStyle default_label_style {
pango_font_description_from_string("sans-serif"),
RGB(0.00000, 0.00000, 0.00000),
RGB(0.00000, 0.00000, 0.00000),
RGB(0.00000, 0.00000, 0.00000),
RGB(0.00000, 0.00000, 0.00000),
0.0,
false,
false
};
}

11
src/Styles.hpp Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include "src/GenericStyle.hpp"
namespace Raven {
extern GenericStyle default_widget_style;
extern GenericStyle default_button_style;
extern GenericStyle default_label_style;
}

View file

@ -1,12 +0,0 @@
#include "TopLevelStyles.hpp"
#include "Window.hpp"
namespace Raven {
void TopLevelStyles::wants_repaint() {
// when one of the styles here changes, we will dispatch
// a full repaint to the entire window :^)
m_window->dispatch_full_repaint();
}
}

View file

@ -1,57 +0,0 @@
#pragma once
#include "Forward.hpp"
#include "pango/pango-font.h"
#include "PropMacros.hpp"
#include "RGB.hpp"
namespace Raven {
class TopLevelStyles {
DEF_WIDGET_STYLE_PROP(controls_font_description, PangoFontDescription*, nullptr)
/* FIXME: */
/* right now, these colors are gruvbox */
/* however the `accent_color_darkest` is a darkened version of `accent_color_darker` */
DEF_WIDGET_STYLE_PROP(text_color, RGB, 0.0, 0.0, 0.0)
DEF_WIDGET_STYLE_PROP(accent_color, RGB, 0.6941, 0.3843, 0.5254)
DEF_WIDGET_STYLE_PROP(accent_color_darker, RGB, 0.5607, 0.2470, 0.4431)
DEF_WIDGET_STYLE_PROP(accent_color_darkest, RGB, 0.48069, 0.1669, 0.3631)
DEF_WIDGET_STYLE_PROP(background_color, RGB, 0.9764, 0.9607, 0.8431)
DEF_WIDGET_STYLE_PROP(button_text_color, RGB, m_text_color)
DEF_WIDGET_STYLE_PROP(button_normal_color, RGB, m_accent_color)
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, 6.0)
DEF_WIDGET_STYLE_PROP(label_text_color, RGB, m_text_color)
private:
Window *m_window;
public:
TopLevelStyles(Window *window)
: m_window(window)
{
PangoFontDescription *font_description = pango_font_description_new();
pango_font_description_set_family(font_description, "sans-serif");
pango_font_description_set_weight(font_description, PANGO_WEIGHT_NORMAL);
pango_font_description_set_absolute_size(font_description, 16 * PANGO_SCALE);
m_controls_font_description = font_description;
}
~TopLevelStyles() {
if (m_controls_font_description) {
pango_font_description_free(m_controls_font_description);
}
}
Window *window() { return m_window; }
void set_window(Window *window) { m_window = window; }
void repaint();
};
}

View file

@ -21,7 +21,7 @@ void Widget::fit_text(std::string &text) {
if (!window()) if (!window())
return; return;
window()->painter().set_pango_font_description(styles()->controls_font_description()); window()->painter().set_pango_font_description(m_style->font_description());
auto size = window()->painter().compute_text_size(current_geometry(), text); auto size = window()->painter().compute_text_size(current_geometry(), text);
if (!resize(size)) { if (!resize(size)) {
reflow(); reflow();
@ -65,7 +65,6 @@ void Widget::set_window(Window *window) {
throw std::logic_error{"null window value for set_window"}; throw std::logic_error{"null window value for set_window"};
m_window = window; m_window = window;
m_styles = m_window->top_level_styles();
on_init(); on_init();
} }
@ -122,14 +121,24 @@ void Widget::handle_repaint_rect(RepaintRectEvent &event) {
cr->save(); cr->save();
// clip this widget. this ensures that the background fill or children won't overflow the bounds of it // clip this widget. this ensures that the background fill or children won't overflow the bounds of it
painter.rounded_rectangle(m_current_geometry, m_background_border_radius); painter.rounded_rectangle(m_current_geometry, m_style->border_radius());
cr->clip(); cr->clip();
// paint the background fill // paint the background fill
// note that we're using the bounds of the paint event as the rectangle, this ensures that we dont draw over other widgets which won't be repainted. // note that we're using the bounds of the paint event as the rectangle, this ensures that we dont draw over other widgets which won't be repainted.
if (m_do_background_fill) { if (m_style->fill_background()) {
painter.source_rgb(m_background_fill_color); if (m_style->update_background()) {
painter.rounded_rectangle(event.box(), m_background_border_radius); if (m_is_focused) {
painter.source_rgb(m_style->background_focused());
} else if (m_is_active) {
painter.source_rgb(m_style->background_active());
} else {
painter.source_rgb(m_style->background_norm());
}
} else {
painter.source_rgb(m_style->background_norm());
}
painter.rounded_rectangle(event.box(), m_style->border_radius());
cr->fill(); cr->fill();
} }
@ -176,6 +185,10 @@ void Widget::handle_mouse_move_event(MouseMoveEvent &event) {
auto focus_update_event = FocusUpdateEvent(update_focus_to); auto focus_update_event = FocusUpdateEvent(update_focus_to);
on_focus_update(focus_update_event); on_focus_update(focus_update_event);
if (m_style->update_background()) {
repaint();
}
} }
if (!m_consumes_hits) { if (!m_consumes_hits) {
@ -208,6 +221,10 @@ void Widget::handle_mouse_button_event(MouseButtonEvent &event) {
auto activation_update_event = ActivationUpdateEvent(update_activation_to); auto activation_update_event = ActivationUpdateEvent(update_activation_to);
on_activation_update(activation_update_event); on_activation_update(activation_update_event);
if (m_style->update_background()) {
repaint();
}
} }
if (!m_consumes_hits) { if (!m_consumes_hits) {
@ -260,9 +277,4 @@ void Widget::dispatch_event(Event &event) {
} }
} }
void Widget::on_init() {
set_background_fill_color(styles()->background_color());
set_did_init(true);
}
} }

View file

@ -9,8 +9,8 @@
#include "Events.hpp" #include "Events.hpp"
#include "Forward.hpp" #include "Forward.hpp"
#include "RGB.hpp" #include "RGB.hpp"
#include "TopLevelStyles.hpp" #include "src/GenericStyle.hpp"
#include "PropMacros.hpp" #include "src/Styles.hpp"
namespace Raven { namespace Raven {
@ -27,10 +27,6 @@ enum class ControlWidgetType {
}; };
class Widget { class Widget {
DEF_WIDGET_STYLE_PROP(do_background_fill, bool, true)
DEF_WIDGET_STYLE_PROP(background_fill_color, RGB, 0, 0, 0)
DEF_WIDGET_STYLE_PROP(background_border_radius, double, 0.0)
public: public:
Widget() {} Widget() {}
@ -73,14 +69,11 @@ public:
Window *window() { return m_window; } Window *window() { return m_window; }
void set_window(Window *window); void set_window(Window *window);
std::shared_ptr<TopLevelStyles> styles() { return m_styles; } GenericStyle *style() { return m_style; }
void set_styles(std::shared_ptr<TopLevelStyles> styles) { m_styles = styles; wants_relayout(); } void set_style(GenericStyle *style) { m_style = style; reflow(); }
void set_did_init(bool did_init) { m_did_init = did_init; }
bool did_init() { return m_did_init; } bool did_init() { return m_did_init; }
bool is_focused() { return m_is_focused; } bool is_focused() { return m_is_focused; }
bool is_active() { return m_is_active; } bool is_active() { return m_is_active; }
bool consumes_hits() { return m_consumes_hits; } bool consumes_hits() { return m_consumes_hits; }
@ -96,8 +89,6 @@ public:
std::shared_ptr<Layout> layout() { return m_layout; } std::shared_ptr<Layout> layout() { return m_layout; }
void dispatch_event(Event &event); void dispatch_event(Event &event);
void wants_repaint();
void wants_relayout();
template<typename T, class... Args> template<typename T, class... Args>
std::shared_ptr<T> set_layout(Args&&... args) { std::shared_ptr<T> set_layout(Args&&... args) {
@ -115,13 +106,15 @@ public:
protected: protected:
WidgetType m_type { WidgetType::Widget }; WidgetType m_type { WidgetType::Widget };
virtual void on_init(); virtual void on_init() { set_did_init(true); }
virtual void on_mouse_button(MouseButtonEvent &event) {} virtual void on_mouse_button(MouseButtonEvent &event) {}
virtual void on_mouse_move(MouseMoveEvent &event) {} virtual void on_mouse_move(MouseMoveEvent &event) {}
virtual void on_focus_update(FocusUpdateEvent &event) {} virtual void on_focus_update(FocusUpdateEvent &event) {}
virtual void on_activation_update(ActivationUpdateEvent &event) {} virtual void on_activation_update(ActivationUpdateEvent &event) {}
virtual void on_paint() {} virtual void on_paint() {}
void set_did_init(bool did_init) { m_did_init = did_init; }
void repaint(); void repaint();
void reflow(); void reflow();
private: private:
@ -135,7 +128,7 @@ private:
std::vector<std::shared_ptr<Widget>> m_children; std::vector<std::shared_ptr<Widget>> m_children;
Widget *m_parent { nullptr }; Widget *m_parent { nullptr };
Window *m_window { nullptr }; Window *m_window { nullptr };
std::shared_ptr<TopLevelStyles> m_styles { nullptr }; GenericStyle *m_style { &default_widget_style };
std::shared_ptr<Layout> m_layout { nullptr }; std::shared_ptr<Layout> m_layout { nullptr };
bool m_did_init { false }; bool m_did_init { false };
bool m_is_focused { false }; bool m_is_focused { false };

View file

@ -34,8 +34,6 @@ public:
std::shared_ptr<Widget> main_widget() { return m_main_widget; } std::shared_ptr<Widget> main_widget() { return m_main_widget; }
void set_main_widget(std::shared_ptr<Widget> main_widget); void set_main_widget(std::shared_ptr<Widget> main_widget);
std::shared_ptr<TopLevelStyles> top_level_styles() { return m_top_level_styles; }
void repaint(); void repaint();
void repaint(Box geometry); void repaint(Box geometry);
void repaint(Widget *target); void repaint(Widget *target);
@ -57,7 +55,6 @@ private:
std::shared_ptr<Widget> m_main_widget { nullptr }; std::shared_ptr<Widget> m_main_widget { nullptr };
Box m_current_geometry { 0, 0, 800, 600 }; Box m_current_geometry { 0, 0, 800, 600 };
Painter m_painter {}; Painter m_painter {};
std::shared_ptr<TopLevelStyles> m_top_level_styles = std::make_shared<TopLevelStyles>(this);
Cairo::RefPtr<Cairo::XlibSurface> m_xlib_surface { nullptr }; Cairo::RefPtr<Cairo::XlibSurface> m_xlib_surface { nullptr };
bool m_is_batching { true }; bool m_is_batching { true };
bool m_did_relayout_during_batch { true }; bool m_did_relayout_during_batch { true };