splink/source/ui.c

873 lines
25 KiB
C

////////////////////////////////
// NOTE(allen): Colors
global v3 cl_black = {0.f, 0.f, 0.f};
global v3 cl_white = {1.f, 1.f, 1.f};
#define cl_gray(f) v3((f), (f), (f))
global v3 cl_red = {1.f, 0.f, 0.f};
global v3 cl_yellow = {1.f, 1.f, 0.f};
global v3 cl_green = {0.f, 1.f, 0.f};
global v3 cl_cyan = {0.f, 1.f, 1.f};
global v3 cl_blue = {0.f, 0.f, 1.f};
global v3 cl_awful = {1.f, 0.f, 1.f};
////////////////////////////////
global v3 cl_back_editor = {0.15f, 0.15f, 0.15f};
global v3 cl_back_unimportant = {0.00f, 0.00f, 0.00f};
global v3 cl_back = {0.21f, 0.21f, 0.21f};
global v3 cl_back_flash = {0.50f, 0.50f, 0.50f};
global v3 cl_outline_unimportant = {0.15f, 0.30f, 0.15f};
global v3 cl_outline_important = {0.09f, 0.50f, 0.09f};
global v3 cl_outline = {0.00f, 0.70f, 0.00f};
global v3 cl_outline_flash = {0.50f, 0.85f, 0.50f};
global v3 cl_button_text = {1.f, 1.f, 0.8f};
global v3 cl_button_text_flash = {0.f, 0.f, 0.2f};
global v3 cl_tab_outline_unimportant = {0.15f, 0.15f, 0.15f};
global v3 cl_tab_outline = {0.50f, 0.50f, 0.50f};
global v3 cl_err_back_unimportant = {0.100f, 0.00f, 0.00f};
global v3 cl_err_back = {0.299f, 0.21f, 0.21};
global v3 cl_err_back_flash = {0.550f, 0.50f, 0.50f};
global v3 cl_err_outline_unimportant = {0.30f, 0.195f, 0.15f};
global v3 cl_err_outline_important = {0.50f, 0.213f, 0.09f};
global v3 cl_err_outline = {0.70f, 0.210f, 0.00f};
global v3 cl_err_outline_flash = {0.85f, 0.605f, 0.50f};
global v3 cl_page_back_unimportant = {0.100f, 0.000f, 0.00f};
global v3 cl_page_back = {0.299f, 0.299f, 0.21f};
global v3 cl_page_back_flash = {0.550f, 0.550f, 0.50f};
global v3 cl_page_outline_unimportant = {0.30f, 0.30f, 0.15f};
global v3 cl_page_outline_important = {0.50f, 0.50f, 0.09f};
global v3 cl_page_outline = {0.70f, 0.70f, 0.00f};
global v3 cl_page_outline_flash = {0.85f, 0.85f, 0.50f};
global v3 cl_test_back_unimportant = {0.050f, 0.000f, 0.050f};
global v3 cl_test_back = {0.150f, 0.105f, 0.150f};
global v3 cl_test_back_flash = {0.275f, 0.250f, 0.275f};
global v3 cl_test_outline_unimportant = {0.200f, 0.130f, 0.200f};
global v3 cl_test_outline_important = {0.250f, 0.106f, 0.250f};
global v3 cl_test_outline = {0.350f, 0.105f, 0.350f};
global v3 cl_test_outline_flash = {0.425f, 0.303f, 0.425f};
global v3 cl_menu_bar = {0.70f, 0.70f, 0.70f};
global v3 cl_error = {1.00f, 0.00f, 0.00f};
global v3 cl_text = {1.00f, 1.00f, 1.00f};
global v3 cl_comment = {0.50f, 0.50f, 0.65f};
global v3 cl_keyword = {0.95f, 0.99f, 0.50f};
////////////////////////////////
internal UI_ColorProfile*
UI_ColorsDefault(void){
local_persist UI_ColorProfile result = {0};
local_persist b32 first = 1;
if (first){
first = 0;
result.back[0] = cl_back_unimportant;
result.back[1] = cl_back;
result.back[2] = cl_back;
result.back[3] = cl_back_flash;
result.outline[0] = cl_outline_unimportant;
result.outline[1] = cl_outline_important;
result.outline[2] = cl_outline;
result.outline[3] = cl_outline_flash;
result.front[0] = cl_button_text;
result.front[1] = cl_button_text;
result.front[2] = cl_button_text;
result.front[3] = cl_button_text_flash;
}
return(&result);
}
internal UI_ColorProfile*
UI_ColorsProblem(void){
local_persist UI_ColorProfile result = {0};
local_persist b32 first = 1;
if (first){
first = 0;
result.back[0] = cl_err_back_unimportant;
result.back[1] = cl_err_back;
result.back[2] = cl_err_back;
result.back[3] = cl_err_back_flash;
result.outline[0] = cl_err_outline_unimportant;
result.outline[1] = cl_err_outline_important;
result.outline[2] = cl_err_outline;
result.outline[3] = cl_err_outline_flash;
result.front[0] = cl_button_text;
result.front[1] = cl_button_text;
result.front[2] = cl_button_text;
result.front[3] = cl_button_text_flash;
}
return(&result);
}
internal UI_ColorProfile*
UI_ColorsTabs(void){
local_persist UI_ColorProfile result = {0};
local_persist b32 first = 1;
if (first){
first = 0;
result.back[0] = cl_back_unimportant;
result.back[1] = cl_back_editor;
result.back[2] = cl_back;
result.back[3] = cl_back;
result.outline[0] = cl_tab_outline_unimportant;
result.outline[1] = cl_back_editor;
result.outline[2] = cl_tab_outline;
result.outline[3] = cl_tab_outline;
for (u64 i = 0; i < (u64)UI_ActionLevel_COUNT; i += 1){
result.front[i] = cl_button_text;
}
}
return(&result);
}
internal UI_ColorProfile*
UI_ColorsPage(void){
local_persist UI_ColorProfile result = {0};
local_persist b32 first = 1;
if (first){
first = 0;
result.back[0] = cl_page_back_unimportant;
result.back[1] = cl_page_back;
result.back[2] = cl_page_back;
result.back[3] = cl_page_back_flash;
result.outline[0] = cl_page_outline_unimportant;
result.outline[1] = cl_page_outline_important;
result.outline[2] = cl_page_outline;
result.outline[3] = cl_page_outline_flash;
result.front[0] = cl_button_text;
result.front[1] = cl_button_text;
result.front[2] = cl_button_text;
result.front[3] = cl_button_text_flash;
}
return(&result);
}
internal UI_ColorProfile*
UI_ColorsTest(void){
local_persist UI_ColorProfile result = {0};
local_persist b32 first = 1;
if (first){
first = 0;
result.back[0] = cl_test_back_unimportant;
result.back[1] = cl_test_back;
result.back[2] = cl_test_back;
result.back[3] = cl_test_back_flash;
result.outline[0] = cl_test_outline_unimportant;
result.outline[1] = cl_test_outline_important;
result.outline[2] = cl_test_outline;
result.outline[3] = cl_test_outline_flash;
result.front[0] = cl_button_text;
result.front[1] = cl_button_text;
result.front[2] = cl_button_text;
result.front[3] = cl_button_text_flash;
}
return(&result);
}
internal void
UI_SetRedText(UI_ColorProfile *cl){
for (u64 i = 0; i < (u64)UI_ActionLevel_COUNT; i += 1){
cl->front[i] = cl_red;
}
}
internal v3
UI_Grayified(v3 color){
v3 hsv = RGBToHSV(color);
hsv.y *= 0.5f;
hsv.z *= 0.4f;
v3 result = HSVToRGB(hsv);
return(result);
}
////////////////////////////////
// NOTE(allen): Event Helpers
internal b32
UI_MouseInRect(Rect rect){
b32 result = 0;
if (APP_MouseIsActive()){
Rect clipped = RectIntersect(rect, R_GetClip());
result = RectContains(clipped, vars->mouse_p);
}
return(result);
}
internal b32
UI_TryGetLeftClick(OS_Event **event_out){
b32 result = 0;
OS_Event *event = 0;
for (;OS_GetNextEvent(&event);){
if (event->type == OS_EventType_MousePress && event->mouse_button == MouseButton_Left){
*event_out = event;
result = 1;
}
}
return(result);
}
internal b32
UI_TryEatLeftClick(void){
OS_Event *event = 0;
b32 result = UI_TryGetLeftClick(&event);
if (result){
OS_EatEvent(event);
}
return(result);
}
internal b32
UI_TryGetKeyPress(OS_Event **event_out, Key key){
b32 result = 0;
OS_Event *event = 0;
for (;OS_GetNextEvent(&event);){
if (event->type == OS_EventType_KeyPress && event->key == key){
*event_out = event;
result = 1;
}
}
return(result);
}
internal b32
UI_TryEatKeyPress(Key key){
OS_Event *event = 0;
b32 result = UI_TryGetKeyPress(&event, key);
if (result){
OS_EatEvent(event);
}
return(result);
}
internal b32
UI_TryGetKeyPressModified(OS_Event **event_out, Key key, KeyModifiers mods){
b32 result = 0;
OS_Event *event = 0;
for (;OS_GetNextEvent(&event);){
if (event->type == OS_EventType_KeyPress && event->key == key && event->modifiers == mods){
*event_out = event;
result = 1;
}
}
return(result);
}
internal b32
UI_TryEatKeyPressModified(Key key, KeyModifiers mods){
OS_Event *event = 0;
b32 result = UI_TryGetKeyPressModified(&event, key, mods);
if (result){
OS_EatEvent(event);
}
return(result);
}
internal b32
UI_TryGetScroll(OS_Event **event_out){
b32 result = 0;
OS_Event *event = 0;
for (;OS_GetNextEvent(&event);){
if (event->type == OS_EventType_MouseScroll){
*event_out = event;
result = 1;
}
}
return(result);
}
internal b32
UI_TryEatScroll(v2 *scroll_out){
OS_Event *event = 0;
b32 result = UI_TryGetScroll(&event);
if (result){
*scroll_out = event->scroll;
OS_EatEvent(event);
}
return(result);
}
internal String8
UI_StringizeKeyModified(M_Arena *arena, Key key, KeyModifiers mods){
String8_List list = {0};
if (mods & KeyModifier_Ctrl){
StringListPush(arena, &list, S8Lit("ctrl-"));
}
if (mods & KeyModifier_Shift){
StringListPush(arena, &list, S8Lit("shift-"));
}
if (mods & KeyModifier_Alt){
StringListPush(arena, &list, S8Lit("alt-"));
}
String8 key_name = KeyName(key);
StringListPush(arena, &list, key_name);
return(StringListJoin(arena, &list, 0));
}
////////////////////////////////
// NOTE(allen): UI ID
internal b32
UI_IdEq(UI_Id a, UI_Id b){
return(MemoryMatchStruct(&a, &b));
}
internal UI_Id
UI_IdZero(void){
UI_Id id = {0};
return(id);
}
internal UI_Id
UI_IdV(u64 v){
UI_Id id;
id.v1 = v;
return(id);
}
internal UI_Id
UI_IdP(void *p){
UI_Id id;
id.p1 = p;
return(id);
}
////////////////////////////////
// NOTE(allen): Button placer
internal void
UI_SetColorProfile(UI_ButtonCtx *ctx, UI_ColorProfile *profile){
MemoryCopyStruct(&ctx->cl, profile);
}
internal UI_ButtonCtx
UI_InitButtonCtx(Rect rect, v2 btn_dim, R_Font *font, UI_Id id){
UI_ButtonCtx result = {0};
v2 rect_dim = RectGetDim(rect);
if (rect_dim.x >= btn_dim.x && rect_dim.y >= btn_dim.y){
result.font = font;
result.id = id;
result.button_id.v1 = 1;
result.rect = rect;
result.p = rect.p0;
result.btn_dim = btn_dim;
result.has_room = 1;
result.condition = 1;
result.text_scale = 1;
UI_SetColorProfile(&result, UI_ColorsDefault());
}
return(result);
}
internal f32
UI_ButtonCtxGetTraversedY(UI_ButtonCtx *ctx){
return(ctx->p.y - ctx->rect.y0);
}
internal void
UI_NextCondition(UI_ButtonCtx *ctx, b32 condition){
ctx->condition = condition;
}
internal void
UI_NextHotkey(UI_ButtonCtx *ctx, Key key, KeyModifiers mods){
ctx->has_hot_key = 1;
ctx->hot_key = key;
ctx->hot_key_mods = mods;
}
internal void
UI_NextTooltip(UI_ButtonCtx *ctx, String8 string){
ctx->tool_tip = string;
}
internal void
UI_NextActive(UI_ButtonCtx *ctx, b32 active){
ctx->active = active;
}
internal void
_UI_ButtonHotkey(UI_ButtonCtx *ctx, b32 *result_out){
if (ctx->enable_hot_keys && ctx->has_hot_key && ctx->condition){
if (UI_TryEatKeyPressModified(ctx->hot_key, ctx->hot_key_mods)){
*result_out = 1;
APP_ZeroOwnershipOfFloatingWindow();
}
}
}
internal f32
_UI_ButtonGetXAdvance(UI_ButtonCtx *ctx){
f32 advance = 0;
if (ctx->enable_flexible_x_advance){
advance = ClampTop(ctx->this_button_x_advance, ctx->btn_dim.x);
}
else{
advance = ctx->btn_dim.x;
}
return(advance);
}
internal b32
_UI_ButtonGetNextP(UI_ButtonCtx *ctx, v2 p, v2 *out){
b32 result = 1;
v2 dim = ctx->btn_dim;
p.x += _UI_ButtonGetXAdvance(ctx);
if (p.x + dim.x > ctx->rect.x1){
p.x = ctx->rect.x0;
p.y += dim.y;
if (p.y + dim.y > ctx->rect.y1){
result = 0;
}
}
*out = p;
return(result);
}
internal b32
_UI_ButtonNextWillFit(UI_ButtonCtx *ctx){
b32 result = 1;
v2 p = ctx->p;
if (!_UI_ButtonGetNextP(ctx, p, &p)){
result = 0;
}
return(result);
}
internal Rect
_UI_ButtonPre(UI_ButtonCtx *ctx, UI_ActionLevel *action_level_out, b32 *result_out){
// NOTE(allen): Drop down check
ctx->do_drop_down = (ctx->enable_drop_down && !_UI_ButtonNextWillFit(ctx));
if (ctx->do_drop_down){
ctx->restore.condition = ctx->condition;
ctx->restore.active = ctx->active;
ctx->restore.has_hot_key = ctx->has_hot_key;
ctx->restore.tool_tip = ctx->tool_tip;
ctx->condition = 1;
ctx->active = 0;
ctx->has_hot_key = 0;
ctx->tool_tip = S8Lit("more");
ctx->has_room = 0;
}
// NOTE(allen): Layout
f32 right = ctx->p.x + _UI_ButtonGetXAdvance(ctx);;
Rect rect = MakeRect(V2Expand(ctx->p), right, ctx->p.y + ctx->btn_dim.y);
// NOTE(allen): Mouse
UI_ActionLevel action_level = UI_ActionLevel_None;
if (ctx->active){
action_level = UI_ActionLevel_Active;
}
ctx->did_tool_tip = 0;
if (UI_MouseInRect(rect)){
if (ctx->condition){
action_level = UI_ActionLevel_Hover;
if (UI_TryEatLeftClick()){
*result_out = 1;
action_level = UI_ActionLevel_Flash;
}
}
// NOTE(allen): Tool tip
{
M_Arena *scratch = OS_GetScratch();
String8_List tool_tip = {0};
StringListPush(scratch, &tool_tip, ctx->tool_tip);
if (ctx->has_hot_key){
StringListPush(scratch, &tool_tip, S8Lit(" ["));
String8 key_str = UI_StringizeKeyModified(scratch, ctx->hot_key, ctx->hot_key_mods);
StringListPush(scratch, &tool_tip, key_str);
StringListPush(scratch, &tool_tip, S8Lit("]"));
}
String8 tool_tip_str = StringListJoin(APP_GetFrameArena(), &tool_tip, 0);
APP_SetToolTip(tool_tip_str);
OS_ReleaseScratch(scratch);
if (tool_tip_str.size > 0){
ctx->did_tool_tip = 1;
}
}
}
*action_level_out = action_level;
// NOTE(allen): Render back
R_SelectFont(ctx->font);
v3 back = ctx->cl.back[action_level];
v3 outline = ctx->cl.outline[action_level];
if (!ctx->condition){
back = UI_Grayified(back);
outline = UI_Grayified(outline);
}
R_Rect(rect, back, 1.f);
R_RectOutline(rect, ctx->outline_t, outline, 1.f);
return(rect);
}
internal void
_UI_ButtonPost(UI_ButtonCtx *ctx){
if (!_UI_ButtonGetNextP(ctx, ctx->p, &ctx->p)){
ctx->has_room = 0;
}
if (ctx->do_drop_down){
ctx->condition = ctx->restore.condition;
ctx->active = ctx->restore.active;
ctx->has_hot_key = ctx->restore.has_hot_key;
ctx->tool_tip = ctx->restore.tool_tip;
}
}
internal void
_UI_ButtonEatExtendedParameters(UI_ButtonCtx *ctx){
ctx->condition = 1;
ctx->active = 0;
ctx->has_hot_key = 0;
ctx->tool_tip.size = 0;
}
internal void
_UI_DropDownButtonClick(UI_ButtonCtx *ctx, b32 click_result){
if (ctx->enable_hot_keys){
if (UI_TryEatKeyPressModified(Key_ForwardSlash, KeyModifier_Ctrl)){
APP_TakeOwnershipOfFloatingWindow(ctx->id);
}
}
if (click_result){
APP_TakeOwnershipOfFloatingWindow(ctx->id);
}
}
internal void
UI_ButtonDropdownCallback(void *ptr, APP_FloatingWindowResult *result);
internal UI_ButtonRecord*
_UI_DropDownButtonSaveRecord(UI_ButtonCtx *ctx, Rect rect, b32 *result_out){
M_Arena *arena = APP_GetFrameArena();
UI_ButtonDropdown *last_frame_dropdown = (UI_ButtonDropdown*)APP_GetLastFrameFloatingWindowPtr();
if (last_frame_dropdown != 0){
UI_ButtonRecord *activated = last_frame_dropdown->activated;
if (activated != 0 && UI_IdEq(activated->id, ctx->button_id)){
*result_out = 1;
APP_ZeroOwnershipOfFloatingWindow();
}
}
UI_ButtonDropdown *dropdown = (UI_ButtonDropdown*)APP_GetFloatingWindowPtr();
if (dropdown == 0){
dropdown = PushArrayZero(arena, UI_ButtonDropdown, 1);
dropdown->font = ctx->font;
dropdown->btn_dim = ctx->btn_dim;
dropdown->source_rect = rect;
APP_SetFloatingWindowPtrAndCallback(dropdown, UI_ButtonDropdownCallback);
}
UI_ButtonRecord *record = PushArray(arena, UI_ButtonRecord, 1);
record->condition = ctx->condition;
record->has_hot_key = ctx->has_hot_key;
record->hot_key = ctx->hot_key;
record->hot_key_mods = ctx->hot_key_mods;
record->outline_t = ctx->outline_t;
record->text_scale = ctx->text_scale;
MemoryCopyStruct(&record->cl, &ctx->cl);
record->tool_tip = ctx->tool_tip;
record->id = ctx->button_id;
SLLQueuePush(dropdown->first, dropdown->last, record);
dropdown->count += 1;
return(record);
}
internal b32
UI_Button(UI_ButtonCtx *ctx, u8 major, u8 minor){
b32 result = 0;
_UI_ButtonHotkey(ctx, &result);
b32 did_button = ctx->has_room;
Rect rect = {0};
if (ctx->has_room){
b32 click_result = 0;
UI_ActionLevel action_level;
rect = _UI_ButtonPre(ctx, &action_level, &click_result);
String8 on_screen_major = S8(&major, 1);
String8 on_screen_minor = S8(&minor, 1);
v3 text_color = ctx->cl.front[action_level];
if (!ctx->condition){
text_color = UI_Grayified(text_color);
}
if (ctx->do_drop_down){
_UI_DropDownButtonClick(ctx, click_result);
on_screen_major = S8Lit("/");
on_screen_minor = S8Lit(" ");
text_color = cl_button_text;
did_button = 0;
}
else{
if (click_result){
result = 1;
}
}
f32 major_scale = ctx->text_scale;
f32 minor_scale = ctx->text_scale*0.65f;
v2 major_dim = R_StringDim(major_scale, on_screen_major);
v2 minor_dim = R_StringDim(minor_scale, on_screen_minor);
if (on_screen_minor.str[0] == ' '){
minor_dim.x = 0.f;
minor_dim.y = 0.f;
}
v2 major_p = {0};
v2 minor_p = {0};
minor_p.x = major_p.x + 0.8f*major_dim.x;
// align 0.7 of the way down major with 0.5 of the way down minor
// solve for minor_y in (major_y + 0.7*major_h = minor_y + 0.5*minor_h)
minor_p.y = major_p.y + 0.7f*major_dim.y - 0.5f*minor_dim.y;
v2 combined_dim;
combined_dim.x = Max(major_p.x + major_dim.x, minor_p.x + minor_dim.x);
combined_dim.y = Max(major_p.y + major_dim.y, minor_p.y + minor_dim.y);
v2 combined_half_dim = V2Mul(combined_dim, 0.5f);
v2 center = RectGetCenter(rect);
major_p = V2Sub(center, combined_half_dim);
minor_p = V2Add(major_p, minor_p);
major_p.x = f32Floor(major_p.x);
major_p.y = f32Floor(major_p.y);
minor_p.x = f32Floor(minor_p.x);
minor_p.y = f32Floor(minor_p.y);
R_String(major_p, major_scale, on_screen_major, text_color, 1.f);
R_String(minor_p, minor_scale, on_screen_minor, text_color, 1.f);
_UI_ButtonPost(ctx);
}
if (ctx->enable_drop_down && !did_button){
if (UI_IdEq(APP_OwnerOfFloatingWindow(), ctx->id)){
b32 drop_result = 0;
UI_ButtonRecord *record = _UI_DropDownButtonSaveRecord(ctx, rect, &drop_result);
if (drop_result){
result = 1;
}
record->kind = UI_ButtonKind_Icon;
record->major = major;
record->minor = minor;
}
}
ctx->button_id.v1 += 1;
_UI_ButtonEatExtendedParameters(ctx);
return(result);
}
internal b32
UI_ButtonLabel(UI_ButtonCtx *ctx, String8 string){
b32 result = 0;
_UI_ButtonHotkey(ctx, &result);
b32 did_button = ctx->has_room;
Rect rect = {0};
if (ctx->has_room){
v2 dim = R_StringDim(ctx->text_scale, string);
if (ctx->enable_flexible_x_advance){
ctx->this_button_x_advance = dim.x + ctx->outline_t*2.f;
}
b32 click_result = 0;
UI_ActionLevel action_level;
rect = _UI_ButtonPre(ctx, &action_level, &click_result);
b32 did_tool_tip = ctx->did_tool_tip;
v3 text_color = ctx->cl.front[action_level];
if (!ctx->condition){
text_color = UI_Grayified(text_color);
}
String8 on_screen_string = string;
if (ctx->do_drop_down){
_UI_DropDownButtonClick(ctx, click_result);
on_screen_string = S8Lit("* more *");
text_color = cl_button_text;
did_button = 0;
}
else{
if (click_result){
result = 1;
}
}
Rect inner = RectShrink(rect, ctx->outline_t);
v2 p = {0};
p.x = inner.x0;
// vertically align center of text with center of box
// solve for y in: y + 0.5*h = center.y
p.y = RectGetCenter(inner).y - 0.5f*dim.y;
R_StringCapped(p, inner.x1, ctx->text_scale, on_screen_string, text_color, 1.f);
if ((action_level == UI_ActionLevel_Hover ||
action_level == UI_ActionLevel_Flash) &&
did_button && !did_tool_tip &&
dim.x > (inner.x1 - inner.x0)){
APP_SetToolTip(on_screen_string);
}
_UI_ButtonPost(ctx);
}
if (ctx->enable_drop_down && !did_button){
if (UI_IdEq(APP_OwnerOfFloatingWindow(), ctx->id)){
b32 drop_result = 0;
UI_ButtonRecord *record = _UI_DropDownButtonSaveRecord(ctx, rect, &drop_result);
if (drop_result){
result = 1;
}
record->kind = UI_ButtonKind_Label;
record->string = string;
}
}
ctx->button_id.v1 += 1;
_UI_ButtonEatExtendedParameters(ctx);
return(result);
}
internal void
UI_ButtonDropdownCallback(void *ptr, APP_FloatingWindowResult *result){
UI_ButtonDropdown *dropdown = (UI_ButtonDropdown*)ptr;
// NOTE(allen): Select drop down placement
f32 best_area = NegInf32();
v2 best_p = {0};
v2 best_dim = {0};
Side best_cast_dir[2] = {0};
Rect win = MakeRect(0, 0, V2Expand(vars->window_dim));
Rect src = dropdown->source_rect;
for (u32 x_it = 0; x_it < 2; x_it += 1){
u32 x_side = x_it^1;
for (u32 y_side = 0; y_side < 2; y_side += 1){
v2 p = v2(src.p[x_side].x, src.p[y_side].y);
Range x = MakeRange(p.x, win.p[x_side].x);
Range y = MakeRange(p.y, win.p[y_side^1].y);
v2 dim = v2(RangeSize(x), RangeSize(y));
f32 area = dim.x*dim.y;
if (area > best_area){
best_area = area;
best_p = p;
best_dim = dim;
best_cast_dir[Dimension_X] = x_side;
best_cast_dir[Dimension_Y] = y_side^1;
}
}
}
// NOTE(allen): Fit the drop down to a nice aesthetic rectangle
v2 weight = V2Hadamard(v2(1.f, SmallGolden32), dropdown->btn_dim);
v2 dim = v2(1, 1);
f32 count = (f32)dropdown->count;
for (;dim.x*dim.y < count;){
f32 scores[2];
for (u32 i = 0; i < 2; i += 1){
dim.v[i] += 1;
scores[i] = V2Dot(dim, weight)/(dim.x*dim.y);
dim.v[i] -= 1;
}
if (scores[0] <= scores[1]){
dim.v[0] += 1;
}
else{
dim.v[1] += 1;
}
}
v2 pixel_dim = V2Hadamard(dim, dropdown->btn_dim);
Range x = MakeRange(best_p.x, best_p.x + SignOfSide(best_cast_dir[Dimension_X])*pixel_dim.x);
Range y = MakeRange(best_p.y, best_p.y + SignOfSide(best_cast_dir[Dimension_Y])*pixel_dim.y);
Rect rect = MakeRectRanges(x, y);
Rect outer = RectGrow(rect, 4.f);
Rect inner = RectShrink(outer, 2.f);
// NOTE(allen): Place buttons
R_RectOutline(outer, 2.f, cl_white, 1.f);
R_Rect(inner, cl_black, 1.f);
inner.p0 = rect.p0;
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(inner, dropdown->btn_dim, dropdown->font, UI_IdV(0));
for (UI_ButtonRecord *node = dropdown->first;
node != 0;
node = node->next){
MemoryCopyStruct(&btn_ctx.cl, &node->cl);
btn_ctx.outline_t = node->outline_t;
btn_ctx.text_scale = node->text_scale;
UI_NextCondition(&btn_ctx, node->condition);
if (node->has_hot_key){
UI_NextHotkey(&btn_ctx, node->hot_key, node->hot_key_mods);
}
UI_NextTooltip(&btn_ctx, node->tool_tip);
if (node->kind == UI_ButtonKind_Icon){
if (UI_Button(&btn_ctx, node->major, node->minor)){
dropdown->activated = node;
}
}
else if (node->kind == UI_ButtonKind_Label){
if (UI_ButtonLabel(&btn_ctx, node->string)){
dropdown->activated = node;
}
}
}
result->rect = outer;
}