1003 lines
50 KiB
C
1003 lines
50 KiB
C
/*************************************************************************
|
|
*
|
|
* Orca
|
|
* Copyright 2023 Martin Fouilleul and the Orca project contributors
|
|
* See LICENSE.txt for licensing information
|
|
*
|
|
**************************************************************************/
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define _USE_MATH_DEFINES //NOTE: necessary for MSVC
|
|
#include <math.h>
|
|
|
|
#include "orca.h"
|
|
|
|
/*
|
|
void debug_print_indent(int indent)
|
|
{
|
|
for(int i = 0; i < indent; i++)
|
|
{
|
|
oc_log_info(" ");
|
|
}
|
|
}
|
|
|
|
void debug_print_rule(oc_ui_style_rule* rule)
|
|
{
|
|
oc_list_for(&rule->pattern.l, selector, oc_ui_selector, listElt)
|
|
{
|
|
switch(selector->kind)
|
|
{
|
|
case OC_UI_SEL_ANY:
|
|
oc_log_info("any: ");
|
|
break;
|
|
|
|
case OC_UI_SEL_OWNER:
|
|
oc_log_info("owner: ");
|
|
break;
|
|
|
|
case OC_UI_SEL_TEXT:
|
|
oc_log_info("text='%.*s': ", (int)selector->text.len, selector->text.ptr);
|
|
break;
|
|
|
|
case OC_UI_SEL_TAG:
|
|
oc_log_info("tag=0x%llx: ", selector->tag.hash);
|
|
break;
|
|
|
|
case OC_UI_SEL_STATUS:
|
|
{
|
|
if(selector->status & OC_UI_HOVER)
|
|
{
|
|
oc_log_info("hover: ");
|
|
}
|
|
if(selector->status & OC_UI_ACTIVE)
|
|
{
|
|
oc_log_info("active: ");
|
|
}
|
|
if(selector->status & OC_UI_DRAGGING)
|
|
{
|
|
oc_log_info("dragging: ");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OC_UI_SEL_KEY:
|
|
oc_log_info("key=0x%llx: ", selector->key.hash);
|
|
break;
|
|
|
|
default:
|
|
oc_log_info("unknown: ");
|
|
break;
|
|
}
|
|
}
|
|
oc_log_info("=> font size = %f\n", rule->style->fontSize);
|
|
}
|
|
|
|
void debug_print_size(oc_ui_box* box, oc_ui_axis axis, int indent)
|
|
{
|
|
debug_print_indent(indent);
|
|
oc_log_info("size %s: ", axis == OC_UI_AXIS_X ? "x" : "y");
|
|
f32 value = box->targetStyle->size.c[axis].value;
|
|
switch(box->targetStyle->size.c[axis].kind)
|
|
{
|
|
case OC_UI_SIZE_TEXT:
|
|
oc_log_info("text\n");
|
|
break;
|
|
|
|
case OC_UI_SIZE_CHILDREN:
|
|
oc_log_info("children\n");
|
|
break;
|
|
|
|
case OC_UI_SIZE_PARENT:
|
|
oc_log_info("parent: %f\n", value);
|
|
break;
|
|
|
|
case OC_UI_SIZE_PARENT_MINUS_PIXELS:
|
|
oc_log_info("parent minus pixels: %f\n", value);
|
|
break;
|
|
|
|
case OC_UI_SIZE_PIXELS:
|
|
oc_log_info("pixels: %f\n", value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void debug_print_styles(oc_ui_box* box, int indent)
|
|
{
|
|
debug_print_indent(indent);
|
|
oc_log_info("### box '%.*s'\n", (int)box->string.len, box->string.ptr);
|
|
indent++;
|
|
|
|
debug_print_indent(indent);
|
|
oc_log_info("font size: %f\n", box->targetStyle->fontSize);
|
|
|
|
debug_print_size(box, OC_UI_AXIS_X, indent);
|
|
debug_print_size(box, OC_UI_AXIS_Y, indent);
|
|
|
|
if(!oc_list_empty(&box->beforeRules))
|
|
{
|
|
debug_print_indent(indent);
|
|
oc_log_info("before rules:\n");
|
|
oc_list_for(&box->beforeRules, rule, oc_ui_style_rule, boxElt)
|
|
{
|
|
debug_print_indent(indent + 1);
|
|
debug_print_rule(rule);
|
|
}
|
|
}
|
|
|
|
if(!oc_list_empty(&box->afterRules))
|
|
{
|
|
debug_print_indent(indent);
|
|
oc_log_info("after rules:\n");
|
|
oc_list_for(&box->afterRules, rule, oc_ui_style_rule, boxElt)
|
|
{
|
|
debug_print_indent(indent + 1);
|
|
debug_print_rule(rule);
|
|
}
|
|
}
|
|
|
|
if(!oc_list_empty(&box->children))
|
|
{
|
|
debug_print_indent(indent);
|
|
oc_log_info("children:\n");
|
|
indent++;
|
|
oc_list_for(&box->children, child, oc_ui_box, listElt)
|
|
{
|
|
debug_print_styles(child, indent);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
oc_vec2 frameSize = { 1200, 838 };
|
|
|
|
oc_surface surface;
|
|
oc_canvas canvas;
|
|
oc_font fontRegular;
|
|
oc_font fontBold;
|
|
oc_ui_context ui;
|
|
oc_arena textArena = { 0 };
|
|
oc_arena logArena = { 0 };
|
|
oc_str8_list logLines;
|
|
|
|
typedef enum cmd
|
|
{
|
|
CMD_NONE,
|
|
CMD_SET_DARK_THEME,
|
|
CMD_SET_LIGHT_THEME
|
|
} cmd;
|
|
|
|
cmd command = CMD_NONE;
|
|
|
|
void log_push(const char* line)
|
|
{
|
|
oc_str8_list_push(&logArena, &logLines, (oc_str8)OC_STR8(line));
|
|
}
|
|
|
|
void log_pushf(const char* format, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, format);
|
|
oc_str8 str = oc_str8_pushfv(&logArena, format, args);
|
|
va_end(args);
|
|
oc_str8_list_push(&logArena, &logLines, str);
|
|
}
|
|
|
|
void column_begin(const char* header, f32 widthFraction)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, widthFraction },
|
|
.size.height = { OC_UI_SIZE_PARENT, 1 },
|
|
.layout.axis = OC_UI_AXIS_Y,
|
|
.layout.margin.y = 8,
|
|
.layout.spacing = 24,
|
|
.bgColor = ui.theme->bg1,
|
|
.borderColor = ui.theme->border,
|
|
.borderSize = 1,
|
|
.roundness = ui.theme->roundnessSmall },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_MARGIN_Y
|
|
| OC_UI_STYLE_LAYOUT_SPACING
|
|
| OC_UI_STYLE_BG_COLOR
|
|
| OC_UI_STYLE_BORDER_COLOR
|
|
| OC_UI_STYLE_BORDER_SIZE
|
|
| OC_UI_STYLE_ROUNDNESS);
|
|
oc_ui_box_begin(header, OC_UI_FLAG_DRAW_BACKGROUND | OC_UI_FLAG_DRAW_BORDER);
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.layout.align.x = OC_UI_ALIGN_CENTER },
|
|
OC_UI_STYLE_SIZE_WIDTH
|
|
| OC_UI_STYLE_LAYOUT_ALIGN_X);
|
|
oc_ui_container("header", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .fontSize = 18 },
|
|
OC_UI_STYLE_FONT_SIZE);
|
|
oc_ui_label(header);
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.size.height = { OC_UI_SIZE_PARENT, 1, 1 },
|
|
.layout.align.x = OC_UI_ALIGN_START,
|
|
.layout.margin.x = 16,
|
|
.layout.spacing = 24 },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_LAYOUT_ALIGN_X
|
|
| OC_UI_STYLE_LAYOUT_MARGIN_X
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_box_begin("contents", OC_UI_FLAG_NONE | OC_UI_FLAG_DRAW_BORDER);
|
|
}
|
|
|
|
void column_end()
|
|
{
|
|
oc_ui_box_end(); // contents
|
|
oc_ui_box_end(); // column
|
|
}
|
|
|
|
#define column(h, w) oc_defer_loop(column_begin(h, w), column_end())
|
|
|
|
void labeled_slider(const char* label, f32* value)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
|
|
.layout.spacing = 8 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container(label, OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_match_after(oc_ui_pattern_owner(),
|
|
&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 100 } },
|
|
OC_UI_STYLE_SIZE_WIDTH);
|
|
oc_ui_label(label);
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 100 } },
|
|
OC_UI_STYLE_SIZE_WIDTH);
|
|
oc_ui_slider("slider", value);
|
|
}
|
|
}
|
|
|
|
i32 ui_runloop(void* user)
|
|
{
|
|
canvas = oc_canvas_create();
|
|
oc_ui_init(&ui);
|
|
|
|
while(!oc_should_quit())
|
|
{
|
|
oc_arena_scope scratch = oc_scratch_begin();
|
|
|
|
oc_event* event = 0;
|
|
while((event = oc_next_event(scratch.arena)) != 0)
|
|
{
|
|
oc_ui_process_event(event);
|
|
|
|
switch(event->type)
|
|
{
|
|
case OC_EVENT_WINDOW_CLOSE:
|
|
{
|
|
oc_request_quit();
|
|
}
|
|
break;
|
|
|
|
case OC_EVENT_WINDOW_RESIZE:
|
|
{
|
|
frameSize = (oc_vec2){ event->move.content.w, event->move.content.h };
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch(command)
|
|
{
|
|
case CMD_SET_DARK_THEME:
|
|
oc_ui_set_theme(&OC_UI_DARK_THEME);
|
|
break;
|
|
case CMD_SET_LIGHT_THEME:
|
|
oc_ui_set_theme(&OC_UI_LIGHT_THEME);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
command = CMD_NONE;
|
|
|
|
oc_ui_style defaultStyle = { .font = fontRegular };
|
|
oc_ui_style_mask defaultMask = OC_UI_STYLE_FONT;
|
|
oc_ui_frame(frameSize, &defaultStyle, defaultMask)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1, .minSize = 1200 },
|
|
.size.height = { OC_UI_SIZE_PARENT, 1, .minSize = 838 } },
|
|
OC_UI_STYLE_SIZE);
|
|
|
|
oc_ui_container("top level", OC_UI_FLAG_NONE)
|
|
{
|
|
//--------------------------------------------------------------------------------------------
|
|
// Menu bar
|
|
//--------------------------------------------------------------------------------------------
|
|
oc_ui_menu_bar("menu_bar")
|
|
{
|
|
oc_ui_menu("File")
|
|
{
|
|
if(oc_ui_menu_button("Quit").pressed)
|
|
{
|
|
oc_request_quit();
|
|
}
|
|
}
|
|
|
|
oc_ui_menu("Theme")
|
|
{
|
|
if(oc_ui_menu_button("Dark theme").pressed)
|
|
{
|
|
command = CMD_SET_DARK_THEME;
|
|
}
|
|
if(oc_ui_menu_button("Light theme").pressed)
|
|
{
|
|
command = CMD_SET_LIGHT_THEME;
|
|
}
|
|
}
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.size.height = { OC_UI_SIZE_PARENT, 1, 1 },
|
|
.layout.axis = OC_UI_AXIS_X,
|
|
.layout.margin.x = 16,
|
|
.layout.margin.y = 16,
|
|
.layout.spacing = 16 },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_MARGINS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
|
|
oc_ui_container("background", OC_UI_FLAG_DRAW_BACKGROUND)
|
|
{
|
|
column("Widgets", 1.0 / 3)
|
|
{
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.layout.axis = OC_UI_AXIS_X,
|
|
.layout.spacing = 32 },
|
|
OC_UI_STYLE_SIZE_WIDTH
|
|
| OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("top", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_Y,
|
|
.layout.spacing = 24 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("top_left", OC_UI_FLAG_NONE)
|
|
{
|
|
//-----------------------------------------------------------------------------
|
|
// Label
|
|
//-----------------------------------------------------------------------------
|
|
oc_ui_label("Label");
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Button
|
|
//-----------------------------------------------------------------------------
|
|
if(oc_ui_button("Button").clicked)
|
|
{
|
|
log_push("Button clicked");
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
|
|
.layout.align.y = OC_UI_ALIGN_CENTER,
|
|
.layout.spacing = 8 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_ALIGN_Y
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("checkbox", OC_UI_FLAG_NONE)
|
|
{
|
|
//-------------------------------------------------------------------------
|
|
// Checkbox
|
|
//-------------------------------------------------------------------------
|
|
static bool checked = false;
|
|
if(oc_ui_checkbox("checkbox", &checked).clicked)
|
|
{
|
|
if(checked)
|
|
{
|
|
log_push("Checkbox checked");
|
|
}
|
|
else
|
|
{
|
|
log_push("Checkbox unchecked");
|
|
}
|
|
}
|
|
|
|
oc_ui_label("Checkbox");
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------
|
|
// Vertical slider
|
|
//---------------------------------------------------------------------------------
|
|
static float vSliderValue = 0;
|
|
static float vSliderLoggedValue = 0;
|
|
static f64 vSliderLogTime = 0;
|
|
oc_ui_style_next(&(oc_ui_style){ .size.height = { OC_UI_SIZE_PIXELS, 130 } },
|
|
OC_UI_STYLE_SIZE_HEIGHT);
|
|
oc_ui_slider("v_slider", &vSliderValue);
|
|
f64 now = oc_clock_time(OC_CLOCK_MONOTONIC);
|
|
if((now - vSliderLogTime) >= 0.2 && vSliderValue != vSliderLoggedValue)
|
|
{
|
|
log_pushf("Vertical slider moved to %f", vSliderValue);
|
|
vSliderLoggedValue = vSliderValue;
|
|
vSliderLogTime = now;
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_Y,
|
|
.layout.spacing = 24 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("top_right", OC_UI_FLAG_NONE)
|
|
{
|
|
//-----------------------------------------------------------------------------
|
|
// Tooltip
|
|
//-----------------------------------------------------------------------------
|
|
if(oc_ui_label("Tooltip").hovering)
|
|
{
|
|
oc_ui_tooltip("Hi");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Radio group
|
|
//-----------------------------------------------------------------------------
|
|
static int radioSelected = 0;
|
|
oc_str8 options[] = { OC_STR8("Radio 1"),
|
|
OC_STR8("Radio 2") };
|
|
oc_ui_radio_group_info radioGroupInfo = { .selectedIndex = radioSelected,
|
|
.optionCount = 2,
|
|
.options = options };
|
|
oc_ui_radio_group_info result = oc_ui_radio_group("radio_group", &radioGroupInfo);
|
|
radioSelected = result.selectedIndex;
|
|
if(result.changed)
|
|
{
|
|
log_pushf("Selected Radio %i", result.selectedIndex + 1);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Horizontal slider
|
|
//-----------------------------------------------------------------------------
|
|
static float hSliderValue = 0;
|
|
static float hSliderLoggedValue = 0;
|
|
static f64 hSliderLogTime = 0;
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 130 } },
|
|
OC_UI_STYLE_SIZE_WIDTH);
|
|
oc_ui_slider("h_slider", &hSliderValue);
|
|
f64 now = oc_clock_time(OC_CLOCK_MONOTONIC);
|
|
if((now - hSliderLogTime) >= 0.2 && hSliderValue != hSliderLoggedValue)
|
|
{
|
|
log_pushf("Slider moved to %f", hSliderValue);
|
|
hSliderLoggedValue = hSliderValue;
|
|
hSliderLogTime = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Text box
|
|
//-------------------------------------------------------------------------------------
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 305 },
|
|
.size.height = { OC_UI_SIZE_TEXT } },
|
|
OC_UI_STYLE_SIZE);
|
|
static oc_str8 text = OC_STR8_LIT("Text box");
|
|
oc_ui_text_box_result res = oc_ui_text_box("text", scratch.arena, text);
|
|
if(res.changed)
|
|
{
|
|
oc_arena_clear(&textArena);
|
|
text = oc_str8_push_copy(&textArena, res.text);
|
|
}
|
|
if(res.accepted)
|
|
{
|
|
log_pushf("Entered text \"%s\"", text.ptr);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Select
|
|
//-------------------------------------------------------------------------------------
|
|
static int selected = -1;
|
|
oc_str8 options[] = { OC_STR8("Option 1"),
|
|
OC_STR8("Option 2") };
|
|
oc_ui_select_popup_info info = { .selectedIndex = selected,
|
|
.optionCount = 2,
|
|
.options = options,
|
|
.placeholder = OC_STR8_LIT("Select") };
|
|
oc_ui_select_popup_info result = oc_ui_select_popup("select", &info);
|
|
if(result.selectedIndex != selected)
|
|
{
|
|
log_pushf("Selected %s", options[result.selectedIndex].ptr);
|
|
}
|
|
selected = result.selectedIndex;
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Scrollable panel
|
|
//-------------------------------------------------------------------------------------
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.size.height = { OC_UI_SIZE_PARENT, 1, 1 },
|
|
.layout.margin.x = 16,
|
|
.layout.margin.y = 16,
|
|
.layout.spacing = 8,
|
|
.bgColor = ui.theme->bg2,
|
|
.borderColor = ui.theme->border,
|
|
.borderSize = 1,
|
|
.roundness = ui.theme->roundnessSmall },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_LAYOUT_MARGINS
|
|
| OC_UI_STYLE_LAYOUT_SPACING
|
|
| OC_UI_STYLE_BG_COLOR
|
|
| OC_UI_STYLE_BORDER_COLOR
|
|
| OC_UI_STYLE_BORDER_SIZE
|
|
| OC_UI_STYLE_ROUNDNESS);
|
|
oc_ui_panel("log", OC_UI_FLAG_DRAW_BACKGROUND | OC_UI_FLAG_DRAW_BORDER)
|
|
{
|
|
if(oc_list_empty(logLines.list))
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .color = ui.theme->text2 },
|
|
OC_UI_STYLE_COLOR);
|
|
oc_ui_label("Log");
|
|
}
|
|
|
|
i32 i = 0;
|
|
oc_list_for(logLines.list, logLine, oc_str8_elt, listElt)
|
|
{
|
|
char id[15];
|
|
snprintf(id, sizeof(id), "%d", i);
|
|
oc_ui_container(id, OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_label_str8(logLine->string);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------
|
|
// Styling
|
|
//-----------------------------------------------------------------------------------------
|
|
// Initial values here are hardcoded from the dark theme and everything is overridden all
|
|
// the time. In a real program you'd only override what you need and supply the values from
|
|
// ui.theme or ui.theme->palette.
|
|
//
|
|
// Rule-based styling is described at
|
|
// https://www.forkingpaths.dev/posts/23-03-10/rule_based_styling_imgui.html
|
|
column("Styling", 2.0 / 3)
|
|
{
|
|
static f32 unselectedWidth = 16;
|
|
static f32 unselectedHeight = 16;
|
|
static f32 unselectedRoundness = 8;
|
|
static f32 unselectedBgR = 0.086;
|
|
static f32 unselectedBgG = 0.086;
|
|
static f32 unselectedBgB = 0.102;
|
|
static f32 unselectedBgA = 1;
|
|
static f32 unselectedBorderR = 0.976;
|
|
static f32 unselectedBorderG = 0.976;
|
|
static f32 unselectedBorderB = 0.976;
|
|
static f32 unselectedBorderA = 0.35;
|
|
static f32 unselectedBorderSize = 1;
|
|
static oc_ui_status unselectedWhenStatus = OC_UI_NONE;
|
|
|
|
static f32 selectedWidth = 16;
|
|
static f32 selectedHeight = 16;
|
|
static f32 selectedRoundness = 8;
|
|
static f32 selectedR = 1;
|
|
static f32 selectedG = 1;
|
|
static f32 selectedB = 1;
|
|
static f32 selectedA = 1;
|
|
static f32 selectedBgR = 0.33;
|
|
static f32 selectedBgG = 0.66;
|
|
static f32 selectedBgB = 1;
|
|
static f32 selectedBgA = 1;
|
|
static oc_ui_status selectedWhenStatus = OC_UI_NONE;
|
|
|
|
static oc_color labelFontColor = { 0.976, 0.976, 0.976, 1 };
|
|
static oc_font* labelFont = &fontRegular;
|
|
static f32 labelFontSize = 14;
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .size.width = { OC_UI_SIZE_PARENT, 1 },
|
|
.size.height = { OC_UI_SIZE_PIXELS, 152 },
|
|
.layout.margin.x = 320,
|
|
.layout.margin.y = 16,
|
|
.bgColor = OC_UI_DARK_THEME.bg0,
|
|
.roundness = OC_UI_DARK_THEME.roundnessSmall },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_LAYOUT_MARGINS
|
|
| OC_UI_STYLE_BG_COLOR
|
|
| OC_UI_STYLE_ROUNDNESS);
|
|
oc_ui_container("styled_radios", OC_UI_FLAG_DRAW_BACKGROUND | OC_UI_FLAG_DRAW_BORDER)
|
|
{
|
|
oc_ui_pattern unselectedPattern = { 0 };
|
|
oc_ui_pattern_push(scratch.arena,
|
|
&unselectedPattern,
|
|
(oc_ui_selector){ .kind = OC_UI_SEL_TAG,
|
|
.tag = oc_ui_tag_make("radio") });
|
|
oc_ui_pattern_push(scratch.arena,
|
|
&unselectedPattern,
|
|
(oc_ui_selector){ .op = OC_UI_SEL_AND,
|
|
.kind = OC_UI_SEL_STATUS,
|
|
.status = unselectedWhenStatus });
|
|
oc_ui_style_match_after(unselectedPattern,
|
|
&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, unselectedWidth },
|
|
.size.height = { OC_UI_SIZE_PIXELS, unselectedHeight },
|
|
.bgColor = { unselectedBgR, unselectedBgG, unselectedBgB, unselectedBgA },
|
|
.borderColor = { unselectedBorderR, unselectedBorderG, unselectedBorderB, unselectedBorderA },
|
|
.borderSize = unselectedBorderSize,
|
|
.roundness = unselectedRoundness },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_BG_COLOR
|
|
| OC_UI_STYLE_BORDER_COLOR
|
|
| OC_UI_STYLE_BORDER_SIZE
|
|
| OC_UI_STYLE_ROUNDNESS);
|
|
|
|
oc_ui_pattern selectedPattern = { 0 };
|
|
oc_ui_pattern_push(scratch.arena,
|
|
&selectedPattern,
|
|
(oc_ui_selector){ .kind = OC_UI_SEL_TAG,
|
|
.tag = oc_ui_tag_make("radio_selected") });
|
|
oc_ui_pattern_push(scratch.arena,
|
|
&selectedPattern,
|
|
(oc_ui_selector){ .op = OC_UI_SEL_AND,
|
|
.kind = OC_UI_SEL_STATUS,
|
|
.status = selectedWhenStatus });
|
|
oc_ui_style_match_after(selectedPattern,
|
|
&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, selectedWidth },
|
|
.size.height = { OC_UI_SIZE_PIXELS, selectedHeight },
|
|
.color = { selectedR, selectedG, selectedB, selectedA },
|
|
.bgColor = { selectedBgR, selectedBgG, selectedBgB, selectedBgA },
|
|
.roundness = selectedRoundness },
|
|
OC_UI_STYLE_SIZE
|
|
| OC_UI_STYLE_COLOR
|
|
| OC_UI_STYLE_BG_COLOR
|
|
| OC_UI_STYLE_ROUNDNESS);
|
|
|
|
oc_ui_pattern labelPattern = { 0 };
|
|
oc_ui_tag labelTag = oc_ui_tag_make("label");
|
|
oc_ui_pattern_push(scratch.arena, &labelPattern, (oc_ui_selector){ .kind = OC_UI_SEL_TAG, .tag = labelTag });
|
|
oc_ui_style_match_after(labelPattern,
|
|
&(oc_ui_style){ .color = labelFontColor,
|
|
.font = *labelFont,
|
|
.fontSize = labelFontSize },
|
|
OC_UI_STYLE_COLOR
|
|
| OC_UI_STYLE_FONT
|
|
| OC_UI_STYLE_FONT_SIZE);
|
|
|
|
static int selectedIndex = 0;
|
|
oc_str8 options[] = { OC_STR8("I"),
|
|
OC_STR8("Am"),
|
|
OC_STR8("Stylish") };
|
|
oc_ui_radio_group_info radioGroupInfo = { .selectedIndex = selectedIndex,
|
|
.optionCount = oc_array_size(options),
|
|
.options = options };
|
|
oc_ui_radio_group_info result = oc_ui_radio_group("radio_group", &radioGroupInfo);
|
|
selectedIndex = result.selectedIndex;
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
|
|
.layout.spacing = 32 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("controls", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_Y,
|
|
.layout.spacing = 16 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("unselected", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .fontSize = 16 },
|
|
OC_UI_STYLE_FONT_SIZE);
|
|
oc_ui_label("Radio style");
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("size", OC_UI_FLAG_NONE)
|
|
{
|
|
f32 widthSlider = (unselectedWidth - 8) / 16;
|
|
labeled_slider("Width", &widthSlider);
|
|
unselectedWidth = 8 + widthSlider * 16;
|
|
|
|
f32 heightSlider = (unselectedHeight - 8) / 16;
|
|
labeled_slider("Height", &heightSlider);
|
|
unselectedHeight = 8 + heightSlider * 16;
|
|
|
|
f32 roundnessSlider = (unselectedRoundness - 4) / 8;
|
|
labeled_slider("Roundness", &roundnessSlider);
|
|
unselectedRoundness = 4 + roundnessSlider * 8;
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("background", OC_UI_FLAG_NONE)
|
|
{
|
|
labeled_slider("Background R", &unselectedBgR);
|
|
labeled_slider("Background G", &unselectedBgG);
|
|
labeled_slider("Background B", &unselectedBgB);
|
|
labeled_slider("Background A", &unselectedBgA);
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("border", OC_UI_FLAG_NONE)
|
|
{
|
|
labeled_slider("Border R", &unselectedBorderR);
|
|
labeled_slider("Border G", &unselectedBorderG);
|
|
labeled_slider("Border B", &unselectedBorderB);
|
|
labeled_slider("Border A", &unselectedBorderA);
|
|
}
|
|
|
|
f32 borderSizeSlider = unselectedBorderSize / 5;
|
|
labeled_slider("Border size", &borderSizeSlider);
|
|
unselectedBorderSize = borderSizeSlider * 5;
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 10 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("status_override", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_label("Override");
|
|
|
|
static int statusIndex = 0;
|
|
oc_str8 statusOptions[] = { OC_STR8("Always"),
|
|
OC_STR8("When hovering"),
|
|
OC_STR8("When dragging") };
|
|
oc_ui_radio_group_info statusInfo = { .selectedIndex = statusIndex,
|
|
.optionCount = oc_array_size(statusOptions),
|
|
.options = statusOptions };
|
|
oc_ui_radio_group_info result = oc_ui_radio_group("status", &statusInfo);
|
|
statusIndex = result.selectedIndex;
|
|
switch(statusIndex)
|
|
{
|
|
case 0:
|
|
unselectedWhenStatus = OC_UI_NONE;
|
|
break;
|
|
case 1:
|
|
unselectedWhenStatus = OC_UI_HOVER;
|
|
break;
|
|
case 2:
|
|
unselectedWhenStatus = OC_UI_DRAGGING;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_Y,
|
|
.layout.spacing = 16 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("selected", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .fontSize = 16 },
|
|
OC_UI_STYLE_FONT_SIZE);
|
|
oc_ui_label("Radio selected style");
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("size", OC_UI_FLAG_NONE)
|
|
{
|
|
f32 widthSlider = (selectedWidth - 8) / 16;
|
|
labeled_slider("Width", &widthSlider);
|
|
selectedWidth = 8 + widthSlider * 16;
|
|
|
|
f32 heightSlider = (selectedHeight - 8) / 16;
|
|
labeled_slider("Height", &heightSlider);
|
|
selectedHeight = 8 + heightSlider * 16;
|
|
|
|
f32 roundnessSlider = (selectedRoundness - 4) / 8;
|
|
labeled_slider("Roundness", &roundnessSlider);
|
|
selectedRoundness = 4 + roundnessSlider * 8;
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("color", OC_UI_FLAG_NONE)
|
|
{
|
|
labeled_slider("Center R", &selectedR);
|
|
labeled_slider("Center G", &selectedG);
|
|
labeled_slider("Center B", &selectedB);
|
|
labeled_slider("Center A", &selectedA);
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 4 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("background", OC_UI_FLAG_NONE)
|
|
{
|
|
labeled_slider("Background R", &selectedBgR);
|
|
labeled_slider("Background G", &selectedBgG);
|
|
labeled_slider("Background B", &selectedBgB);
|
|
labeled_slider("Background A", &selectedBgA);
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.spacing = 10 },
|
|
OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("status_override", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .size.height = { OC_UI_SIZE_PIXELS, 30 } },
|
|
OC_UI_STYLE_SIZE_HEIGHT);
|
|
oc_ui_box_make("spacer", OC_UI_FLAG_NONE);
|
|
|
|
oc_ui_label("Override");
|
|
|
|
static int statusIndex = 0;
|
|
oc_str8 statusOptions[] = { OC_STR8("Always"),
|
|
OC_STR8("When hovering"),
|
|
OC_STR8("When dragging") };
|
|
oc_ui_radio_group_info statusInfo = { .selectedIndex = statusIndex,
|
|
.optionCount = oc_array_size(statusOptions),
|
|
.options = statusOptions };
|
|
oc_ui_radio_group_info result = oc_ui_radio_group("status", &statusInfo);
|
|
statusIndex = result.selectedIndex;
|
|
switch(statusIndex)
|
|
{
|
|
case 0:
|
|
selectedWhenStatus = OC_UI_NONE;
|
|
break;
|
|
case 1:
|
|
selectedWhenStatus = OC_UI_HOVER;
|
|
break;
|
|
case 2:
|
|
selectedWhenStatus = OC_UI_DRAGGING;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_Y,
|
|
.layout.spacing = 16 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("label", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_next(&(oc_ui_style){ .fontSize = 16 },
|
|
OC_UI_STYLE_FONT_SIZE);
|
|
oc_ui_label("Label style");
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
|
|
.layout.spacing = 8 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("font_color", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_match_after(oc_ui_pattern_owner(),
|
|
&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 100 } },
|
|
OC_UI_STYLE_SIZE_WIDTH);
|
|
oc_ui_label("Font color");
|
|
|
|
static int colorSelected = 0;
|
|
oc_str8 colorNames[] = { OC_STR8("Default"),
|
|
OC_STR8("Red"),
|
|
OC_STR8("Orange"),
|
|
OC_STR8("Amber"),
|
|
OC_STR8("Yellow"),
|
|
OC_STR8("Lime"),
|
|
OC_STR8("Light Green"),
|
|
OC_STR8("Green") };
|
|
oc_color colors[] = { OC_UI_DARK_THEME.text0,
|
|
OC_UI_DARK_THEME.palette->red5,
|
|
OC_UI_DARK_THEME.palette->orange5,
|
|
OC_UI_DARK_THEME.palette->amber5,
|
|
OC_UI_DARK_THEME.palette->yellow5,
|
|
OC_UI_DARK_THEME.palette->lime5,
|
|
OC_UI_DARK_THEME.palette->lightGreen5,
|
|
OC_UI_DARK_THEME.palette->green5 };
|
|
oc_ui_select_popup_info colorInfo = { .selectedIndex = colorSelected,
|
|
.optionCount = oc_array_size(colorNames),
|
|
.options = colorNames };
|
|
oc_ui_select_popup_info colorResult = oc_ui_select_popup("color", &colorInfo);
|
|
colorSelected = colorResult.selectedIndex;
|
|
labelFontColor = colors[colorSelected];
|
|
}
|
|
|
|
oc_ui_style_next(&(oc_ui_style){ .layout.axis = OC_UI_AXIS_X,
|
|
.layout.spacing = 8 },
|
|
OC_UI_STYLE_LAYOUT_AXIS
|
|
| OC_UI_STYLE_LAYOUT_SPACING);
|
|
oc_ui_container("font", OC_UI_FLAG_NONE)
|
|
{
|
|
oc_ui_style_match_after(oc_ui_pattern_owner(),
|
|
&(oc_ui_style){ .size.width = { OC_UI_SIZE_PIXELS, 100 } },
|
|
OC_UI_STYLE_SIZE_WIDTH);
|
|
oc_ui_label("Font");
|
|
|
|
static int fontSelected = 0;
|
|
oc_str8 fontNames[] = { OC_STR8("Regular"),
|
|
OC_STR8("Bold") };
|
|
oc_font* fonts[] = { &fontRegular,
|
|
&fontBold };
|
|
oc_ui_select_popup_info fontInfo = { .selectedIndex = fontSelected,
|
|
.optionCount = oc_array_size(fontNames),
|
|
.options = fontNames };
|
|
oc_ui_select_popup_info fontResult = oc_ui_select_popup("font_style", &fontInfo);
|
|
fontSelected = fontResult.selectedIndex;
|
|
labelFont = fonts[fontSelected];
|
|
}
|
|
|
|
f32 fontSizeSlider = (labelFontSize - 8) / 16;
|
|
labeled_slider("Font size", &fontSizeSlider);
|
|
labelFontSize = 8 + fontSizeSlider * 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
oc_canvas_select(canvas);
|
|
oc_surface_select(surface);
|
|
oc_ui_draw();
|
|
oc_render(canvas);
|
|
oc_surface_present(surface);
|
|
|
|
oc_scratch_end(scratch);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
oc_init();
|
|
oc_clock_init(); //TODO put that in oc_init()?
|
|
|
|
oc_rect windowRect = { .x = 100, .y = 100, .w = frameSize.x, .h = frameSize.y };
|
|
oc_window window = oc_window_create(windowRect, OC_STR8("test"), 0);
|
|
|
|
oc_rect contentRect = oc_window_get_content_rect(window);
|
|
|
|
oc_window_set_title(window, OC_STR8("Orca UI Demo"));
|
|
|
|
surface = oc_surface_create_for_window(window, OC_CANVAS);
|
|
oc_surface_deselect();
|
|
|
|
oc_arena_scope scratch = oc_scratch_begin();
|
|
|
|
oc_font* fonts[2] = { &fontRegular, &fontBold };
|
|
oc_str8 fontNames[2] = {
|
|
oc_path_executable_relative(scratch.arena, OC_STR8("../OpenSans-Regular.ttf")),
|
|
oc_path_executable_relative(scratch.arena, OC_STR8("../OpenSans-Bold.ttf"))
|
|
};
|
|
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
oc_file file = oc_file_open(fontNames[i], OC_FILE_ACCESS_READ, 0);
|
|
if(oc_file_last_error(file) != OC_IO_OK)
|
|
{
|
|
oc_log_error("Couldn't open file %.*s\n", oc_str8_ip(fontNames[i]));
|
|
}
|
|
u64 size = oc_file_size(file);
|
|
char* buffer = (char*)oc_arena_push(scratch.arena, size);
|
|
oc_file_read(file, size, buffer);
|
|
oc_file_close(file);
|
|
oc_unicode_range ranges[5] = { OC_UNICODE_BASIC_LATIN,
|
|
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
|
OC_UNICODE_LATIN_EXTENDED_A,
|
|
OC_UNICODE_LATIN_EXTENDED_B,
|
|
OC_UNICODE_SPECIALS };
|
|
|
|
*fonts[i] = oc_font_create_from_memory(oc_str8_from_buffer(size, buffer), 5, ranges);
|
|
}
|
|
oc_scratch_end(scratch);
|
|
|
|
oc_arena_init(&textArena);
|
|
oc_arena_init(&logArena);
|
|
oc_list_init(&logLines.list);
|
|
|
|
// start app
|
|
oc_window_bring_to_front(window);
|
|
oc_window_focus(window);
|
|
|
|
oc_thread* runloopThread = oc_thread_create(ui_runloop, 0);
|
|
|
|
while(!oc_should_quit())
|
|
{
|
|
oc_pump_events(-1);
|
|
//TODO: what to do with mem scratch here?
|
|
}
|
|
|
|
oc_surface_destroy(surface);
|
|
oc_terminate();
|
|
|
|
return (0);
|
|
}
|