#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);