#pragma once
enum Element_Type {
Elem_None = 0,
Elem_Image,
Elem_Label,
Elem_Button,
Elem_Divider,
Elem_Data_View,
Elem_Scroll,
Elem_Edit_Box,
Elem_Drop_Down,
Elem_Checkbox,
Elem_Hex_View,
Elem_Text_Editor,
Elem_Tabs,
Elem_Number_Edit
};
struct UI_Element;
struct Scroll;
struct Box;
struct Editor {
Editor(UI_Element *elem) {
parent = elem;
}
bool multiline = true;
bool use_clipboard = true;
bool numeric_only = false;
int last_tick = 0;
int click_run = 0;
int click_window = 30;
bool selected = false;
int tab_width = 4;
UI_Element *parent = nullptr;
struct Cursor {
int cursor;
int line;
int column;
int target_column;
}
primary = {0},
secondary = {0};
std::string text;
int columns = 0;
int lines = 0;
Render_Clip clip = {0};
Font *font = nullptr;
float digit_w = 0;
float font_h = 0;
float x_offset = 0;
float y_offset = 0;
float x_scroll_delta = 0;
float y_scroll_delta = 0;
int vis_lines = 0;
int vis_columns = 0;
float cursor_width = 1;
float border = 4;
void clear();
void measure_text();
void set_text_from_buffer(std::pair<int, std::unique_ptr<u8[]>>&& buffer);
void erase(Cursor& cursor, bool is_back);
void set_cursor(Cursor& cursor, int cur);
void set_line(Cursor& cursor, int line_idx);
void set_column(Cursor& cursor, int col_idx);
int handle_input(Input& input);
void update_cursor(float x, float y, Font *font, float scale, int ticks, bool click);
void refresh(Render_Clip& clip, Font *font, float scroll_x = -1.0, float scroll_y = -1.0);
void apply_scroll(Scroll *hscroll = nullptr, Scroll *vscroll = nullptr);
void draw_selection_box(Renderer renderer, RGBA& color);
void draw_cursor(Renderer renderer, RGBA& color, Editor::Cursor& cursor, Point& pos, Render_Clip& cur_clip, float scale);
};
struct UI_Element {
bool visible = true;
bool table_vis = true;
bool active = true;
bool use_sf_cache = false;
bool use_default_cursor = false;
bool use_post_draw = false;
enum Element_Type elem_type;
enum CursorType cursor_type = CursorDefault;
Box *parent = nullptr;
Rect pos = {};
int old_width = 0;
int old_height = 0;
Rect_Int screen = {};
Texture tex_cache = nullptr;
int reify_timer = 0;
int hw_draw_timeout = 15;
bool needs_redraw = true;
int ticks = 0;
RGBA default_color = {};
RGBA inactive_color = {};
RGBA hl_color = {};
RGBA sel_color = {};
Font *font = nullptr;
void (*action)(UI_Element*, Camera&, bool) = nullptr;
void draw(Camera& view, Rect_Int const& rect, bool elem_hovered, bool box_hovered, bool focussed);
void set_active(bool state);
virtual void update(Camera& view, Rect_Int const& back) {}
virtual void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) {}
virtual void post_draw(Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) {}
// 'cursor' is relative to the upper-left corner of the box
virtual void base_action(Camera& view, Point& cursor, Input& input) {}
virtual void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) {}
virtual void key_handler(Camera& view, Input& input) {}
// 'inside' is relative to the upper-left corner of this element
virtual bool scroll_handler(Camera& view, Input& input, Point& inside) { return false; }
virtual void highlight(Camera& view, Point& inside, Input& input) {}
virtual void deselect() {}
virtual void release() {}
UI_Element() = default;
UI_Element(Element_Type t, CursorType ct = CursorDefault) : elem_type(t), cursor_type(ct) {}
virtual ~UI_Element() {
sdl_destroy_texture(&tex_cache);
}
};
struct Data_View;
struct Draw_Cell_Info {
Data_View *dv;
Camera *camera;
Renderer renderer;
Rect_Int dst;
Rect_Int src;
float y_max;
float line_off;
float sp_half;
float font_height;
int elem_pad;
};
void (*get_delete_box(void))(UI_Element*, Camera&, bool);
void (*get_maximize_box(void))(UI_Element*, Camera&, bool);
struct Scroll;
struct Button : UI_Element {
Button() : UI_Element(Elem_Button) {}
~Button() {
sdl_destroy_texture(&icon);
}
struct Theme {
RGBA back;
RGBA hover;
RGBA held;
Font *font;
};
Theme active_theme = {};
Theme inactive_theme = {};
Texture icon = nullptr;
float icon_right = false;
float width = 0;
float height = 0;
float padding = 1;
float x_offset = 0.25;
float y_offset = -0.2;
bool held = false;
std::string text;
void update_size(float scale);
int get_icon_length(float scale);
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Checkbox : UI_Element {
Checkbox() : UI_Element(Elem_Checkbox) {}
bool checked = false;
std::string text;
float border_frac = 0.2;
float text_off_x = 0.3;
float text_off_y = -0.15;
float leaning = -1.0; // -1 = left, 0 = centre, 1 = right
void toggle();
void base_action(Camera& view, Point& cursor, Input& input) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Data_View : UI_Element {
Data_View() : UI_Element(Elem_Data_View) {
use_sf_cache = true;
}
~Data_View() {
sdl_destroy_texture(&icon_plus);
sdl_destroy_texture(&icon_minus);
}
RGBA back_color = {};
Table *data = nullptr;
std::vector<Table*> *tables = nullptr;
Texture icon_plus = nullptr;
Texture icon_minus = nullptr;
Scroll *hscroll = nullptr;
Scroll *vscroll = nullptr;
UI_Element *active_elem = nullptr;
int active_row = -1;
int active_col = -1;
int hl_row = -1;
int hl_col = -1;
int sel_row = -1;
int condition_col = -1;
int x_origin = 0;
int y_origin = 0;
float header_height = 25;
float padding = 2;
float extra_line_spacing = 0;
float column_spacing = 0.2;
float scroll_total = 0.2;
float item_height = 0;
float total_spacing = 0;
bool show_column_names = false;
void (*checkbox_toggle_handler)(Data_View*, int, int) = nullptr;
Render_Clip clip = {
CLIP_LEFT | CLIP_RIGHT | CLIP_BOTTOM,
0, 0, 0, 0
};
float column_width(float total_width, float min_width, float font_height, float scale, int idx);
void draw_item_backing(Renderer renderer, RGBA& color, Rect_Int& back, float scale, int idx);
void draw_cell(Draw_Cell_Info& dci, void *cell, Column& header, Rect& r);
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
void base_action(Camera& view, Point& cursor, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
bool scroll_handler(Camera& view, Input& input, Point& inside) override;
void highlight(Camera& view, Point& inside, Input& input) override;
void deselect() override;
void release() override;
};
struct Divider : UI_Element {
Divider() : UI_Element(Elem_Divider) {}
~Divider() {
sdl_destroy_texture(&icon_default);
sdl_destroy_texture(&icon_hl);
}
bool vertical = false;
bool moveable = false;
bool held = false;
float minimum = 0;
float maximum = 0;
float position = 0;
float hold_pos = 0;
float breadth = 0;
float padding = 8;
Texture icon_default = nullptr;
Texture icon_hl = nullptr;
Texture icon = nullptr;
int icon_w = 0;
int icon_h = 0;
void make_icon(float scale);
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Drop_Down : UI_Element {
Drop_Down() : UI_Element(Elem_Drop_Down) {}
Rect menu_rect = {};
int counter = 0;
bool dropped = false;
int hl = -1;
int sel = -1;
bool keep_selected = false;
UI_Element *edit_elem = nullptr;
float item_off_x = 0.8;
float title_pad_x = 0.3;
float title_off_y = 0.03;
float leaning = 0.5;
float line_height = 1.2f;
float width = 150;
bool icon_right = true;
int icon_length = 0;
RGBA icon_color = {0};
Texture icon = nullptr;
const char *title = nullptr;
std::vector<char*> content;
std::vector<char*> *external = nullptr;
Render_Clip item_clip = {
CLIP_RIGHT,
0, 0, 0, 0
};
std::vector<char*> *get_content() {
return external ? external : &content;
}
void draw_menu(Renderer renderer, Camera& view);
void cancel();
void update(Camera& view, Rect_Int const& rect) override;
void base_action(Camera& view, Point& cursor, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void highlight(Camera& view, Point& inside, Input& input) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Edit_Box : UI_Element {
Edit_Box() : UI_Element(Elem_Edit_Box, CursorEdit), editor(this) {
editor.multiline = false;
}
~Edit_Box() {
if (manage_icon)
sdl_destroy_texture(&icon);
}
void (*key_action)(Edit_Box*, Input&) = nullptr;
bool text_changed = false;
Editor editor;
RGBA caret = {};
float text_off_x = 0.3;
float text_off_y = 0.03;
float max_width = 200;
float caret_off_y = 0.2;
float caret_width = 0.08;
bool manage_icon = true;
bool icon_right = false;
int icon_length = 0;
RGBA icon_color = {};
Texture icon = nullptr;
Drop_Down *dropdown = nullptr;
Font *ph_font = nullptr;
std::string placeholder;
Render_Clip clip = {
CLIP_LEFT | CLIP_RIGHT,
0, 0, 0, 0
};
void update_icon(IconType type, float height, float scale);
bool is_above_icon(Point& p);
void dropdown_item_selected(Input& input);
void key_handler(Camera& view, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void base_action(Camera& view, Point& cursor, Input& input) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Hex_View : UI_Element {
Hex_View() : UI_Element(Elem_Hex_View) {}
bool alive = false;
int offset = 0;
int col_offset = 0;
int sel = -1;
int vis_rows = 0;
int columns = 16;
int addr_digits = 0;
bool show_addrs = false;
bool show_hex = false;
bool show_ascii = false;
RGBA caret = {};
RGBA back_color = {};
String_Vector hex_vec;
u64 region_address = 0;
u64 region_size = 0;
Source *source = nullptr;
int span_idx = -1;
float font_height = 0;
float row_height = 0;
float x_offset = 4;
float padding = 0.25;
float cursor_width = 1.5;
Scroll *scroll = nullptr;
void set_region(u64 address, u64 size);
void set_offset(u64 offset);
void update_view(float scale);
float print_address(Renderer renderer, u64 address, float x, float y, float digit_w, Render_Clip& clip, Rect_Int& box, float padding);
float print_hex_row(Renderer renderer, Span& span, int idx, float x, float y, Rect_Int& box, float padding);
void print_ascii_row(Renderer renderer, Span& span, int idx, float x, float y, Render_Clip& clip, Rect_Int& box);
void draw_cursors(Renderer renderer, int idx, Rect_Int& back, float x_start, float pad, float scale);
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
void key_handler(Camera& view, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
bool scroll_handler(Camera& view, Input& input, Point& inside) override;
};
struct Image : UI_Element {
Image() : UI_Element(Elem_Image) {}
~Image() {
//sdl_destroy_texture(&img);
}
Texture img = nullptr;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Label : UI_Element {
Label() : UI_Element(Elem_Label) {}
std::string text;
Rect outer_box = {0};
Render_Clip clip = {
CLIP_RIGHT,
0, 0, 0, 0
};
float padding = 0.2;
void update(Camera& view, Rect_Int const& rect) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Number_Edit : UI_Element {
Number_Edit() : UI_Element(Elem_Number_Edit), editor(this) {
editor.use_clipboard = false;
editor.numeric_only = true;
}
~Number_Edit() {
sdl_destroy_texture(&icon);
}
int number = 0;
Editor editor;
void (*key_action)(Number_Edit*, Input&) = nullptr;
Render_Clip clip = {
CLIP_RIGHT,
0, 0, 0, 0
};
float text_off_x = 0.15;
float text_off_y = -0.15;
float end_width = 0.6;
float box_width = 0.6;
float box_height = 0.8;
RGBA arrow_color = {0.8, 0.87, 1.0, 0.9};
Texture icon = nullptr;
int icon_w = 0;
int icon_h = 0;
void make_icon(float scale);
void affect_number(int delta);
void key_handler(Camera& view, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void base_action(Camera& view, Point& cursor, Input& input) override;
bool scroll_handler(Camera& view, Input& input, Point& inside) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Scroll : UI_Element {
Scroll() : UI_Element(Elem_Scroll) {}
bool vertical = true;
RGBA back_color = {};
float breadth = 16;
float length = 0;
float padding = 2;
float thumb_min = 0.2;
double thumb_frac = 0;
double view_span = 0;
double position = 0;
double maximum = 0;
UI_Element *content = nullptr;
bool show_thumb = true;
bool hl = false;
bool held = false;
int hold_region = 0;
void set_maximum(double max, double span);
void engage(Point& p);
void scroll(double delta);
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
void highlight(Camera& view, Point& inside, Input& input) override;
void deselect() override;
};
struct Tabs : UI_Element {
Tabs() : UI_Element(Elem_Tabs) {}
void (*event)(Tabs *tabs) = nullptr;
struct Tab {
char *name;
float width;
};
std::vector<Tab> tabs;
int sel = 0;
int hl = -1;
float x_offset = 0.2;
float tab_border = 4;
void add(const char **names, int count);
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Text_Editor : UI_Element {
Text_Editor() : UI_Element(Elem_Text_Editor, CursorEdit), editor(this) {
editor.parent = this;
use_sf_cache = true;
use_post_draw = true;
}
bool mouse_held = false;
bool show_caret = false;
float border = 4;
int caret_on_time = 35;
int caret_off_time = 30;
RGBA caret_color = {};
Scroll *vscroll = nullptr;
Scroll *hscroll = nullptr;
Editor editor;
Render_Clip clip = {
CLIP_ALL,
0, 0, 0, 0
};
void (*key_action)(Text_Editor*, Input&) = nullptr;
void key_handler(Camera& view, Input& input) override;
void mouse_handler(Camera& view, Input& input, Point& cursor, bool hovered) override;
void base_action(Camera& view, Point& cursor, Input& input) override;
bool scroll_handler(Camera& view, Input& input, Point& inside) override;
void update(Camera& view, Rect_Int const& rect) override;
Render_Clip make_clip(Rect_Int& r, float edge);
void draw_element(Renderer renderer, Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
void post_draw(Camera& view, Rect_Int& back, bool elem_hovered, bool box_hovered, bool focussed) override;
};
struct Colors {
RGBA background;
RGBA text;
RGBA ph_text;
RGBA outline;
RGBA back;
RGBA dark;
RGBA light;
RGBA light_hl;
RGBA table_cb_back;
RGBA hl;
RGBA active;
RGBA inactive;
RGBA inactive_text;
RGBA inactive_outline;
RGBA scroll_back;
RGBA scroll;
RGBA scroll_hl;
RGBA scroll_sel;
RGBA div;
RGBA caret;
RGBA cb;
RGBA sel;
RGBA editor;
RGBA folder_dark;
RGBA folder_light;
RGBA process_back;
RGBA process_outline;
RGBA file_back;
RGBA file_fold;
RGBA file_line;
RGBA cancel;
};
enum BoxType {
BoxDefault = 0,
BoxOpening,
BoxMain,
BoxOpenSource,
BoxViewSource,
BoxStructs,
BoxViewObject,
BoxDefinitions,
BoxSearch
};
enum MenuType {
MenuDefault = 0,
MenuProcess,
MenuFile,
MenuValue,
MenuObject
};
struct Workspace;
#define FLAG_INVISIBLE 1
#define FLAG_INACTIVE 2
struct Menu_Item {
u32 flags;
char *name;
void (*action)(Workspace& ws, Box *box);
};
struct Context_Menu {
std::vector<Menu_Item> *list = nullptr;
Rect_Int rect = {0};
Font *font = nullptr;
Font *inactive_font = nullptr;
int hl = -1;
int get_item_height() {
return font ? 0.5 + font->render.text_height() * 1.2 : 0;
}
};
struct Box {
static const BoxType box_type_meta = BoxDefault;
BoxType box_type = BoxDefault;
enum MenuType menu_type = MenuDefault;
bool expungeable = false; // the expungeables
bool visible = false;
bool moving = false;
bool ui_held = false;
bool dd_menu_hovered = false;
int ticks = 0;
int refresh_every = 60;
int move_start = 5;
Workspace *parent = nullptr;
Drop_Down *current_dd = nullptr;
Editor *active_edit = nullptr;
int edge = 0;
int dd_hl_count = 0;
Rect box = {};
RGBA back_color = {};
RGBA edge_color = {};
std::vector<Menu_Item> rclick_menu_items;
std::vector<UI_Element*> ui;
float cross_size = 16;
float border = 4;
float edge_size = 2;
float min_width = 300;
float min_height = 200;
float initial_width = 450;
float initial_height = 300;
virtual void update_ui(Camera& view) {}
virtual void refresh(Point *cursor) {}
virtual void handle_zoom(Workspace& ws, float new_scale) {}
virtual void prepare_rclick_menu(Context_Menu& menu, Camera& view, Point& cursor) {}
virtual void wake_up() {}
virtual void on_close() {}
void draw(Workspace& ws, Camera& view, bool held, Point *inside, bool hovered, bool focussed);
void require_redraw();
void select_edge(Camera& view, Point& p);
void move(float dx, float dy, Camera& view, Input& input);
void set_dropdown(Drop_Down *dd);
void update_hovered(Camera& view, Input& input, Point& inside);
void update_active_elements(Camera& view, Input& input, Point& inside, Box *hover);
void update_element_actions(Camera& view, Input& input, Point& inside, Box *hover);
void update(Workspace& ws, Camera& view, Input& input, Box *hover, bool focussed);
};
struct Struct;
struct Workspace {
Colors colors;
int dpi_w = 0, dpi_h = 0;
Texture cross = nullptr;
Texture maxm = nullptr;
float cross_size = 16;
Font_Face face = nullptr;
std::vector<Font*> fonts;
Font *default_font = nullptr;
Font *inactive_font = nullptr;
std::vector<Box*> boxes;
Box *selected = nullptr;
Box *new_box = nullptr;
bool box_moving = false;
bool box_expunged = false;
bool cursor_set = false;
bool show_rclick_menu = false;
std::vector<Menu_Item> default_rclick_menu;
Box *rclick_box = nullptr;
Context_Menu rclick_menu;
std::vector<Source*> sources;
Map definitions;
Arena object_arena;
String_Vector tokens;
String_Vector name_vector;
std::vector<Struct*> structs;
std::vector<char*> struct_names;
bool first_struct_run = true;
void init(Font_Face face);
void close();
Box *box_under_cursor(Camera& view, Point& cur, Point& inside);
void add_box(Box *b);
void delete_box(int idx);
void delete_box(Box *b);
void bring_to_front(Box *b);
Font *make_font(float size, RGBA color, float scale);
void refresh_sources();
void view_source_at(Source *source, u64 address = -1);
void adjust_scale(float old_scale, float new_scale);
void prepare_rclick_menu(Camera& view, Point& cursor);
void update(Camera& view, Input& input, Point& cursor);
void update_structs(std::string& text);
template<typename B>
B *first_box_of_type(MenuType mtype = MenuDefault) {
static_assert(std::is_base_of_v<Box, B>);
if (mtype != MenuDefault) {
for (auto& b : boxes) {
if (b->box_type == B::box_type_meta && b->menu_type == mtype)
return dynamic_cast<B*>(b);
}
}
else {
for (auto& b : boxes) {
if (b->box_type == B::box_type_meta)
return dynamic_cast<B*>(b);
}
}
return nullptr;
}
template<typename B>
B *make_box(MenuType mtype = MenuDefault) {
static_assert(std::is_base_of_v<Box, B>);
B *box = first_box_of_type<B>(mtype);
if (box && !box->expungeable) {
box->visible = true;
bring_to_front(box);
}
else {
B *b = new B(*this, mtype);
b->box_type = B::box_type_meta;
b->menu_type = mtype;
b->parent = this;
b->visible = true;
for (auto& elem : b->ui)
elem->parent = b;
Camera& view = get_default_camera();
float x = view.center_x - b->initial_width * view.scale / 2;
float y = view.center_y - b->initial_height * view.scale / 2;
Point p = view.to_world(x, y);
b->box = { p.x, p.y, b->initial_width, b->initial_height };
b->edge_color = colors.outline;
b->back_color = colors.back;
add_box(b);
box = b;
}
if (box)
box->wake_up();
return box;
}
};
Rect make_ui_box(Rect_Int const& box, Rect const& elem, float scale);
Rect_Int make_int_ui_box(Rect_Int const& box, Rect const& elem, float scale);
void reposition_box_buttons(Image& cross, Image& maxm, float box_w, float size);
void load_config(Workspace& ws, std::string& path);