//////////////////////////////////////////////////////////// //~ Theme helpers PP_WidgetTheme PP_GetWidgetTheme(void) { PP_WidgetTheme theme = ZI; theme.font = ResourceKeyFromStore(&PP_Resources, Lit("font/fixedsys.ttf")); theme.font_size = 16; theme.window_background_color = Rgb32(0xff1a1d1e); theme.window_border_color = Rgb32(0xff343a3b); theme.window_border = 1; theme.window_width = 500; theme.window_padding = theme.window_border - 1; theme.divider_color = theme.window_border_color; theme.window_title_font_size = 16; theme.text_padding_x = 3; theme.text_padding_y = 3; return theme; } void PP_PushWidgetThemeStyles(PP_WidgetTheme theme) { UI_Push(Font, theme.font); UI_Push(FontSize, theme.font_size); } //////////////////////////////////////////////////////////// //~ Hotkey helpers String PP_StringFromHotkey(Arena *arena, PP_Hotkey hotkey) { TempArena scratch = BeginScratch(arena); StringList parts = ZI; if (hotkey.ctrl) { PushStringToList(scratch.arena, &parts, Lit("Ctrl")); } if (hotkey.alt) { PushStringToList(scratch.arena, &parts, Lit("Alt")); } if (hotkey.shift) { PushStringToList(scratch.arena, &parts, Lit("Shift")); } PushStringToList(scratch.arena, &parts, StringFromButton(hotkey.button)); return StringFromList(arena, parts, Lit(" + ")); } //////////////////////////////////////////////////////////// //~ Commands widget void PP_BeginCommandsWidget(PP_CommandsWidget *widget) { ZeroStruct(&widget->build); widget->build.cp = UI_PushCP(UI_NilKey); UI_Push(Tag, HashF("commands widget")); } PP_CommandsWidgetItemReport PP_PushCommandsWidgetItem(PP_CommandsWidget *widget, PP_CommandsWidgetItemDesc desc) { Arena *frame_arena = UI_FrameArena(); UI_Key key = UI_KeyF("btn%F", FmtSint(widget->build.num_items)); { PP_CommandsWidgetItem *item = PushStruct(frame_arena, PP_CommandsWidgetItem); item->key = key; item->desc = desc; QueuePush(widget->build.first_item, widget->build.last_item, item); ++widget->build.num_items; } PP_CommandsWidgetItemReport result = ZI; UI_Report rep = UI_ReportFromKey(key); result.ui_report = rep; result.pressed = rep.m1_presses > 0; CopyStructs(result.new_hotkeys, desc.hotkeys, MinU32(countof(result.new_hotkeys), countof(desc.hotkeys))); return result; } void PP_EndCommandsWidget(PP_CommandsWidget *widget) { PP_WidgetTheme theme = PP_GetWidgetTheme(); Vec2 cursor_pos = UI_CursorPos(); UI_Push(Tag, HashF("commands widget")); UI_Key titlebar_key = UI_KeyF("title bar"); Vec4 window_background_color = theme.window_background_color; Vec4 window_border_color = theme.window_border_color; Vec4 titlebar_color = ZI; Vec4 titlebar_border_color = ZI; Vec4 divider_color = theme.divider_color; { UI_Report rep = UI_ReportFromKey(titlebar_key); if (rep.m1_held) { widget->pos = SubVec2(cursor_pos, rep.last_m1_offset); } window_border_color = BlendSrgb(window_border_color, Rgb(0.5, 0.5, 0.5), rep.hot); } UI_Push(BackgroundColor, window_background_color); UI_Push(BorderColor, window_border_color); UI_Push(Border, theme.window_border); // UI_Push(Rounding, UI_RPIX(15)); UI_Push(Rounding, UI_RPIX(15)); UI_Push(Width, UI_PIX(theme.window_width, 0)); UI_Push(Height, UI_SHRINK(0, 0)); UI_Push(ChildLayoutAxis, Axis_Y); UI_Push(FloatingPos, widget->pos); UI_SetNext(Flags, UI_BoxFlag_Floating); UI_PushCP(UI_BuildBoxEx(UI_KeyF("titlebar"))); { /* Title bar */ UI_PushCP(UI_NilKey); { UI_Push(BackgroundColor, titlebar_color); UI_Push(BorderColor, titlebar_border_color); UI_Push(Rounding, UI_RPIX(0)); UI_Push(ChildLayoutAxis, Axis_X); UI_Push(Width, UI_GROW(1, 0)); UI_Push(Height, UI_FNT(2, 1)); UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_Interactable); UI_PushCP(UI_BuildBoxEx(titlebar_key)); { UI_Push(Width, UI_GROW(1, 0)); UI_Push(BorderColor, 0); /* Left title box */ UI_BuildRow(); /* Title box */ UI_SetNext(FontSize, theme.window_title_font_size); UI_SetNext(ChildAlignment, UI_Alignment_Center); UI_SetNext(Width, UI_SHRINK(0, 1)); UI_SetNext(Text, Lit("Commands")); UI_SetNext(Flags, UI_BoxFlag_DrawText); UI_BuildBox(); /* Right title box */ UI_BuildRow(); } UI_PopCP(UI_TopCP()); } UI_PopCP(UI_TopCP()); } f32 padding = theme.window_border; UI_SetNext(Tint, 0); UI_SetNext(Rounding, 0); UI_PushCP(UI_BuildRow()); { UI_BuildSpacer(UI_PIX(padding, 1), Axis_X); { UI_SetNext(Tint, 0); UI_SetNext(Rounding, 0); UI_SetNext(Width, UI_GROW(1, 0)); UI_PushCP(UI_BuildColumn()); { for (PP_CommandsWidgetItem *item = widget->build.first_item; item; item = item->next) { UI_BuildDivider(UI_PIX(1, 1), theme.divider_color, Axis_Y); UI_Key btn_key = item->key; UI_Report btn_rep = UI_ReportFromKey(btn_key); Vec4 btn_color = theme.window_background_color; Vec4 btn_border_color = ZI; { Vec4 hovered_color = Rgb32(0x103c4c); Vec4 pressed_color = hovered_color; pressed_color.w = 0.2; f32 btn_hot = btn_rep.hot; f32 btn_active = btn_rep.active; f32 btn_hovered = btn_rep.hovered; btn_color = BlendSrgb(btn_color, hovered_color, btn_hot); btn_color = BlendSrgb(btn_color, pressed_color, btn_active * btn_hovered); btn_border_color = BlendSrgb(btn_border_color, Rgb32(0x0078a6), btn_hot); } UI_SetNext(Rounding, 0); UI_SetNext(Tint, 0); UI_PushCP(UI_BuildRow()); { UI_SetNext(BorderColor, btn_border_color); UI_SetNext(BackgroundColor, btn_color); UI_SetNext(Rounding, UI_RPIX(5)); UI_SetNext(Width, UI_GROW(1, 0)); UI_SetNext(Height, UI_FNT(1.5, 1)); UI_SetNext(ChildAlignment, UI_Alignment_Left); UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_Interactable); UI_PushCP(UI_BuildRowEx(btn_key)); { UI_Push(Tag, btn_key.hash); /* Begin spacer */ UI_BuildSpacer(UI_PIX(20, 1), Axis_X); /* Command label */ UI_SetNext(ChildAlignment, UI_Alignment_Center); UI_BuildLabel(item->desc.display_name); /* Middle spacer */ UI_BuildSpacer(UI_GROW(1, 0), Axis_X); /* Command hotkey buttons */ for (u64 i = 0; i < countof(item->desc.hotkeys); ++i) { UI_Key hotkey_key = UI_KeyF("hotkey%F", FmtUint(i)); UI_Report hotkey_rep = UI_ReportFromKey(hotkey_key); Vec4 hotkey_color = ZI; Vec4 hotkey_border_color = ZI; { Vec4 hovered_color = Rgb32(0x103c4c); Vec4 pressed_color = hovered_color; pressed_color.w = 0.2; f32 hotkey_hot = hotkey_rep.hot; f32 hotkey_active = hotkey_rep.active; f32 hotkey_hovered = hotkey_rep.hovered; hotkey_color = BlendSrgb(hotkey_color, hovered_color, hotkey_hot); hotkey_color = BlendSrgb(hotkey_color, pressed_color, hotkey_active * hotkey_hovered); hotkey_border_color = BlendSrgb(hotkey_border_color, Rgb32(0x0078a6), hotkey_hot); } PP_Hotkey hotkey = item->desc.hotkeys[i]; if (hotkey.button == Button_None) { break; } else { UI_BuildSpacer(UI_PIX(10, 1), Axis_X); String hotkey_name = PP_StringFromHotkey(UI_FrameArena(), hotkey); UI_SetNext(BackgroundColor, hotkey_color); UI_SetNext(BorderColor, hotkey_border_color); UI_SetNext(Text, hotkey_name); UI_SetNext(Width, UI_SHRINK(theme.text_padding_x, 1)); UI_SetNext(Height, UI_GROW(1, 0)); UI_SetNext(Rounding, UI_RPIX(5)); UI_SetNext(Border, 1); UI_SetNext(ChildAlignment, UI_Alignment_Center); UI_SetNext(Flags, UI_BoxFlag_DrawText | UI_BoxFlag_Interactable); UI_PushCP(UI_BuildRowEx(hotkey_key)); { } UI_PopCP(UI_TopCP()); } } /* End spacer */ UI_BuildSpacer(UI_PIX(20, 1), Axis_X); } UI_PopCP(UI_TopCP()); } UI_PopCP(UI_TopCP()); } } UI_PopCP(UI_TopCP()); } UI_BuildSpacer(UI_PIX(padding, 1), Axis_X); } UI_PopCP(UI_TopCP()); UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y); UI_PopCP(widget->build.cp); } //////////////////////////////////////////////////////////// //~ Console widget UI_Key PP_BuildConsoleWidget(b32 minimized) { /* TODO: Remove this whole thing */ __prof; TempArena scratch = BeginScratchNoConflict(); // i32 console_level = minimized ? LogLevel_Success : LogLevel_Debug; i32 console_level = LogLevel_Debug; Vec4 colors[LogLevel_Count][2] = ZI; SetBytes(colors, 0xFF, sizeof(colors)); /* Debug colors */ colors[LogLevel_Debug][0] = Rgb(0.4, 0.1, 0.4); colors[LogLevel_Debug][1] = Rgb(0.5, 0.2, 0.5); /* Info colors */ colors[LogLevel_Info][0] = Rgb(0.4, 0.4, 0.4); colors[LogLevel_Info][1] = Rgb(0.5, 0.5, 0.5); /* Success colors */ colors[LogLevel_Success][0] = Rgb(0.1, 0.3, 0.1); colors[LogLevel_Success][1] = Rgb(0.2, 0.4, 0.2); /* Warning colors */ colors[LogLevel_Warning][0] = Rgb(0.4, 0.4, 0.1); colors[LogLevel_Warning][1] = Rgb(0.5, 0.5, 0.2); /* Error colors */ colors[LogLevel_Error][0] = Rgb(0.4, 0.1, 0.1); colors[LogLevel_Error][1] = Rgb(0.5, 0.2, 0.2); i64 max_time_ns = I64Max; i64 fade_time_ns = max_time_ns; if (minimized) { max_time_ns = NsFromSeconds(10); fade_time_ns = max_time_ns; } f32 fade_curve = 0.5; i64 now_ns = TimeNs(); UI_Key console_box = ZI; { UI_SetNext(Border, 0); if (minimized) { UI_SetNext(BackgroundColor, 0); UI_SetNext(Width, UI_PIX(500, 0)); UI_SetNext(Height, UI_SHRINK(0, 1)); } else { UI_SetNext(BackgroundColor, Rgba(1, 1, 1, 0.02)); UI_SetNext(Width, UI_GROW(1, 0)); UI_SetNext(Height, UI_SHRINK(0, 1)); } console_box = UI_BuildColumnEx(UI_KeyF("Console box")); UI_PushCP(console_box); { /* Gather display logs */ u64 max = 20; u64 display_count = 0; LogEvent *display_logs = PushStructs(scratch.arena, LogEvent, max); { b32 done = 0; if (minimized) { max = 5; } LogEventsArray logs = GetLogEvents(); for (u64 i = logs.count; i-- > 0 && display_count < max && !done;) { LogEvent ev = logs.logs[i]; if (ev.time_ns > (now_ns - max_time_ns)) { if (ev.level <= console_level) { display_logs[display_count] = ev; ++display_count; } } else { done = 1; } } } /* Display logs in reverse */ for (u64 i = display_count; i-- > 0;) { LogEvent log = display_logs[i]; f32 opacity = 0.75; f32 lin = 1.0 - ClampF64((f64)(now_ns - log.time_ns) / (f64)fade_time_ns, 0, 1); opacity *= PowF32(lin, fade_curve); String text = log.msg; if (!minimized) { DateTime datetime = log.datetime; text = StringF( scratch.arena, "[%F:%F:%F.%F] %F", FmtUintZ(datetime.hour, 2), FmtUintZ(datetime.minute, 2), FmtUintZ(datetime.second, 2), FmtUintZ(datetime.milliseconds, 3), FmtString(text)); } UI_PushCP(UI_NilKey); { Vec4 tint = VEC4(1, 1, 1, opacity); UI_Push(Tint, tint); { Vec4 color = colors[log.level][log.level_id % 2]; UI_Push(BackgroundColor, color); UI_Push(Width, UI_GROW(1, 0)); UI_Push(Height, UI_FNT(1.5, 1)); UI_Push(BorderColor, Rgb(0.25, 0.25, 0.25)); UI_Push(Rounding, UI_RPIX(0)); UI_Push(Border, 1); UI_Push(ChildAlignment, UI_Alignment_Left); UI_PushCP(UI_BuildRow()); { // UI_SetNext(Height, UI_PIX(100, 0)); UI_BuildSpacer(UI_PIX(10, 0), Axis_X); UI_Push(BackgroundColor, 0); UI_Push(Border, 0); UI_Push(Text, text); UI_Push(Width, UI_GROW(1, 0)); UI_Push(Height, UI_SHRINK(0, 1)); UI_Push(Flags, UI_BoxFlag_DrawText); UI_BuildBox(); } UI_PopCP(UI_TopCP()); } } UI_PopCP(UI_TopCP()); } } UI_PopCP(UI_TopCP()); } EndScratch(scratch); return console_box; }