Initial commit.
This commit adds some basic components. Currently, there is an issue with the drawing order of the text.
This commit is contained in:
commit
1b53572480
2 changed files with 350 additions and 0 deletions
8
meson.build
Normal file
8
meson.build
Normal file
|
@ -0,0 +1,8 @@
|
|||
project('blit', 'cpp')
|
||||
|
||||
|
||||
cairomm_dep = dependency('cairomm-1.0')
|
||||
pangocairo_dep = dependency('pangocairo')
|
||||
xlib_dep = dependency('x11')
|
||||
|
||||
executable('blit_app', './src/main.cpp', dependencies : [cairomm_dep, xlib_dep, pangocairo_dep])
|
342
src/main.cpp
Normal file
342
src/main.cpp
Normal file
|
@ -0,0 +1,342 @@
|
|||
#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:
|
||||
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() {
|
||||
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;
|
||||
|
||||
window.attach_element(&button);
|
||||
window.attach_element(&button_text);
|
||||
|
||||
for (;;) {
|
||||
XNextEvent(window.dsp, &e);
|
||||
window.handle_x_event(&e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue