gui/gui.h

211 lines
6.8 KiB
C++

#ifndef HOST_GUI_H
#define HOST_GUI_H
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
#include <memory>
#include <iostream>
#include <cstdint>
namespace gui
{
class window;
class page;
class RawPicture;
struct Widget;
using callback_void = std::function<void()>;
using callback_page = std::function<void(page&)>;
using callback_window = std::function<void(window&)>;
struct EventData {
std::string source_id;
std::string text;
float value = 0;
int index = -1;
bool flag = false;
};
class EventBus {
public:
using Handler = std::function<void(const EventData&)>;
static EventBus& get();
int on(const std::string& event, Handler h);
void off(int token);
void emit(const std::string& event, EventData data = {});
private:
int next_token = 1;
std::unordered_map<std::string, std::vector<std::pair<int, Handler>>> handlers;
};
class RawPicture {
public:
RawPicture(int w = 0, int h = 0, int bits = 32);
void create(int w, int h, int bits);
int width() const { return w; }
int height() const { return h; }
int bitness() const { return bits; }
void clear(uint32_t color = 0x000000);
void setPixel(int x, int y, uint32_t color);
uint32_t getPixel(int x, int y) const;
void drawLine(int x0, int y0, int x1, int y1, uint32_t c);
void drawRect(int x, int y, int w, int h, uint32_t c);
void fillRect(int x, int y, int w, int h, uint32_t c);
void drawCircle(int cx, int cy, int r, uint32_t c);
void fillCircle(int cx, int cy, int r, uint32_t c);
void drawTriangle(int x1,int y1,int x2,int y2,int x3,int y3,uint32_t c);
void fillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,uint32_t c);
void drawPolygon(const std::vector<std::pair<int,int>>& pts, uint32_t c);
void fillPolygon(const std::vector<std::pair<int,int>>& pts, uint32_t c);
void drawText(Display* dpy, int x, int y, const std::string& t, uint32_t c, int scale = 1);
void blit(const RawPicture& src, int dx, int dy);
bool load(const std::string& path);
void present(Display* dpy, Window win, GC gc, int dx, int dy) const;
private:
int w = 0, h = 0, bits = 32;
std::vector<uint8_t> buf;
int bpp() const { return bits / 8; }
};
// ================= Widgets =================
enum class WidgetType {
BUTTON, LABEL, INPUT, CHECKBOX, RADIO, SLIDER, PROGRESS,
LISTBOX, DROPDOWN, TREE_VIEW, TREE, SCROLL, RAW_PIC, SPRITE_BTN
};
struct WidgetData {
WidgetType type = WidgetType::BUTTON;
std::string text;
std::string id;
unsigned long bg_color = 0x1A1A1A;
unsigned long fg_color = 0xFFFFFF;
unsigned long frame_color = 0x00FF00;
int x = 0;
int y = 0;
int width = 100;
int height = 30;
bool checked = false;
float value = 0.0f;
float target_value = 0.0f;
int selected_index = -1;
int scroll = 0;
bool expanded = false;
bool indeterminate = false;
std::vector<std::string> items;
std::shared_ptr<RawPicture> picture;
callback_void on_change;
callback_page on_page_click;
callback_window on_window_click;
};
using OverlayList = std::vector<const Widget*>;
class combat
{
friend struct Widget;
protected:
std::vector<std::unique_ptr<Widget>> widgets;
Widget& add_widget(WidgetData d);
public:
virtual ~combat() = default;
Widget& Button(WidgetData a);
Widget& Label(WidgetData a);
Widget& Input(WidgetData a);
Widget& Checkbox(WidgetData a);
Widget& Radiobutton(WidgetData a);
Widget& Slider(WidgetData a);
Widget& Progressbar(WidgetData a);
Widget& Listbox(WidgetData a);
Widget& Dropdown(WidgetData a);
Widget& TreeView(WidgetData a);
Widget& TreeNode(WidgetData a);
Widget& ScrollView(WidgetData a);
Widget& RawPictureWidget(WidgetData a);
Widget& SpriteButton(WidgetData a);
void visit(const std::function<void(Widget&)>& fn);
Widget* find(const std::string& id);
Widget* find_at(int mx, int my);
void tick(float dt);
void render_all(Display*, Window, GC, int ox, int oy, const XRectangle& clip, OverlayList* ov) const;
int process_press(int mx, int my, window& w, page& p, int ox, int oy);
bool process_wheel(int delta, int mx, int my, int ox, int oy);
};
struct Widget : combat
{
WidgetData data;
float anim = 0;
bool open_target = false;
float phase = 0;
int cap_ox = 0, cap_oy = 0;
bool thumb_drag = false;
int drag_my = 0, drag_scroll = 0;
void setValue(float v) { data.target_value = v; if (data.type != WidgetType::PROGRESS) data.value = v; }
float getValue() const { return data.value; }
void tick(float dt);
void draw(Display*, Window, GC, int ox, int oy, const XRectangle& clip, OverlayList* ov) const;
void draw_popup(Display*, Window, GC, const XRectangle& clip) const;
int handle_press(int mx, int my, window& w, page& p, int ox, int oy);
void handle_drag(int mx, int my);
void handle_release();
bool handle_wheel(int delta, int mx, int my, int ox, int oy);
bool contains(int mx, int my, int ox, int oy) const;
int content_height() const;
};
class page : public combat {
public:
bool active = false;
int x = 0, y = 0;
int width = 1200, height = 600;
};
class window
{
private:
Display* X11display = nullptr;
Window X11window = 0;
GC gc = nullptr;
std::vector<WidgetData> tab_buttons;
std::unordered_map<std::string, std::unique_ptr<page>> pages;
std::string current_page_name;
Widget* captured = nullptr;
Widget* focused_input = nullptr;
void draw_tabs() const;
void draw_active_page() const;
void handle_event(const XEvent& ev);
public:
explicit window(std::string title);
~window();
window(const window&) = delete;
window& operator=(const window&) = delete;
Display* display() const { return X11display; }
void update();
void addTAB_BUTTON(WidgetData args);
void addPAGE(std::string name);
void configur_page(std::string name, callback_page fn, bool active = false);
void openPAGE(std::string name);
Widget* find(const std::string& id);
void set_focus(Widget* w) { focused_input = w; }
};
}
#endif