orca/src/ui/ui.h

592 lines
15 KiB
C
Raw Normal View History

/************************************************************/ /**
*
* @file: ui.h
* @author: Martin Fouilleul
* @date: 08/08/2022
* @revision:
*
2023-08-19 12:49:23 +00:00
*****************************************************************/
#ifndef __UI_H_
#define __UI_H_
#include "graphics/graphics.h"
#include "input_state.h"
#include "util/lists.h"
#include "util/typedefs.h"
#ifdef __cplusplus
extern "C" {
2023-08-19 12:49:23 +00:00
#endif
typedef struct oc_ui_key
{
u64 hash;
} oc_ui_key;
2023-08-19 12:49:23 +00:00
typedef enum
{
OC_UI_AXIS_X,
OC_UI_AXIS_Y,
OC_UI_AXIS_COUNT
} oc_ui_axis;
2023-08-19 12:49:23 +00:00
typedef enum
{
OC_UI_ALIGN_START,
OC_UI_ALIGN_END,
OC_UI_ALIGN_CENTER,
} oc_ui_align;
2023-08-19 12:49:23 +00:00
typedef union oc_ui_layout_align
{
struct
2023-08-19 12:49:23 +00:00
{
oc_ui_align x;
oc_ui_align y;
};
2023-08-19 12:49:23 +00:00
oc_ui_align c[OC_UI_AXIS_COUNT];
} oc_ui_layout_align;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_layout
{
oc_ui_axis axis;
f32 spacing;
2023-08-19 12:49:23 +00:00
union
2023-08-19 12:49:23 +00:00
{
struct
{
f32 x;
f32 y;
2023-08-19 12:49:23 +00:00
};
f32 c[OC_UI_AXIS_COUNT];
} margin;
2023-08-19 12:49:23 +00:00
oc_ui_layout_align align;
2023-08-19 12:49:23 +00:00
} oc_ui_layout;
2023-08-19 12:49:23 +00:00
typedef enum oc_ui_size_kind
{
OC_UI_SIZE_TEXT,
OC_UI_SIZE_PIXELS,
OC_UI_SIZE_CHILDREN,
OC_UI_SIZE_PARENT,
OC_UI_SIZE_PARENT_MINUS_PIXELS,
2023-08-19 12:49:23 +00:00
} oc_ui_size_kind;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_size
{
oc_ui_size_kind kind;
f32 value;
f32 relax;
} oc_ui_size;
2023-08-19 12:49:23 +00:00
typedef union oc_ui_box_size
{
struct
2023-08-19 12:49:23 +00:00
{
oc_ui_size width;
oc_ui_size height;
};
2023-08-19 12:49:23 +00:00
oc_ui_size c[OC_UI_AXIS_COUNT];
} oc_ui_box_size;
2023-08-19 12:49:23 +00:00
typedef union oc_ui_box_floating
{
struct
2023-08-19 12:49:23 +00:00
{
bool x;
bool y;
2023-08-19 12:49:23 +00:00
};
bool c[OC_UI_AXIS_COUNT];
} oc_ui_box_floating;
2023-08-19 12:49:23 +00:00
//NOTE: flags for axis-dependent properties (e.g. OC_UI_STYLE_FLOAT_X/Y) need to be consecutive bits
// in order to play well with axis agnostic functions
typedef u64 oc_ui_style_mask;
2023-08-19 12:49:23 +00:00
enum
{
OC_UI_STYLE_NONE = 0,
OC_UI_STYLE_SIZE_WIDTH = 1 << 1,
OC_UI_STYLE_SIZE_HEIGHT = 1 << 2,
OC_UI_STYLE_LAYOUT_AXIS = 1 << 3,
OC_UI_STYLE_LAYOUT_ALIGN_X = 1 << 4,
OC_UI_STYLE_LAYOUT_ALIGN_Y = 1 << 5,
OC_UI_STYLE_LAYOUT_SPACING = 1 << 6,
OC_UI_STYLE_LAYOUT_MARGIN_X = 1 << 7,
OC_UI_STYLE_LAYOUT_MARGIN_Y = 1 << 8,
OC_UI_STYLE_FLOAT_X = 1 << 9,
OC_UI_STYLE_FLOAT_Y = 1 << 10,
OC_UI_STYLE_COLOR = 1 << 11,
OC_UI_STYLE_BG_COLOR = 1 << 12,
OC_UI_STYLE_BORDER_COLOR = 1 << 13,
OC_UI_STYLE_BORDER_SIZE = 1 << 14,
OC_UI_STYLE_ROUNDNESS = 1 << 15,
OC_UI_STYLE_FONT = 1 << 16,
OC_UI_STYLE_FONT_SIZE = 1 << 17,
OC_UI_STYLE_ANIMATION_TIME = 1 << 18,
OC_UI_STYLE_ANIMATION_MASK = 1 << 19,
//masks
OC_UI_STYLE_SIZE = OC_UI_STYLE_SIZE_WIDTH
| OC_UI_STYLE_SIZE_HEIGHT,
OC_UI_STYLE_LAYOUT_MARGINS = OC_UI_STYLE_LAYOUT_MARGIN_X
| OC_UI_STYLE_LAYOUT_MARGIN_Y,
OC_UI_STYLE_LAYOUT = OC_UI_STYLE_LAYOUT_AXIS
| OC_UI_STYLE_LAYOUT_ALIGN_X
| OC_UI_STYLE_LAYOUT_ALIGN_Y
| OC_UI_STYLE_LAYOUT_SPACING
| OC_UI_STYLE_LAYOUT_MARGIN_X
| OC_UI_STYLE_LAYOUT_MARGIN_Y,
OC_UI_STYLE_FLOAT = OC_UI_STYLE_FLOAT_X
| OC_UI_STYLE_FLOAT_Y,
OC_UI_STYLE_MASK_INHERITED = OC_UI_STYLE_COLOR
| OC_UI_STYLE_FONT
| OC_UI_STYLE_FONT_SIZE
| OC_UI_STYLE_ANIMATION_TIME
| OC_UI_STYLE_ANIMATION_MASK,
};
typedef struct oc_ui_style
{
oc_ui_box_size size;
oc_ui_layout layout;
oc_ui_box_floating floating;
oc_vec2 floatTarget;
oc_color color;
oc_color bgColor;
oc_color borderColor;
oc_font font;
f32 fontSize;
f32 borderSize;
f32 roundness;
f32 animationTime;
oc_ui_style_mask animationMask;
} oc_ui_style;
typedef struct oc_ui_theme
{
oc_color white;
oc_color primary;
oc_color primaryHover;
oc_color primaryActive;
oc_color fill0;
oc_color fill1;
oc_color fill2;
oc_color bg0;
oc_color bg1;
oc_color bg2;
oc_color bg3;
oc_color bg4;
oc_color text0;
oc_color text1;
oc_color text2;
oc_color text3;
} oc_ui_theme;
typedef struct oc_ui_tag
{
u64 hash;
} oc_ui_tag;
2023-08-19 12:49:23 +00:00
typedef enum
{
OC_UI_SEL_ANY,
OC_UI_SEL_OWNER,
OC_UI_SEL_TEXT,
OC_UI_SEL_TAG,
OC_UI_SEL_STATUS,
OC_UI_SEL_KEY,
//...
} oc_ui_selector_kind;
typedef u8 oc_ui_status;
enum
{
OC_UI_NONE = 0,
OC_UI_HOVER = 1 << 1,
OC_UI_ACTIVE = 1 << 2,
OC_UI_DRAGGING = 1 << 3,
};
2023-08-19 12:49:23 +00:00
typedef enum
{
OC_UI_SEL_DESCENDANT = 0,
OC_UI_SEL_AND = 1,
//...
} oc_ui_selector_op;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_selector
{
oc_list_elt listElt;
oc_ui_selector_kind kind;
oc_ui_selector_op op;
2023-08-19 12:49:23 +00:00
union
2023-08-19 12:49:23 +00:00
{
oc_str8 text;
oc_ui_key key;
oc_ui_tag tag;
oc_ui_status status;
//...
};
} oc_ui_selector;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_pattern
{
oc_list l;
} oc_ui_pattern;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_box oc_ui_box;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_style_rule
{
oc_list_elt boxElt;
oc_list_elt buildElt;
oc_list_elt tmpElt;
2023-08-19 12:49:23 +00:00
oc_ui_box* owner;
oc_ui_pattern pattern;
oc_ui_style_mask mask;
oc_ui_style* style;
} oc_ui_style_rule;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_sig
{
oc_ui_box* box;
2023-08-19 12:49:23 +00:00
oc_vec2 mouse;
oc_vec2 delta;
oc_vec2 wheel;
2023-08-19 12:49:23 +00:00
bool pressed;
bool released;
bool clicked;
bool doubleClicked;
bool rightPressed;
2023-08-19 12:49:23 +00:00
bool dragging;
bool hovering;
2023-08-19 12:49:23 +00:00
} oc_ui_sig;
2023-08-19 12:49:23 +00:00
typedef void (*oc_ui_box_draw_proc)(oc_ui_box* box, void* data);
2023-08-19 12:49:23 +00:00
typedef enum
{
OC_UI_FLAG_CLICKABLE = (1 << 0),
OC_UI_FLAG_SCROLL_WHEEL_X = (1 << 1),
OC_UI_FLAG_SCROLL_WHEEL_Y = (1 << 2),
OC_UI_FLAG_BLOCK_MOUSE = (1 << 3),
OC_UI_FLAG_HOT_ANIMATION = (1 << 4),
OC_UI_FLAG_ACTIVE_ANIMATION = (1 << 5),
//WARN: these two following flags need to be kept as consecutive bits to
// play well with axis-agnostic functions
OC_UI_FLAG_ALLOW_OVERFLOW_X = (1 << 6),
OC_UI_FLAG_ALLOW_OVERFLOW_Y = (1 << 7),
OC_UI_FLAG_CLIP = (1 << 8),
OC_UI_FLAG_DRAW_BACKGROUND = (1 << 9),
OC_UI_FLAG_DRAW_FOREGROUND = (1 << 10),
OC_UI_FLAG_DRAW_BORDER = (1 << 11),
OC_UI_FLAG_DRAW_TEXT = (1 << 12),
OC_UI_FLAG_DRAW_PROC = (1 << 13),
OC_UI_FLAG_OVERLAY = (1 << 14),
} oc_ui_flags;
struct oc_ui_box
{
// hierarchy
oc_list_elt listElt;
oc_list children;
oc_ui_box* parent;
oc_list_elt overlayElt;
// keying and caching
oc_list_elt bucketElt;
oc_ui_key key;
u64 frameCounter;
// builder-provided info
oc_ui_flags flags;
oc_str8 string;
oc_list tags;
oc_ui_box_draw_proc drawProc;
void* drawData;
// styling
oc_list beforeRules;
oc_list afterRules;
//oc_ui_style_tag tag;
oc_ui_style* targetStyle;
oc_ui_style style;
u32 z;
oc_vec2 floatPos;
f32 childrenSum[2];
f32 spacing[2];
oc_rect rect;
// signals
oc_ui_sig* sig;
// stateful behaviour
bool fresh;
bool closed;
bool parentClosed;
bool dragging;
bool hot;
bool active;
oc_vec2 scroll;
oc_vec2 pressedMouse;
// animation data
f32 hotTransition;
f32 activeTransition;
};
//-----------------------------------------------------------------------------
// context
//-----------------------------------------------------------------------------
enum
{
OC_UI_MAX_INPUT_CHAR_PER_FRAME = 64
};
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_input_text
{
u8 count;
oc_utf32 codePoints[OC_UI_MAX_INPUT_CHAR_PER_FRAME];
2023-08-19 12:49:23 +00:00
} oc_ui_input_text;
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_stack_elt oc_ui_stack_elt;
2023-08-19 12:49:23 +00:00
struct oc_ui_stack_elt
{
oc_ui_stack_elt* parent;
2023-08-19 12:49:23 +00:00
union
2023-08-19 12:49:23 +00:00
{
oc_ui_box* box;
oc_ui_size size;
oc_rect clip;
2023-08-19 12:49:23 +00:00
};
};
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_tag_elt
{
oc_list_elt listElt;
oc_ui_tag tag;
} oc_ui_tag_elt;
2023-08-19 12:49:23 +00:00
enum
{
OC_UI_BOX_MAP_BUCKET_COUNT = 1024
};
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_context
{
bool init;
2023-08-19 12:49:23 +00:00
oc_input_state input;
2023-08-19 12:49:23 +00:00
u64 frameCounter;
f64 frameTime;
f64 lastFrameDuration;
2023-08-19 12:49:23 +00:00
oc_arena frameArena;
oc_pool boxPool;
oc_list boxMap[OC_UI_BOX_MAP_BUCKET_COUNT];
2023-08-19 12:49:23 +00:00
oc_ui_box* root;
oc_ui_box* overlay;
oc_list overlayList;
oc_ui_stack_elt* boxStack;
oc_ui_stack_elt* clipStack;
2023-08-19 12:49:23 +00:00
oc_list nextBoxBeforeRules;
oc_list nextBoxAfterRules;
oc_list nextBoxTags;
2023-08-19 12:49:23 +00:00
u32 z;
oc_ui_box* hovered;
2023-08-19 12:49:23 +00:00
oc_ui_box* focus;
i32 editCursor;
i32 editMark;
i32 editFirstDisplayedChar;
f64 editCursorBlinkStart;
2023-08-19 12:49:23 +00:00
} oc_ui_context;
2023-08-19 12:49:23 +00:00
//-------------------------------------------------------------------------------------
// UI context initialization and frame cycle
//-------------------------------------------------------------------------------------
ORCA_API void oc_ui_init(oc_ui_context* context);
ORCA_API oc_ui_context* oc_ui_get_context(void);
ORCA_API void oc_ui_set_context(oc_ui_context* context);
2023-08-19 12:49:23 +00:00
ORCA_API void oc_ui_process_event(oc_event* event);
ORCA_API void oc_ui_begin_frame(oc_vec2 size, oc_ui_style* defaultStyle, oc_ui_style_mask mask);
ORCA_API void oc_ui_end_frame(void);
ORCA_API void oc_ui_draw(void);
2023-08-19 12:49:23 +00:00
#define oc_ui_frame(size, style, mask) oc_defer_loop(oc_ui_begin_frame((size), (style), (mask)), oc_ui_end_frame())
//-------------------------------------------------------------------------------------
// Box keys
//-------------------------------------------------------------------------------------
ORCA_API oc_ui_key oc_ui_key_make_str8(oc_str8 string);
ORCA_API oc_ui_key oc_ui_key_make_path(oc_str8_list path);
2023-08-19 12:49:23 +00:00
ORCA_API oc_ui_box* oc_ui_box_lookup_key(oc_ui_key key);
ORCA_API oc_ui_box* oc_ui_box_lookup_str8(oc_str8 string);
2023-08-19 12:49:23 +00:00
// C-string helper
#define oc_ui_key_make(s) oc_ui_key_make_str8(OC_STR8(s))
#define oc_ui_box_lookup(s) oc_ui_box_lookup_str8(OC_STR8(s))
//-------------------------------------------------------------------------------------
// Box hierarchy building
//-------------------------------------------------------------------------------------
ORCA_API oc_ui_box* oc_ui_box_make_str8(oc_str8 string, oc_ui_flags flags);
ORCA_API oc_ui_box* oc_ui_box_begin_str8(oc_str8 string, oc_ui_flags flags);
2023-08-19 12:49:23 +00:00
ORCA_API oc_ui_box* oc_ui_box_end(void);
2023-08-19 12:49:23 +00:00
#define oc_ui_container(name, flags) oc_defer_loop(oc_ui_box_begin(name, flags), oc_ui_box_end())
#define oc_ui_container_str8(name, flags) oc_defer_loop(oc_ui_box_begin_str8(name, flags), oc_ui_box_end())
ORCA_API void oc_ui_box_push(oc_ui_box* box);
ORCA_API void oc_ui_box_pop(void);
ORCA_API oc_ui_box* oc_ui_box_top(void);
2023-08-19 12:49:23 +00:00
ORCA_API void oc_ui_box_set_draw_proc(oc_ui_box* box, oc_ui_box_draw_proc proc, void* data);
2023-08-19 12:49:23 +00:00
// C-string helpers
#define oc_ui_box_lookup(s) oc_ui_box_lookup_str8(OC_STR8(s))
#define oc_ui_box_make(s, flags) oc_ui_box_make_str8(OC_STR8(s), flags)
#define oc_ui_box_begin(s, flags) oc_ui_box_begin_str8(OC_STR8(s), flags)
//-------------------------------------------------------------------------------------
// Box status and signals
//-------------------------------------------------------------------------------------
ORCA_API bool oc_ui_box_closed(oc_ui_box* box);
ORCA_API void oc_ui_box_set_closed(oc_ui_box* box, bool closed);
2023-08-19 12:49:23 +00:00
ORCA_API bool oc_ui_box_active(oc_ui_box* box);
ORCA_API void oc_ui_box_activate(oc_ui_box* box);
ORCA_API void oc_ui_box_deactivate(oc_ui_box* box);
2023-08-19 12:49:23 +00:00
ORCA_API bool oc_ui_box_hot(oc_ui_box* box);
ORCA_API void oc_ui_box_set_hot(oc_ui_box* box, bool hot);
2023-08-19 12:49:23 +00:00
ORCA_API oc_ui_sig oc_ui_box_sig(oc_ui_box* box);
2023-08-19 12:49:23 +00:00
//-------------------------------------------------------------------------------------
// Tagging
//-------------------------------------------------------------------------------------
ORCA_API oc_ui_tag oc_ui_tag_make_str8(oc_str8 string);
ORCA_API void oc_ui_tag_box_str8(oc_ui_box* box, oc_str8 string);
ORCA_API void oc_ui_tag_next_str8(oc_str8 string);
2023-08-19 12:49:23 +00:00
// C-string helpers
#define oc_ui_tag_make(s) oc_ui_tag_make_str8(OC_STR8(s))
#define oc_ui_tag_box(b, s) oc_ui_tag_box_str8(b, OC_STR8(s))
#define oc_ui_tag_next(s) oc_ui_tag_next_str8(OC_STR8(s))
//-------------------------------------------------------------------------------------
// Styling
//-------------------------------------------------------------------------------------
//NOTE: styling API
//WARN: You can use a pattern in multiple rules, but be aware that a pattern is references an underlying list of selectors,
// hence pushing to a pattern also modifies rules in which the pattern was previously used!
ORCA_API void oc_ui_apply_style_with_mask(oc_ui_style* dst, oc_ui_style* src, oc_ui_style_mask mask);
ORCA_API void oc_ui_pattern_push(oc_arena* arena, oc_ui_pattern* pattern, oc_ui_selector selector);
ORCA_API oc_ui_pattern oc_ui_pattern_all(void);
ORCA_API oc_ui_pattern oc_ui_pattern_owner(void);
ORCA_API void oc_ui_style_next(oc_ui_style* style, oc_ui_style_mask mask);
ORCA_API void oc_ui_style_match_before(oc_ui_pattern pattern, oc_ui_style* style, oc_ui_style_mask mask);
ORCA_API void oc_ui_style_match_after(oc_ui_pattern pattern, oc_ui_style* style, oc_ui_style_mask mask);
//-------------------------------------------------------------------------
// Basic widget helpers
//-------------------------------------------------------------------------
enum
{
OC_UI_STYLE_TAG_USER_MAX = 1 << 16,
OC_UI_STYLE_TAG_LABEL,
OC_UI_STYLE_TAG_BUTTON,
OC_UI_STYLE_TAG_SCROLLBAR,
OC_UI_STYLE_TAG_PANEL,
OC_UI_STYLE_TAG_TOOLTIP,
OC_UI_STYLE_TAG_MENU
};
ORCA_API oc_ui_sig oc_ui_label(const char* label);
ORCA_API oc_ui_sig oc_ui_label_str8(oc_str8 label);
ORCA_API oc_ui_sig oc_ui_button(const char* label);
ORCA_API oc_ui_sig oc_ui_checkbox(const char* name, bool* checked);
ORCA_API oc_ui_box* oc_ui_slider(const char* label, f32* value);
ORCA_API oc_ui_box* oc_ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue);
ORCA_API void oc_ui_tooltip(const char* label);
ORCA_API void oc_ui_panel_begin(const char* name, oc_ui_flags flags);
ORCA_API void oc_ui_panel_end(void);
2023-08-19 12:49:23 +00:00
#define oc_ui_panel(s, f) oc_defer_loop(oc_ui_panel_begin(s, f), oc_ui_panel_end())
ORCA_API void oc_ui_menu_bar_begin(const char* label);
ORCA_API void oc_ui_menu_bar_end(void);
2023-08-19 12:49:23 +00:00
#define oc_ui_menu_bar(name) oc_defer_loop(oc_ui_menu_bar_begin(name), oc_ui_menu_bar_end())
ORCA_API void oc_ui_menu_begin(const char* label);
ORCA_API void oc_ui_menu_end(void);
2023-08-19 12:49:23 +00:00
#define oc_ui_menu(name) oc_defer_loop(oc_ui_menu_begin(name), oc_ui_menu_end())
ORCA_API oc_ui_sig oc_ui_menu_button(const char* name);
2023-08-19 12:49:23 +00:00
typedef struct oc_ui_text_box_result
{
bool changed;
bool accepted;
oc_str8 text;
2023-08-19 12:49:23 +00:00
} oc_ui_text_box_result;
2023-08-19 12:49:23 +00:00
ORCA_API oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 text);
typedef struct oc_ui_select_popup_info
{
bool changed;
int selectedIndex;
int optionCount;
oc_str8* options;
} oc_ui_select_popup_info;
ORCA_API oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_info* info);
2023-08-19 12:49:23 +00:00
#ifdef __cplusplus
} // extern "C"
#endif
#endif //__UI_H_