basic structure
This commit is contained in:
parent
f3ab6c72a1
commit
2cc5e1c1e0
15 changed files with 585 additions and 337 deletions
11
meson.build
11
meson.build
|
@ -5,4 +5,13 @@ cairomm_dep = dependency('cairomm-1.0')
|
||||||
pangocairo_dep = dependency('pangocairo')
|
pangocairo_dep = dependency('pangocairo')
|
||||||
xlib_dep = dependency('x11')
|
xlib_dep = dependency('x11')
|
||||||
|
|
||||||
executable('blit_app', './src/main.cpp', dependencies : [cairomm_dep, xlib_dep, pangocairo_dep])
|
executable(
|
||||||
|
'blit_app',
|
||||||
|
'./src/Box.cpp',
|
||||||
|
'./src/Point.cpp',
|
||||||
|
'./src/Events.cpp',
|
||||||
|
'./src/Widget.cpp',
|
||||||
|
'./src/Window.cpp',
|
||||||
|
'./src/main.cpp',
|
||||||
|
dependencies : [cairomm_dep, xlib_dep, pangocairo_dep]
|
||||||
|
)
|
||||||
|
|
14
src/Box.cpp
Normal file
14
src/Box.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "Box.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
src/Box.hpp
Normal file
36
src/Box.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class Box {
|
||||||
|
private:
|
||||||
|
double m_x {0};
|
||||||
|
double m_y {0};
|
||||||
|
double m_width {0};
|
||||||
|
double m_height {0};
|
||||||
|
public:
|
||||||
|
Box() {}
|
||||||
|
Box(double x, double y, double width, double height)
|
||||||
|
: m_x(x)
|
||||||
|
, m_y(y)
|
||||||
|
, m_width(width)
|
||||||
|
, m_height(height)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_x() { return m_x; }
|
||||||
|
double get_y() { return m_y; }
|
||||||
|
double get_width() { return m_width; }
|
||||||
|
double get_height() { return m_height; }
|
||||||
|
|
||||||
|
void set_x(double x) { m_x = x; }
|
||||||
|
void set_y(double y) { m_y = y; }
|
||||||
|
void set_width(double width) { m_width = width; }
|
||||||
|
void set_height(double height) { m_height = height; }
|
||||||
|
|
||||||
|
bool contains_point(double x, double y);
|
||||||
|
bool contains_box(const Box &other);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
0
src/Events.cpp
Normal file
0
src/Events.cpp
Normal file
54
src/Events.hpp
Normal file
54
src/Events.hpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "Point.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
enum class EventType {
|
||||||
|
None,
|
||||||
|
|
||||||
|
MouseButton,
|
||||||
|
MouseMove
|
||||||
|
};
|
||||||
|
|
||||||
|
class Event {
|
||||||
|
private:
|
||||||
|
bool m_accepted { false };
|
||||||
|
public:
|
||||||
|
Event() {}
|
||||||
|
|
||||||
|
void accept() { m_accepted = true; }
|
||||||
|
bool is_accepted() { return m_accepted; }
|
||||||
|
virtual EventType get_event_type() { return EventType::None; }
|
||||||
|
virtual const char *get_event_name() { return "None"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class MouseButtonEvent : public Event {
|
||||||
|
private:
|
||||||
|
bool m_was_left_button_pressed;
|
||||||
|
bool m_was_right_button_pressed;
|
||||||
|
public:
|
||||||
|
MouseButtonEvent(bool was_left_button_pressed, bool was_right_button_pressed)
|
||||||
|
: m_was_left_button_pressed(was_left_button_pressed)
|
||||||
|
, m_was_right_button_pressed(was_right_button_pressed) {}
|
||||||
|
|
||||||
|
EventType get_event_type() { return EventType::MouseButton; }
|
||||||
|
const char *get_event_name() { return "MouseButton"; }
|
||||||
|
|
||||||
|
bool get_was_left_button_pressed() { return m_was_left_button_pressed; }
|
||||||
|
bool get_was_right_button_pressed() { return m_was_right_button_pressed; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class MouseMoveEvent : public Event {
|
||||||
|
private:
|
||||||
|
Point m_point;
|
||||||
|
public:
|
||||||
|
MouseMoveEvent(Point point)
|
||||||
|
: m_point(point) {}
|
||||||
|
|
||||||
|
EventType get_event_type() { return EventType::MouseMove; }
|
||||||
|
const char *get_event_name() { return "MouseMove"; }
|
||||||
|
|
||||||
|
Point &get_point() { return m_point; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
0
src/Painter.cpp
Normal file
0
src/Painter.cpp
Normal file
9
src/Painter.hpp
Normal file
9
src/Painter.hpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class Painter {
|
||||||
|
Painter() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
0
src/Point.cpp
Normal file
0
src/Point.cpp
Normal file
23
src/Point.hpp
Normal file
23
src/Point.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
private:
|
||||||
|
int m_x;
|
||||||
|
int m_y;
|
||||||
|
public:
|
||||||
|
Point(int x, int y)
|
||||||
|
: m_x(x)
|
||||||
|
, m_y(y) {}
|
||||||
|
|
||||||
|
int get_x() { return m_x; }
|
||||||
|
const int get_x() const { return m_x; }
|
||||||
|
int get_y() { return m_y; }
|
||||||
|
const int get_y() const { return m_y; }
|
||||||
|
|
||||||
|
void set_x(int x) { m_x = x; }
|
||||||
|
void set_y(int y) { m_y = y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
0
src/Widget.cpp
Normal file
0
src/Widget.cpp
Normal file
18
src/Widget.hpp
Normal file
18
src/Widget.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Box.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class Widget {
|
||||||
|
private:
|
||||||
|
Box m_current_geometry {};
|
||||||
|
public:
|
||||||
|
Widget() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const Box &get_current_geometry() const { return m_current_geometry; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
37
src/Window.cpp
Normal file
37
src/Window.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "Window.hpp"
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <cairomm-1.0/cairomm/cairomm.h>
|
||||||
|
#include <cairomm-1.0/cairomm/context.h>
|
||||||
|
#include <cairomm-1.0/cairomm/surface.h>
|
||||||
|
#include <cairomm-1.0/cairomm/xlib_surface.h>
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
bool Window::spawn_window() {
|
||||||
|
Display *dsp;
|
||||||
|
Drawable da;
|
||||||
|
int screen;
|
||||||
|
|
||||||
|
if ((dsp = XOpenDisplay(NULL)) == NULL) {
|
||||||
|
std::cout << "error: XOpenDisplay(NULL)" << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
screen = DefaultScreen(dsp);
|
||||||
|
da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), 0, 0, get_current_geometry().get_width(), get_current_geometry().get_height(), 0, 0, 0);
|
||||||
|
XSelectInput(dsp, da, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask);
|
||||||
|
XMapWindow(dsp, da);
|
||||||
|
auto x_surface = Cairo::XlibSurface::create(
|
||||||
|
dsp,
|
||||||
|
da,
|
||||||
|
DefaultVisual(dsp, screen),
|
||||||
|
get_current_geometry().get_width(),
|
||||||
|
get_current_geometry().get_height()
|
||||||
|
);
|
||||||
|
Cairo::Context::create(x_surface);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
src/Window.hpp
Normal file
22
src/Window.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include "Widget.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class Window {
|
||||||
|
private:
|
||||||
|
Widget *m_active_widget { nullptr };
|
||||||
|
Box m_current_geometry { 0, 0, 800, 600 };
|
||||||
|
public:
|
||||||
|
Window() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget *get_active_widget() { return m_active_widget; }
|
||||||
|
void set_active_widget(Widget *active_widget) { m_active_widget = active_widget; }
|
||||||
|
Box &get_current_geometry() { return m_current_geometry; }
|
||||||
|
|
||||||
|
bool spawn_window();
|
||||||
|
void window_event_loop();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
357
src/_main_old.cpp
Normal file
357
src/_main_old.cpp
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cairomm-1.0/cairomm/xlib_surface.h>
|
||||||
|
#include <cairomm-1.0/cairomm/context.h>
|
||||||
|
#include <cairomm-1.0/cairomm/surface.h>
|
||||||
|
#include <pango/pangocairo.h>
|
||||||
|
|
||||||
|
|
||||||
|
void panic(int exit_code, const char *message) {
|
||||||
|
fputs(message, stderr);
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Blit {
|
||||||
|
|
||||||
|
bool collide_point_rect(double x, double y, double w, double h, double x1, double y1) {
|
||||||
|
return x1 >= x && x + w >= x1 &&
|
||||||
|
y1 >= y && y + h >= y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum TextAlign {
|
||||||
|
TEXT_ALIGN_NONE,
|
||||||
|
TEXT_ALIGN_CENTER_LEFT,
|
||||||
|
TEXT_ALIGN_CENTER
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RGB {
|
||||||
|
public:
|
||||||
|
double r, g, b;
|
||||||
|
public:
|
||||||
|
RGB(double r, double g, double b) : r(r), g(g), b(b) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BaseElement {
|
||||||
|
public:
|
||||||
|
std::vector<std::reference_wrapper<BaseElement>> bound_elements;
|
||||||
|
|
||||||
|
BaseElement() {}
|
||||||
|
virtual ~BaseElement() {}
|
||||||
|
|
||||||
|
bool should_get_x_events = true;
|
||||||
|
virtual void on_draw() = 0;
|
||||||
|
virtual void handle_event(XEvent *e) = 0;
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
on_draw();
|
||||||
|
for (auto i : bound_elements) {
|
||||||
|
i.get().draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind_element(BaseElement &other) {
|
||||||
|
bound_elements.push_back(other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Window {
|
||||||
|
public:
|
||||||
|
Cairo::RefPtr<Cairo::Context> cr;
|
||||||
|
Display *dsp;
|
||||||
|
private:
|
||||||
|
std::vector<Blit::BaseElement *> m_attached_elements;
|
||||||
|
public:
|
||||||
|
Window(int x, int y)
|
||||||
|
{
|
||||||
|
Drawable da;
|
||||||
|
int screen;
|
||||||
|
|
||||||
|
if ((dsp = XOpenDisplay(NULL)) == NULL)
|
||||||
|
panic(1, "Failed to open display");
|
||||||
|
|
||||||
|
screen = DefaultScreen(dsp);
|
||||||
|
da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), 0, 0, x, y, 0, 0, 0);
|
||||||
|
XSelectInput(dsp, da, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask);
|
||||||
|
XMapWindow(dsp, da);
|
||||||
|
|
||||||
|
auto x_surface = Cairo::XlibSurface::create(
|
||||||
|
dsp,
|
||||||
|
da,
|
||||||
|
DefaultVisual(dsp, screen),
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
);
|
||||||
|
cr = Cairo::Context::create(x_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach_element_init(Blit::BaseElement *element) {
|
||||||
|
m_attached_elements.push_back(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach_element(Blit::BaseElement *element) {
|
||||||
|
m_attached_elements.push_back(element);
|
||||||
|
element->draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void redraw_all() {
|
||||||
|
for (Blit::BaseElement * element : m_attached_elements) {
|
||||||
|
element->draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_x_event(XEvent *e) {
|
||||||
|
for (Blit::BaseElement * element : m_attached_elements) {
|
||||||
|
if (element->should_get_x_events)
|
||||||
|
element->handle_event(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Text : public BaseElement {
|
||||||
|
public:
|
||||||
|
PangoFontDescription *font_description;
|
||||||
|
bool should_get_x_events = false;
|
||||||
|
Blit::TextAlign text_align = Blit::TextAlign::TEXT_ALIGN_NONE;
|
||||||
|
double x, y, box_x, box_y, box_w, box_h = 0;
|
||||||
|
std::string text;
|
||||||
|
Blit::RGB fg;
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::Context> cr;
|
||||||
|
public:
|
||||||
|
Text(Cairo::RefPtr<Cairo::Context> cr, std::string& text, PangoFontDescription *font_description) :
|
||||||
|
cr(cr),
|
||||||
|
text(text),
|
||||||
|
font_description(font_description),
|
||||||
|
fg(0.0, 0.0, 0.0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~Text() {}
|
||||||
|
|
||||||
|
void on_draw() {
|
||||||
|
cr->save();
|
||||||
|
cr->set_source_rgb(fg.r, fg.g, fg.b);
|
||||||
|
|
||||||
|
if (text_align == Blit::TextAlign::TEXT_ALIGN_NONE) {
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
||||||
|
pango_layout_set_font_description(layout, font_description);
|
||||||
|
pango_layout_set_text(layout, text.c_str(), -1);
|
||||||
|
cr->move_to(x, y);
|
||||||
|
pango_cairo_show_layout(cr->cobj(), layout);
|
||||||
|
g_object_unref(layout);
|
||||||
|
} else if (text_align == Blit::TextAlign::TEXT_ALIGN_CENTER_LEFT) {
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
||||||
|
int font_width;
|
||||||
|
int font_height;
|
||||||
|
|
||||||
|
pango_layout_set_font_description(layout, font_description);
|
||||||
|
pango_layout_set_text(layout, text.c_str(), -1);
|
||||||
|
pango_layout_get_pixel_size(layout, &font_width, &font_height);
|
||||||
|
|
||||||
|
double x = box_x + ((box_w - font_width) / 2);
|
||||||
|
double y = box_y + ((box_h - font_height) / 2);
|
||||||
|
|
||||||
|
// TODO: unhardcode the padding
|
||||||
|
x = box_x + 4;
|
||||||
|
|
||||||
|
cr->move_to(x, y);
|
||||||
|
pango_cairo_show_layout(cr->cobj(), layout);
|
||||||
|
g_object_unref(layout);
|
||||||
|
} else if (text_align == Blit::TextAlign::TEXT_ALIGN_CENTER) {
|
||||||
|
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
||||||
|
int font_width;
|
||||||
|
int font_height;
|
||||||
|
|
||||||
|
pango_layout_set_font_description(layout, font_description);
|
||||||
|
pango_layout_set_text(layout, text.c_str(), -1);
|
||||||
|
pango_layout_get_pixel_size(layout, &font_width, &font_height);
|
||||||
|
|
||||||
|
double x = box_x + ((box_w - font_width) / 2);
|
||||||
|
double y = box_y + ((box_h - font_height) / 2);
|
||||||
|
|
||||||
|
cr->move_to(x, y);
|
||||||
|
pango_cairo_show_layout(cr->cobj(), layout);
|
||||||
|
g_object_unref(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
cr->fill();
|
||||||
|
cr->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_event(XEvent *e) {}
|
||||||
|
|
||||||
|
void set_x(double val) {
|
||||||
|
x = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_y(double val) {
|
||||||
|
y = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_box_x(double val) {
|
||||||
|
box_x = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_box_y(double val) {
|
||||||
|
box_y = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_box_w(double val) {
|
||||||
|
box_w = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_box_h(double val) {
|
||||||
|
box_h = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_text(std::string& text) {
|
||||||
|
text = text;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_text_align(Blit::TextAlign val) {
|
||||||
|
text_align = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_current_fg(Blit::RGB val) {
|
||||||
|
fg = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Button : public BaseElement {
|
||||||
|
public:
|
||||||
|
bool should_get_x_events = true;
|
||||||
|
double x, y, w, h, border_radius = 0;
|
||||||
|
Blit::RGB current_bg, bg, sel_bg;
|
||||||
|
|
||||||
|
bool is_hot, is_active = false;
|
||||||
|
|
||||||
|
Cairo::RefPtr<Cairo::Context> cr;
|
||||||
|
public:
|
||||||
|
Button(Cairo::RefPtr<Cairo::Context> cr) :
|
||||||
|
should_get_x_events(true),
|
||||||
|
current_bg(0.0, 0.0, 0.0),
|
||||||
|
sel_bg(0.0, 0.0, 0.0),
|
||||||
|
bg(0.0, 0.0, 0.0),
|
||||||
|
cr(cr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~Button() {}
|
||||||
|
|
||||||
|
void on_draw() {
|
||||||
|
cr->save();
|
||||||
|
cr->set_source_rgb(current_bg.r, current_bg.g, current_bg.b);
|
||||||
|
if (border_radius > 0) {
|
||||||
|
double aspect = 1.0;
|
||||||
|
double radius = border_radius / aspect;
|
||||||
|
double degrees = M_PI / 180.0;
|
||||||
|
|
||||||
|
cr->begin_new_sub_path();
|
||||||
|
cr->arc(x + w - radius, y + radius, radius, -90 * degrees, 0 * degrees);
|
||||||
|
cr->arc(x + w - radius, y + h - radius, radius, 0 * degrees, 90 * degrees);
|
||||||
|
cr->arc(x + radius, y + h - radius, radius, 90 * degrees, 180 * degrees);
|
||||||
|
cr->arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
|
||||||
|
cr->close_path();
|
||||||
|
} else {
|
||||||
|
cr->rectangle(x, y, w, h);
|
||||||
|
}
|
||||||
|
cr->fill();
|
||||||
|
cr->restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_event(XEvent *e) {
|
||||||
|
char keybuf[8];
|
||||||
|
KeySym key;
|
||||||
|
|
||||||
|
switch (e->type) {
|
||||||
|
case MotionNotify:
|
||||||
|
bool is_hot_now = Blit::collide_point_rect(x, y, w, h, e->xmotion.x, e->xmotion.y);
|
||||||
|
if (!is_hot && is_hot_now) { // just became hot
|
||||||
|
set_current_bg(sel_bg);
|
||||||
|
is_hot = true;
|
||||||
|
} else if (is_hot && !is_hot_now) { // just became not hot
|
||||||
|
set_current_bg(bg);
|
||||||
|
is_hot = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_x(double val) {
|
||||||
|
x = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_y(double val) {
|
||||||
|
y = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_w(double val) {
|
||||||
|
w = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_h(double val) {
|
||||||
|
h = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_border_radius(double val) {
|
||||||
|
border_radius = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
void set_current_bg(Blit::RGB val) {
|
||||||
|
current_bg = val;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string text = "click me now";
|
||||||
|
XEvent e;
|
||||||
|
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);
|
||||||
|
|
||||||
|
Blit::Window window(800, 600);
|
||||||
|
Blit::Button button(window.cr);
|
||||||
|
Blit::Text button_text(window.cr, text, font_description);
|
||||||
|
|
||||||
|
button.x = 20.0;
|
||||||
|
button.y = 20.0;
|
||||||
|
button.w = 100.0;
|
||||||
|
button.h = 30.0;
|
||||||
|
button.border_radius = 0.0;
|
||||||
|
button.bg = Blit::RGB(1.0, 0.0, 0.0);
|
||||||
|
button.sel_bg = Blit::RGB(0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
button_text.x = button.x;
|
||||||
|
button_text.y = button.y;
|
||||||
|
button_text.box_x = button.x;
|
||||||
|
button_text.box_y = button.y;
|
||||||
|
button_text.box_w = button.w;
|
||||||
|
button_text.box_h = button.h;
|
||||||
|
button_text.fg = Blit::RGB(1.0, 1.0, 1.0);
|
||||||
|
button_text.text_align = Blit::TextAlign::TEXT_ALIGN_CENTER;
|
||||||
|
|
||||||
|
button.bind_element(button_text);
|
||||||
|
|
||||||
|
window.attach_element(&button);
|
||||||
|
window.attach_element(&button_text);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
XNextEvent(window.dsp, &e);
|
||||||
|
window.handle_x_event(&e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
339
src/main.cpp
339
src/main.cpp
|
@ -1,342 +1,11 @@
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cairomm-1.0/cairomm/xlib_surface.h>
|
#include "Window.hpp"
|
||||||
#include <cairomm-1.0/cairomm/context.h>
|
|
||||||
#include <cairomm-1.0/cairomm/surface.h>
|
|
||||||
#include <pango/pangocairo.h>
|
|
||||||
|
|
||||||
|
|
||||||
void panic(int exit_code, const char *message) {
|
|
||||||
fputs(message, stderr);
|
|
||||||
exit(exit_code);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Blit {
|
|
||||||
|
|
||||||
bool collide_point_rect(double x, double y, double w, double h, double x1, double y1) {
|
|
||||||
return x1 >= x && x + w >= x1 &&
|
|
||||||
y1 >= y && y + h >= y1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum TextAlign {
|
|
||||||
TEXT_ALIGN_NONE,
|
|
||||||
TEXT_ALIGN_CENTER_LEFT,
|
|
||||||
TEXT_ALIGN_CENTER
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class RGB {
|
|
||||||
public:
|
|
||||||
double r, g, b;
|
|
||||||
public:
|
|
||||||
RGB(double r, double g, double b) : r(r), g(g), b(b) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class BaseElement {
|
|
||||||
public:
|
|
||||||
BaseElement() {}
|
|
||||||
virtual ~BaseElement() {}
|
|
||||||
|
|
||||||
bool should_get_x_events = true;
|
|
||||||
virtual void draw() = 0;
|
|
||||||
virtual void handle_event(XEvent *e) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Window {
|
|
||||||
public:
|
|
||||||
Cairo::RefPtr<Cairo::Context> cr;
|
|
||||||
Display *dsp;
|
|
||||||
private:
|
|
||||||
std::vector<Blit::BaseElement *> m_attached_elements;
|
|
||||||
public:
|
|
||||||
Window(int x, int y)
|
|
||||||
{
|
|
||||||
Drawable da;
|
|
||||||
int screen;
|
|
||||||
|
|
||||||
if ((dsp = XOpenDisplay(NULL)) == NULL)
|
|
||||||
panic(1, "Failed to open display");
|
|
||||||
|
|
||||||
screen = DefaultScreen(dsp);
|
|
||||||
da = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), 0, 0, x, y, 0, 0, 0);
|
|
||||||
XSelectInput(dsp, da, ButtonPressMask | ButtonReleaseMask | KeyPressMask | PointerMotionMask);
|
|
||||||
XMapWindow(dsp, da);
|
|
||||||
|
|
||||||
auto x_surface = Cairo::XlibSurface::create(
|
|
||||||
dsp,
|
|
||||||
da,
|
|
||||||
DefaultVisual(dsp, screen),
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
);
|
|
||||||
cr = Cairo::Context::create(x_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
void attach_element_init(Blit::BaseElement *element) {
|
|
||||||
m_attached_elements.push_back(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
void attach_element(Blit::BaseElement *element) {
|
|
||||||
m_attached_elements.push_back(element);
|
|
||||||
element->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
void redraw_all() {
|
|
||||||
for (Blit::BaseElement * element : m_attached_elements) {
|
|
||||||
element->draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_x_event(XEvent *e) {
|
|
||||||
for (Blit::BaseElement * element : m_attached_elements) {
|
|
||||||
if (element->should_get_x_events)
|
|
||||||
element->handle_event(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Text : public BaseElement {
|
|
||||||
public:
|
|
||||||
PangoFontDescription *font_description;
|
|
||||||
bool should_get_x_events = false;
|
|
||||||
Blit::TextAlign text_align = Blit::TextAlign::TEXT_ALIGN_NONE;
|
|
||||||
double x, y, box_x, box_y, box_w, box_h = 0;
|
|
||||||
std::string text;
|
|
||||||
Blit::RGB fg;
|
|
||||||
|
|
||||||
Cairo::RefPtr<Cairo::Context> cr;
|
|
||||||
public:
|
|
||||||
Text(Cairo::RefPtr<Cairo::Context> cr, std::string& text, PangoFontDescription *font_description) :
|
|
||||||
cr(cr),
|
|
||||||
text(text),
|
|
||||||
font_description(font_description),
|
|
||||||
fg(0.0, 0.0, 0.0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual ~Text() {}
|
|
||||||
|
|
||||||
void draw() {
|
|
||||||
cr->save();
|
|
||||||
cr->set_source_rgb(fg.r, fg.g, fg.b);
|
|
||||||
|
|
||||||
if (text_align == Blit::TextAlign::TEXT_ALIGN_NONE) {
|
|
||||||
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
|
||||||
pango_layout_set_font_description(layout, font_description);
|
|
||||||
pango_layout_set_text(layout, text.c_str(), -1);
|
|
||||||
cr->move_to(x, y);
|
|
||||||
pango_cairo_show_layout(cr->cobj(), layout);
|
|
||||||
g_object_unref(layout);
|
|
||||||
} else if (text_align == Blit::TextAlign::TEXT_ALIGN_CENTER_LEFT) {
|
|
||||||
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
|
||||||
int font_width;
|
|
||||||
int font_height;
|
|
||||||
|
|
||||||
pango_layout_set_font_description(layout, font_description);
|
|
||||||
pango_layout_set_text(layout, text.c_str(), -1);
|
|
||||||
pango_layout_get_pixel_size(layout, &font_width, &font_height);
|
|
||||||
|
|
||||||
double x = box_x + ((box_w - font_width) / 2);
|
|
||||||
double y = box_y + ((box_h - font_height) / 2);
|
|
||||||
|
|
||||||
// TODO: unhardcode the padding
|
|
||||||
x = box_x + 4;
|
|
||||||
|
|
||||||
cr->move_to(x, y);
|
|
||||||
pango_cairo_show_layout(cr->cobj(), layout);
|
|
||||||
g_object_unref(layout);
|
|
||||||
} else if (text_align == Blit::TextAlign::TEXT_ALIGN_CENTER) {
|
|
||||||
PangoLayout *layout = pango_cairo_create_layout(cr->cobj());
|
|
||||||
int font_width;
|
|
||||||
int font_height;
|
|
||||||
|
|
||||||
pango_layout_set_font_description(layout, font_description);
|
|
||||||
pango_layout_set_text(layout, text.c_str(), -1);
|
|
||||||
pango_layout_get_pixel_size(layout, &font_width, &font_height);
|
|
||||||
|
|
||||||
double x = box_x + ((box_w - font_width) / 2);
|
|
||||||
double y = box_y + ((box_h - font_height) / 2);
|
|
||||||
|
|
||||||
cr->move_to(x, y);
|
|
||||||
pango_cairo_show_layout(cr->cobj(), layout);
|
|
||||||
g_object_unref(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
cr->fill();
|
|
||||||
cr->restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_event(XEvent *e) {}
|
|
||||||
|
|
||||||
void set_x(double val) {
|
|
||||||
x = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_y(double val) {
|
|
||||||
y = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_box_x(double val) {
|
|
||||||
box_x = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_box_y(double val) {
|
|
||||||
box_y = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_box_w(double val) {
|
|
||||||
box_w = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_box_h(double val) {
|
|
||||||
box_h = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_text(std::string& text) {
|
|
||||||
text = text;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_text_align(Blit::TextAlign val) {
|
|
||||||
text_align = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_current_fg(Blit::RGB val) {
|
|
||||||
fg = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Button : public BaseElement {
|
|
||||||
public:
|
|
||||||
bool should_get_x_events = true;
|
|
||||||
double x, y, w, h, border_radius = 0;
|
|
||||||
Blit::RGB current_bg, bg, sel_bg;
|
|
||||||
|
|
||||||
bool is_hot, is_active = false;
|
|
||||||
|
|
||||||
Cairo::RefPtr<Cairo::Context> cr;
|
|
||||||
public:
|
|
||||||
Button(Cairo::RefPtr<Cairo::Context> cr) :
|
|
||||||
should_get_x_events(true),
|
|
||||||
current_bg(0.0, 0.0, 0.0),
|
|
||||||
sel_bg(0.0, 0.0, 0.0),
|
|
||||||
bg(0.0, 0.0, 0.0),
|
|
||||||
cr(cr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual ~Button() {}
|
|
||||||
|
|
||||||
void draw() {
|
|
||||||
cr->save();
|
|
||||||
cr->set_source_rgb(current_bg.r, current_bg.g, current_bg.b);
|
|
||||||
if (border_radius > 0) {
|
|
||||||
double aspect = 1.0;
|
|
||||||
double radius = border_radius / aspect;
|
|
||||||
double degrees = M_PI / 180.0;
|
|
||||||
|
|
||||||
cr->begin_new_sub_path();
|
|
||||||
cr->arc(x + w - radius, y + radius, radius, -90 * degrees, 0 * degrees);
|
|
||||||
cr->arc(x + w - radius, y + h - radius, radius, 0 * degrees, 90 * degrees);
|
|
||||||
cr->arc(x + radius, y + h - radius, radius, 90 * degrees, 180 * degrees);
|
|
||||||
cr->arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
|
|
||||||
cr->close_path();
|
|
||||||
} else {
|
|
||||||
cr->rectangle(x, y, w, h);
|
|
||||||
}
|
|
||||||
cr->fill();
|
|
||||||
cr->restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_event(XEvent *e) {
|
|
||||||
char keybuf[8];
|
|
||||||
KeySym key;
|
|
||||||
|
|
||||||
switch (e->type) {
|
|
||||||
case MotionNotify:
|
|
||||||
bool is_hot_now = Blit::collide_point_rect(x, y, w, h, e->xmotion.x, e->xmotion.y);
|
|
||||||
if (!is_hot && is_hot_now) { // just became hot
|
|
||||||
set_current_bg(sel_bg);
|
|
||||||
is_hot = true;
|
|
||||||
} else if (is_hot && !is_hot_now) { // just became not hot
|
|
||||||
set_current_bg(bg);
|
|
||||||
is_hot = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_x(double val) {
|
|
||||||
x = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_y(double val) {
|
|
||||||
y = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_w(double val) {
|
|
||||||
w = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_h(double val) {
|
|
||||||
h = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_border_radius(double val) {
|
|
||||||
border_radius = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
void set_current_bg(Blit::RGB val) {
|
|
||||||
current_bg = val;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::string text = "click me now";
|
Raven::Window window{};
|
||||||
XEvent e;
|
|
||||||
PangoFontDescription *font_description = pango_font_description_new();
|
|
||||||
|
|
||||||
pango_font_description_set_family(font_description, "sans-serif");
|
window.spawn_window();
|
||||||
pango_font_description_set_weight(font_description, PANGO_WEIGHT_NORMAL);
|
for(;;){}
|
||||||
pango_font_description_set_absolute_size(font_description, 16 * PANGO_SCALE);
|
|
||||||
|
|
||||||
Blit::Window window(800, 600);
|
|
||||||
Blit::Button button(window.cr);
|
|
||||||
Blit::Text button_text(window.cr, text, font_description);
|
|
||||||
|
|
||||||
button.x = 20.0;
|
|
||||||
button.y = 20.0;
|
|
||||||
button.w = 100.0;
|
|
||||||
button.h = 30.0;
|
|
||||||
button.border_radius = 0.0;
|
|
||||||
button.bg = Blit::RGB(1.0, 0.0, 0.0);
|
|
||||||
button.sel_bg = Blit::RGB(0.0, 1.0, 0.0);
|
|
||||||
|
|
||||||
button_text.x = button.x;
|
|
||||||
button_text.y = button.y;
|
|
||||||
button_text.box_x = button.x;
|
|
||||||
button_text.box_y = button.y;
|
|
||||||
button_text.box_w = button.w;
|
|
||||||
button_text.box_h = button.h;
|
|
||||||
button_text.fg = Blit::RGB(1.0, 1.0, 1.0);
|
|
||||||
button_text.text_align = Blit::TextAlign::TEXT_ALIGN_CENTER;
|
|
||||||
|
|
||||||
window.attach_element(&button);
|
|
||||||
window.attach_element(&button_text);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
XNextEvent(window.dsp, &e);
|
|
||||||
window.handle_x_event(&e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
Loading…
Reference in a new issue