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