//- sixten: Views inline workspace_view *W_CreateNewView(workspace_view_type Type, workspace_panel *Parent) { memory_arena *Arena = ArenaAllocate(Gigabytes(1)); workspace_view *View = PushStruct(Arena, workspace_view); View->Arena = Arena; View->Type = Type; View->Parent = Parent; switch(View->Type) { case W_View_Editor: { View->Data = PushStruct(View->Arena, workspace_view_editor); } break; case W_View_CommandPalette: { View->Data = PushStruct(View->Arena, workspace_view_command_palette); } break; case W_View_Settings: { View->Data = PushStruct(View->Arena, workspace_view_settings); } break; case W_View_TextEditor: { View->Data = PushStruct(View->Arena, workspace_view_text_editor); workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; Editor->ProcessingArena = ArenaAllocate(Gigabytes(1)); Editor->Text = MutableStringAllocate(Gigabytes(1)); Editor->HistoryArena = ArenaAllocate(Gigabytes(1)); SenDLLInit(&Editor->History.Sentinel); Editor->History.At = &Editor->History.Sentinel; Editor->SavePoint = Editor->History.At; workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String); Editor->Tokens = TextData.Tokens; Editor->Lines = TextData.Lines; } break; case W_View_CharacterEditor: { View->Data = PushStruct(View->Arena, workspace_view_character_editor); } break; default: break; } DLLInsertLast(Parent->FirstView, Parent->LastView, View); if(View->Parent) { Parent->CurrentView = View; } return(View); } inline void W_DestroyView(workspace_view *View) { switch(View->Type) { case W_View_TextEditor: { workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; ArenaRelease(Editor->ProcessingArena); MutableStringRelease(&Editor->Text); ArenaRelease(Editor->HistoryArena); } break; default: break; } // sixten(NOTE): This function does not ensure that the view is not being used anywhere else. ArenaRelease(View->Arena); } inline b32 W_ViewIsCurrent(workspace_view *View) { workspace *Workspace = W_GetState(); b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View); return(Result); } inline string W_GetViewName(workspace_view *View) { string Result = StrLit("Unnamed view"); switch(View->Type) { case W_View_Startup: { Result = StrLit("Welcome"); } break; case W_View_Editor: { Result = StrLit("Editor"); } break; case W_View_Settings: { Result = StrLit("Settings"); } break; case W_View_TextEditor: { workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; if(AreEqual(Editor->FileName, StrLit(""))) { Result = StrLit("Open File"); } else { //if(Editor->History.At == &Editor->History.Sentinel) if(Editor->History.At == Editor->SavePoint) { Result = PushString(W_FrameArena(), Editor->FileName); } else { Result = PushFormat(W_FrameArena(), "* %S", Editor->FileName); } } } break; case W_View_SceneView: { Result = StrLit("Scene View"); } break; case W_View_CommandPalette: { Result = StrLit("Command Palette"); } break; case W_View_CharacterEditor: { Result = StrLit("Character Editor"); } break; } return(Result); } //- sixten: Builder code static void W_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data) { workspace_view_command_palette *CommandPalette = (workspace_view_command_palette *)Data; string ToCursor = MakeString(Box->String.Data, CommandPalette->ListerInputEditState.Cursor); string ToMark = MakeString(Box->String.Data, CommandPalette->ListerInputEditState.Mark); r32 TargetCursorX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToCursor); r32 TargetMarkerX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToMark); r32 CursorX = AC_AnimateValueF(TargetCursorX, 0, 0.175, "Workspace View Input Cursor %p", Box); r32 MarkerX = AC_AnimateValueF(TargetMarkerX, 0, 0.175, "Workspace View Input Mark %p", Box); v2 BoxDim = DimOfRange(Box->Rect); // sixten: Draw selection { v2 Offset = V2(7.5, (BoxDim.y - Box->FontSize) / 2); v2 Dim = V2(0, Box->FontSize); if(CursorX > MarkerX) { Offset.x += MarkerX; Dim.x = CursorX - MarkerX; } else { Offset.x += CursorX; Dim.x = MarkerX - CursorX; } v2 P = Box->Rect.Min + Offset; v4 Color = V4(0.4, 0.7, 0.8, 0.3); PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0); } // sixten: Draw cursor if(CommandPalette->ListerFieldSelected) { range1_r32 CursorSpan = Range1R32(CursorX, TargetCursorX); r32 Height = Box->FontSize + 4; v2 Offset = V2(7.5F + CursorSpan.Min, (BoxDim.y - Height) / 2); v2 Dim = V2(1.25F + CursorSpan.Max - CursorSpan.Min, Height); v2 P = Box->Rect.Min + Offset; v4 Color = V4(0.3, 1, 0.3, 0.7); PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0); } } static void W_BuildViewTypeLister(workspace_view *View) { workspace_view_command_palette *CommandPalette = (workspace_view_command_palette *)View->Data; workspace *Workspace = W_GetState(); temporary_memory Scratch = GetScratch(0, 0); UI_Size(UI_Percent(1, 1), UI_Percent(1, 1)) UI_Parent(UI_MakeBox(0, StrLit(""))) { UI_Spacer(UI_Pixels(1.25, 1)); UI_CornerRadius(4) UI_BackgroundColor(V4(0.5, 0.2, 0.3, 1.0)) UI_LayoutAxis(Axis2_X) UI_Height(UI_Pixels(30, 1)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawDropShadow, "Workspace View Lister Header")) { UI_Spacer(UI_Pixels(5, 1)); UI_Width(UI_TextContent(10, 1)) UI_LabelF("Open View:"); UI_Spacer(UI_Pixels(15, 1)); // sixten: Input Field. { r32 SelectedTransition = AC_AnimateValueF(CommandPalette->ListerFieldSelected ? 1.0 : 0.0, 0, 0.125, "View Input Field %p", View); v4 BorderColor = UI_TopBackgroundColor()*2; BorderColor.w = SelectedTransition; UI_SetNextBorderColor(BorderColor); UI_SetNextBackgroundColor(LinearBlend(V4(0, 0, 0, 1), UI_TopBackgroundColor(), 0.5)); UI_SetNextWidth(UI_Percent(1, 0)); ui_box *InputBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | UI_BoxFlag_Clickable | UI_BoxFlag_DrawBorder, "View Type Lister Input"); UI_Parent(InputBox) { UI_SetNextWidth(UI_TextContent(15, 1)); ui_box *InputTextBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Workspace View Lister")); InputTextBox->String = MakeString(CommandPalette->ListerInput, CommandPalette->ListerInputUsed); UI_EquipBoxCustomDrawCallback(InputTextBox, W_ViewListerInputCallback, CommandPalette); } UI_Spacer(UI_Pixels(4, 1)); if(CommandPalette->ListerFieldSelected) { for(platform_event *Event = Workspace->EventList->First; Event != 0; Event = Event->Next) { if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) { text_action Action = SingleLineTextActionFromEvent(Event); if(IsValid(&Action)) { text_op Op = TextOpFromAction(Scratch.Arena, MakeString(CommandPalette->ListerInput, CommandPalette->ListerInputUsed), &CommandPalette->ListerInputEditState, &Action); string Left = MakeString(CommandPalette->ListerInput, Op.Range.Min); string Right = MakeString(CommandPalette->ListerInput + Op.Range.Max, CommandPalette->ListerInputUsed - Op.Range.Max); u64 NewStringSize = Left.Count + Right.Count + Op.ReplaceString.Count; char *NewString = PushArray(Scratch.Arena, char, NewStringSize); Copy(NewString, Left.Data, Left.Count); Copy(NewString + Left.Count, Op.ReplaceString.Data, Op.ReplaceString.Count); Copy(NewString + Left.Count + Op.ReplaceString.Count, Right.Data, Right.Count); CommandPalette->ListerInputUsed = Minimum(ArrayCount(CommandPalette->ListerInput), NewStringSize); Copy(CommandPalette->ListerInput, NewString, CommandPalette->ListerInputUsed); CommandPalette->ListerInputEditState.Cursor = Minimum(Op.NewCursor, CommandPalette->ListerInputUsed); CommandPalette->ListerInputEditState.Mark = Minimum(Op.NewMark, CommandPalette->ListerInputUsed); } } } } if(UI_SignalFromBox(InputBox).Pressed) { CommandPalette->ListerFieldSelected = true; } ui *UI = UI_GetState(); if(!(AreEqual(UI->Active, UI_EmptyKey()) || AreEqual(UI->Active, InputBox->Key))) { CommandPalette->ListerFieldSelected = false; } } } UI_Spacer(UI_Pixels(15, 1)); { UI_SetNextCornerRadius(10); UI_SetNextBackgroundColor(V4(0.15, 0.15, 0.15, 1.0)); UI_SetNextBorderColor(V4(0.35, 0.35, 0.35, 1.0)); UI_SetNextSize(UI_Percent(1, 1), UI_Pixels(100, 1)); UI_SetNextLayoutAxis(Axis2_Y); ui_box *ButtonBox = UI_MakeBox(UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawBorder | UI_BoxFlag_HotAnimation | UI_BoxFlag_ActiveAnimation | UI_BoxFlag_Clickable, StrLit("Type Lister Box")); UI_SignalFromBox(ButtonBox); } } ReleaseScratch(Scratch); } static void W_BuildSettingsTabButton(workspace_view_settings *Settings, char *Name, workspace_settings_category Category) { b32 IsSelected = (Settings->Category == Category); v4 Color = LinearBlend(Theme_TextColor, Theme_HighlightBorderColor, AC_AnimateValueF(IsSelected, IsSelected, 0.3, "Workspace Settings %s %p", Name, Settings)); UI_SetNextFont(Font_Bold); UI_SetNextHeight(UI_TextContent(0, 1)); UI_SetNextTextColor(Color); ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawText | UI_BoxFlag_Clickable, Name); ui_signal Signal = UI_SignalFromBox(Box); if(Signal.Hovering) { Platform.SetCursor(PlatformCursor_Hand); } if(Signal.Pressed) { Settings->Category = Category; } } static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 *Open, s32 *Selected) { b32 Result = false; UI_SetNextLayoutAxis(Axis2_X); UI_Parent(UI_MakeBoxF(0, "")) { UI_LabelF("Refresh Rate:"); UI_Spacer(UI_Pixels(10, 1)); b32 ActiveInDropdown = false; UI_SetNextWidth(UI_Pixels(200, 1)); UI_SetNextCornerRadius(4); UI_SetNextLayoutAxis(Axis2_X); ui_box *DropdownBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawBorder | UI_BoxFlag_HotAnimation | UI_BoxFlag_ActiveAnimation | UI_BoxFlag_Clickable, "Dropdown"); UI_Parent(DropdownBox) { UI_Width(UI_Percent(1, 0)) UI_LabelF(Alternatives[*Selected]); UI_BackgroundColor(Theme_BorderColor) UI_Width(UI_Pixels(1, 1)) UI_MakeBoxF(UI_BoxFlag_DrawBackground, ""); UI_Width(UI_Pixels(25, 1)) UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_DownDir); } ui_signal DropdownSignal = UI_SignalFromBox(DropdownBox); if(DropdownSignal.Pressed) { *Open = !(*Open); } if(AreEqual(UI_ActiveKey(), DropdownBox->Key)) { ActiveInDropdown = true; } r32 OpenTransition = AC_AnimateValueF(*Open, 0, 0.175, "UI Dropdown %p%p", Alternatives, Open); if(OpenTransition > 0.1) { UI_Tooltip { UI_SetNextFixedP(V2(DropdownBox->Rect.Min.x, DropdownBox->Rect.Max.y)); UI_SetNextCornerRadius(4); UI_SetNextWidth(UI_Pixels(200, 1)); UI_SetNextHeight(UI_ChildrenSum(OpenTransition, 1)); UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip | UI_BoxFlag_DrawDropShadow | UI_BoxFlag_FloatingX | UI_BoxFlag_FloatingY, "Dropdown Contents")) { UI_PushWidth(UI_Percent(1, 1)); for(s64 Index = 0; Index < Round(AlternativeCount*OpenTransition); ++Index) { ui_signal ButtonSignal = UI_ButtonF(Alternatives[Index]); if(AreEqual(UI_ActiveKey(), ButtonSignal.Box->Key)) { ActiveInDropdown = true; } if(ButtonSignal.Pressed) { *Selected = Index; Result = true; } } UI_PopWidth(); } } } if(!ActiveInDropdown && !AreEqual(UI_ActiveKey(), UI_EmptyKey())) { *Open = false; } } return(Result); } static void W_BuildSceneView(workspace_view *View) { workspace *Workspace = W_GetState(); SV_BuildSceneView(Workspace->Input); } static void W_BuildSettings(workspace_view *View) { workspace_view_settings *Settings = (workspace_view_settings *)View->Data; workspace *Workspace = W_GetState(); UI_Height(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) UI_Row() UI_Padding(UI_Pixels(50, 0)) { UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Settings"); } UI_LayoutAxis(Axis2_X) UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(0, "")) { UI_Width(UI_Pixels(300, 1)) UI_Parent(UI_MakeBoxF(0, "Navigation")) { UI_Row() UI_Padding(UI_Pixels(50, 1)) UI_Width(UI_Percent(1, 0)) UI_Column() UI_Padding(UI_Percent(1, 0)) UI_Height(UI_ChildrenSum(1, 1)) UI_LayoutAxis(Axis2_Y) UI_Parent(UI_MakeBoxF(0, "")) { W_BuildSettingsTabButton(Settings, "All", W_Settings_All); UI_Spacer(UI_Pixels(30, 1)); W_BuildSettingsTabButton(Settings, "General", W_Settings_General); UI_Spacer(UI_Pixels(30, 1)); W_BuildSettingsTabButton(Settings, "Theme", W_Settings_Theme); UI_Spacer(UI_Pixels(30, 1)); W_BuildSettingsTabButton(Settings, "Developer", W_Settings_Developer); UI_Spacer(UI_Pixels(150, 1)); } } UI_CornerRadius(5) UI_Width(UI_Pixels(1.25, 1)) UI_BackgroundColor(Color_Grey) UI_MakeBoxF(UI_BoxFlag_DrawBackground, "Separator"); UI_Padding(UI_Pixels(70, 0)) UI_LayoutAxis(Axis2_Y) UI_Width(UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(0, "Tab")) UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) { UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); UI_Scroll(0, &Settings->GlobalScroll, UI_BoxFlag_Clip) { workspace_settings_category Category = Settings->Category; if(!Category || (Category == W_Settings_General)) { UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("General"); char *Alternatives[] = {"15 Hz", "30 Hz", "60 Hz", "120 Hz", "144 Hz", "Uncapped", "V-Sync"}; s64 AlternativeMapping[] = {15, 30, 60, 120, 144, -1, 0}; s32 DropdownSelected; FindIndexOfElement(DropdownSelected, AlternativeMapping, 0, Workspace->Input->RefreshRate); if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives), &Settings->GeneralDropdownOpen, &DropdownSelected)) { Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected]; Settings->GeneralDropdownOpen = false; } UI_Spacer(UI_Pixels(50, 1)); } if(!Category || (Category == W_Settings_Theme)) { UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Theme"); UI_SetNextSize(UI_Percent(1, 1), UI_Em(13, 1)); UI_Scroll(0, &Settings->ThemeScroll) { UI_Size(UI_Percent(1, 1), UI_Em(2, 1)) { for(s32 Index = 0; Index < 2; ++Index) { UI_ButtonF("Hello#%i", Index); UI_ButtonF("Line#%i", Index); UI_ButtonF("Paint#%i", Index); UI_ButtonF("Color#%i", Index); UI_ButtonF("Design#%i", Index); UI_ButtonF("Address#%i", Index); UI_ButtonF("Brightness#%i", Index); } } } UI_Spacer(UI_Pixels(50, 1)); } if(!Category || (Category == W_Settings_Developer)) { UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Developer"); UI_Checkbox(&DEBUG_DebugSettings->RenderUIDebugRects, StrLit("Render UI Debug Rects")); UI_Spacer(UI_Pixels(5, 1)); UI_Checkbox(&DEBUG_DebugSettings->RenderFPSCounter, StrLit("Render FPS Counter")); UI_Spacer(UI_Pixels(5, 1)); UI_Checkbox(&DEBUG_DebugSettings->ListHotAndActive, StrLit("List Hot & Active")); UI_Spacer(UI_Pixels(50, 1)); } } } } UI_Spacer(UI_Pixels(50, 1)); } static void W_BuildView(workspace_view *View) { r32 ViewHighlightTransition = AC_AnimateValueF(W_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View); UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, ViewHighlightTransition)); UI_PushBackgroundColor(Theme_BackgroundColor); UI_SetNextCornerRadius(3); ui_box *ViewBox = UI_MakeBoxF(UI_BoxFlag_Clickable | UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawBorder | UI_BoxFlag_Clip, "Workspace View %p", View); UI_Parent(ViewBox) UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) { switch(View->Type) { case W_View_Startup: { UI_Row() UI_Padding(UI_Pixels(50, 0)) UI_Width(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) { UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) { UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Welcome to VN"); UI_TextColor(Theme_BorderColor) UI_LabelF("An impractical way to make a game"); UI_Spacer(UI_Percent(1, 0)); UI_Checkbox(&DEBUG_DebugSettings->ShowWelcomeMessage, StrLit("Show this message on startup")); } } } break; case W_View_CommandPalette: { W_BuildViewTypeLister(View); } break; case W_View_Editor: { Workspace_BuildEditor(View); } break; case W_View_Settings: { W_BuildSettings(View); } break; case W_View_TextEditor: { W_BuildTextEditor(View); } break; case W_View_SceneView: { W_BuildSceneView(View); } break; case W_View_CharacterEditor: { W_BuildCharacterEditorView(View); } break; } } UI_PopBackgroundColor(); UI_SignalFromBox(ViewBox); }