diff --git a/code/build.bat b/code/build.bat index e886679..3fdafea 100644 --- a/code/build.bat +++ b/code/build.bat @@ -1,11 +1,11 @@ @echo off -set CommonCompilerOptions=/Zi /FC /nologo /DVN_INTERNAL=1 /DVN_SLOW=1 /Oi /W4 /WX /wd4996 /wd4201 /wd4305 /wd4244 /wd4100 /wd4505 /std:c++17 +set CommonCompilerOptions=/Zi /FC /nologo /DVN_INTERNAL=1 /DVN_SLOW=1 /DVN_USE_INSTANCING=1 /Oi /W4 /WX /wd4996 /wd4201 /wd4305 /wd4244 /wd4100 /wd4505 /std:c++17 if not exist "../build" mkdir "../build" pushd "../build/" -rem cl /Zi /nologo /FC ../code/third_party/codegen/codegen.c +cl /Zi /nologo /FC ../code/third_party/codegen/codegen.c codegen ../code/ cl %CommonCompilerOptions% ../code/vn.cpp /LD /link /export:VN_UpdateAndRender /incremental:no diff --git a/code/core/core_string.cpp b/code/core/core_string.cpp index dcc734e..5ff771a 100644 --- a/code/core/core_string.cpp +++ b/code/core/core_string.cpp @@ -35,12 +35,24 @@ inline string MakeString(u8 *Data, s64 Count) return(Result); } +inline string MakeString(u8 *Start, u8 *End) +{ + string Result = {(s64)(End-Start), Start}; + return(Result); +} + inline string MakeString(char *CString) { string Result = {StringLength(CString), (u8 *)CString}; return(Result); } +inline string16 MakeString16(u16 *Data, s64 Count) +{ + string16 Result = {Count, Data}; + return(Result); +} + //- sixten: Equality static b32 AreEqual(string A, string B) @@ -351,6 +363,17 @@ static s64 StringLength(char *String) return(Result); } +static s64 StringLength16(u16 *String) +{ + s64 Result = 0; + while(*String++) + { + ++Result; + } + + return(Result); +} + //~ sixten: String list @@ -553,7 +576,7 @@ static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint) u32 Size = 0; u8 DummyDest[4]; Dest = Dest?Dest:DummyDest; - if(Codepoint < (1<<8)) + if(Codepoint < (1<<7)) { Dest[0] = Codepoint; Size = 1; diff --git a/code/core/core_string.h b/code/core/core_string.h index d09ae17..a8dcc9d 100644 --- a/code/core/core_string.h +++ b/code/core/core_string.h @@ -64,8 +64,11 @@ inline b32 IsLetter(char C); //- sixten: Basic constructors inline string MakeString(u8 *Data, s64 Count); +inline string MakeString(u8 *Start, u8 *End); inline string MakeString(char *CString); -#define StrLit(String) MakeString((u8 *)String, ArrayCount(String) - 1) +#define StrLit(String) MakeString((u8 *)String, (s64)(ArrayCount(String) - 1)) + +inline string16 MakeString16(u16 *Data, s64 Count); //- sixten: Equality @@ -109,6 +112,7 @@ static string RemoveAll(memory_arena *Arena, string Text, char ToRemove); //- sixten: "C Style" strings static s64 StringLength(char *String); +static s64 StringLength16(u16 *String); #if 0 diff --git a/code/generated/vn_opengl_functions.meta.c b/code/generated/opengl_functions.meta.c similarity index 100% rename from code/generated/vn_opengl_functions.meta.c rename to code/generated/opengl_functions.meta.c diff --git a/code/generated/vn_opengl_functions.meta.h b/code/generated/opengl_functions.meta.h similarity index 100% rename from code/generated/vn_opengl_functions.meta.h rename to code/generated/opengl_functions.meta.h diff --git a/code/generated/vn_platform.meta.h b/code/generated/vn_platform.meta.h index 6ef505c..b4ad352 100644 --- a/code/generated/vn_platform.meta.h +++ b/code/generated/vn_platform.meta.h @@ -13,6 +13,8 @@ #define PLATFORM_BEGIN_FILE_ITER(name) platform_file_iter * name(memory_arena *Arena, string Path) #define PLATFORM_ADVANCE_FILE_ITER(name) b32 name(memory_arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo) #define PLATFORM_END_FILE_ITER(name) void name(platform_file_iter *Iter) +#define PLATFORM_SET_CLIPBOARD(name) void name(string String) +#define PLATFORM_GET_CLIPBOARD(name) string name(memory_arena *Arena) typedef PLATFORM_RESERVE(platform_reserve); typedef PLATFORM_RELEASE(platform_release); @@ -29,6 +31,8 @@ typedef PLATFORM_SHOW_MESSAGE(platform_show_message); typedef PLATFORM_BEGIN_FILE_ITER(platform_begin_file_iter); typedef PLATFORM_ADVANCE_FILE_ITER(platform_advance_file_iter); typedef PLATFORM_END_FILE_ITER(platform_end_file_iter); +typedef PLATFORM_SET_CLIPBOARD(platform_set_clipboard); +typedef PLATFORM_GET_CLIPBOARD(platform_get_clipboard); struct platform_api { @@ -47,6 +51,8 @@ platform_show_message *ShowMessage; platform_begin_file_iter *BeginFileIter; platform_advance_file_iter *AdvanceFileIter; platform_end_file_iter *EndFileIter; +platform_set_clipboard *SetClipboard; +platform_get_clipboard *GetClipboard; }; #define RegisterPlatformFunctions(PlatformName)\ @@ -65,4 +71,6 @@ Platform.ShowMessage = PlatformName##_ShowMessage;\ Platform.BeginFileIter = PlatformName##_BeginFileIter;\ Platform.AdvanceFileIter = PlatformName##_AdvanceFileIter;\ Platform.EndFileIter = PlatformName##_EndFileIter;\ +Platform.SetClipboard = PlatformName##_SetClipboard;\ +Platform.GetClipboard = PlatformName##_GetClipboard;\ diff --git a/code/generated/vn_ui.meta.c b/code/generated/vn_ui.meta.c index 36d32f2..9518956 100644 --- a/code/generated/vn_ui.meta.c +++ b/code/generated/vn_ui.meta.c @@ -13,6 +13,7 @@ inline void UI_PushFont(font_id Element) { ui *UI = UI_GetState(); Ass inline void UI_PushFontSize(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed + 1 < ArrayCount(UI->Stacks.FontSizeStack)); UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed++] = Element; } inline void UI_PushOffsetX(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed + 1 < ArrayCount(UI->Stacks.OffsetXStack)); UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed++] = Element; } inline void UI_PushOffsetY(r32 Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed + 1 < ArrayCount(UI->Stacks.OffsetYStack)); UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed++] = Element; } +inline void UI_PushHoverCursor(platform_cursor Element) { ui *UI = UI_GetState(); Assert(UI->Stacks.HoverCursorStackUsed + 1 < ArrayCount(UI->Stacks.HoverCursorStack)); UI->Stacks.HoverCursorStack[UI->Stacks.HoverCursorStackUsed++] = Element; } inline void UI_PopParent(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.ParentStackUsed > 0); --UI->Stacks.ParentStackUsed; } inline void UI_PopWidth(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.WidthStackUsed > 0); --UI->Stacks.WidthStackUsed; } inline void UI_PopHeight(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.HeightStackUsed > 0); --UI->Stacks.HeightStackUsed; } @@ -28,6 +29,7 @@ inline void UI_PopFont(void) { ui *UI = UI_GetState(); Ass inline void UI_PopFontSize(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.FontSizeStackUsed > 0); --UI->Stacks.FontSizeStackUsed; } inline void UI_PopOffsetX(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetXStackUsed > 0); --UI->Stacks.OffsetXStackUsed; } inline void UI_PopOffsetY(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.OffsetYStackUsed > 0); --UI->Stacks.OffsetYStackUsed; } +inline void UI_PopHoverCursor(void) { ui *UI = UI_GetState(); Assert(UI->Stacks.HoverCursorStackUsed > 0); --UI->Stacks.HoverCursorStackUsed; } inline void UI_SetNextParent(ui_box * Element) { ui *UI = UI_GetState(); UI_PushParent(Element); UI->Stacks.AutoPopParent = true; } inline void UI_SetNextWidth(ui_size Element) { ui *UI = UI_GetState(); UI_PushWidth(Element); UI->Stacks.AutoPopWidth = true; } inline void UI_SetNextHeight(ui_size Element) { ui *UI = UI_GetState(); UI_PushHeight(Element); UI->Stacks.AutoPopHeight = true; } @@ -43,6 +45,7 @@ inline void UI_SetNextFont(font_id Element) { ui *UI = UI_GetState(); UI_PushFon inline void UI_SetNextFontSize(r32 Element) { ui *UI = UI_GetState(); UI_PushFontSize(Element); UI->Stacks.AutoPopFontSize = true; } inline void UI_SetNextOffsetX(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetX(Element); UI->Stacks.AutoPopOffsetX = true; } inline void UI_SetNextOffsetY(r32 Element) { ui *UI = UI_GetState(); UI_PushOffsetY(Element); UI->Stacks.AutoPopOffsetY = true; } +inline void UI_SetNextHoverCursor(platform_cursor Element) { ui *UI = UI_GetState(); UI_PushHoverCursor(Element); UI->Stacks.AutoPopHoverCursor = true; } inline ui_box * UI_FirstParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[0]); } inline ui_size UI_FirstWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[0]); } inline ui_size UI_FirstHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[0]); } @@ -58,6 +61,7 @@ inline font_id UI_FirstFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.Fo inline r32 UI_FirstFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[0]); } inline r32 UI_FirstOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[0]); } inline r32 UI_FirstOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[0]); } +inline platform_cursor UI_FirstHoverCursor(void) { ui *UI = UI_GetState(); return(UI->Stacks.HoverCursorStack[0]); } inline ui_box * UI_TopParent(void) { ui *UI = UI_GetState(); return(UI->Stacks.ParentStack[UI->Stacks.ParentStackUsed - 1]); } inline ui_size UI_TopWidth(void) { ui *UI = UI_GetState(); return(UI->Stacks.WidthStack[UI->Stacks.WidthStackUsed - 1]); } inline ui_size UI_TopHeight(void) { ui *UI = UI_GetState(); return(UI->Stacks.HeightStack[UI->Stacks.HeightStackUsed - 1]); } @@ -73,6 +77,7 @@ inline font_id UI_TopFont(void) { ui *UI = UI_GetState(); return(UI->Stacks.Font inline r32 UI_TopFontSize(void) { ui *UI = UI_GetState(); return(UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]); } inline r32 UI_TopOffsetX(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]); } inline r32 UI_TopOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]); } +inline platform_cursor UI_TopHoverCursor(void) { ui *UI = UI_GetState(); return(UI->Stacks.HoverCursorStack[UI->Stacks.HoverCursorStackUsed - 1]); } #define UI_Parent(Element) DeferLoop(UI_PushParent(Element), UI_PopParent()) #define UI_Width(Element) DeferLoop(UI_PushWidth(Element), UI_PopWidth()) #define UI_Height(Element) DeferLoop(UI_PushHeight(Element), UI_PopHeight()) @@ -88,6 +93,7 @@ inline r32 UI_TopOffsetY(void) { ui *UI = UI_GetState(); return(UI->Stacks.Offse #define UI_FontSize(Element) DeferLoop(UI_PushFontSize(Element), UI_PopFontSize()) #define UI_OffsetX(Element) DeferLoop(UI_PushOffsetX(Element), UI_PopOffsetX()) #define UI_OffsetY(Element) DeferLoop(UI_PushOffsetY(Element), UI_PopOffsetY()) +#define UI_HoverCursor(Element) DeferLoop(UI_PushHoverCursor(Element), UI_PopHoverCursor()) static void UI_ApplyStyles(ui_box *Box) { @@ -107,5 +113,6 @@ Assert(UI->Stacks.FontStackUsed > 0); Box->Font = UI Assert(UI->Stacks.FontSizeStackUsed > 0); Box->FontSize = UI->Stacks.FontSizeStack[UI->Stacks.FontSizeStackUsed - 1]; if(UI->Stacks.AutoPopFontSize) { UI_PopFontSize(); UI->Stacks.AutoPopFontSize = false; } Assert(UI->Stacks.OffsetXStackUsed > 0); Box->Offset.x = UI->Stacks.OffsetXStack[UI->Stacks.OffsetXStackUsed - 1]; if(UI->Stacks.AutoPopOffsetX) { UI_PopOffsetX(); UI->Stacks.AutoPopOffsetX = false; } Assert(UI->Stacks.OffsetYStackUsed > 0); Box->Offset.y = UI->Stacks.OffsetYStack[UI->Stacks.OffsetYStackUsed - 1]; if(UI->Stacks.AutoPopOffsetY) { UI_PopOffsetY(); UI->Stacks.AutoPopOffsetY = false; } +Assert(UI->Stacks.HoverCursorStackUsed > 0); Box->HoverCursor = UI->Stacks.HoverCursorStack[UI->Stacks.HoverCursorStackUsed - 1]; if(UI->Stacks.AutoPopHoverCursor) { UI_PopHoverCursor(); UI->Stacks.AutoPopHoverCursor = false; } } diff --git a/code/generated/vn_ui.meta.h b/code/generated/vn_ui.meta.h index a32695f..f0ad51f 100644 --- a/code/generated/vn_ui.meta.h +++ b/code/generated/vn_ui.meta.h @@ -15,5 +15,6 @@ font_id FontStack[64]; s32 FontStackUsed; b32 AutoPopFont; r32 FontSizeStack[64]; s32 FontSizeStackUsed; b32 AutoPopFontSize; r32 OffsetXStack[64]; s32 OffsetXStackUsed; b32 AutoPopOffsetX; r32 OffsetYStack[64]; s32 OffsetYStackUsed; b32 AutoPopOffsetY; +platform_cursor HoverCursorStack[64]; s32 HoverCursorStackUsed; b32 AutoPopHoverCursor; }; diff --git a/code/vn_opengl_defines.h b/code/opengl_defines.h similarity index 100% rename from code/vn_opengl_defines.h rename to code/opengl_defines.h diff --git a/code/vn_opengl_functions.md b/code/opengl_functions.md similarity index 100% rename from code/vn_opengl_functions.md rename to code/opengl_functions.md diff --git a/code/opengl_render.cpp b/code/opengl_render.cpp index e1087a8..5367a9f 100644 --- a/code/opengl_render.cpp +++ b/code/opengl_render.cpp @@ -353,11 +353,157 @@ Out_Color = Color*TextureFactor*BorderFactor*SDFFactor; return(Program); } +static instanced_quad_program OpenGL_CompileInstacedQuadProgram(void) +{ + char *VertexSource = + R"GLSL( + + in v4 In_Dest; +in v4 In_Source; +in s32 In_TextureIndex; + in u32 In_Color[4]; + in r32 In_CornerRadius; + in r32 In_EdgeSoftness; + in r32 In_BorderThickness; +)GLSL" + "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" + R"GLSL( + +uniform v2 Uniform_Resolution; + +out v2 DestP; +out v2 DestHalfSize; +out v2 DestCenter; +out v2 SourceP; +out v4 Color; +out r32 CornerRadius; +out r32 EdgeSoftness; +out r32 BorderThickness; +flat out s32 TextureIndex; + +void main() +{ +v2[] Vertices = V2[](V2(0, 0), V2(0, 1), V2(1, 0), V2(1, 1)); + +DestP = In_Dest.xy + (In_Dest.zw-In_Dest.xy)*Vertices[gl_VertexID]; +DestHalfSize = (In_Dest.zw-In_Dest.xy)/2; +DestCenter = (In_Dest.xy+In_Dest.zw)/2; + +v2 SourceDim = In_Source.zw-In_Source.xy; +SourceP = In_Source.xy + SourceDim*Vertices[gl_VertexID]; + +v2 ScreenP = V2(DestP.x / Uniform_Resolution.x, DestP.y / Uniform_Resolution.y); +ScreenP = ScreenP*2 - 1; + ScreenP.y = -ScreenP.y; + +gl_Position = V4(ScreenP, 0, 1); +u32 ColorData = In_Color[gl_VertexID]; +Color.r = r32((ColorData >> 24) & 255u)/255.0; + Color.g = r32((ColorData >> 16) & 255u)/255.0; + Color.b = r32((ColorData >> 8) & 255u)/255.0; + Color.a = r32((ColorData >> 0) & 255u)/255.0; + +CornerRadius = In_CornerRadius; + EdgeSoftness = In_EdgeSoftness; + BorderThickness = In_BorderThickness; + +TextureIndex = In_TextureIndex; +} + +)GLSL"; + + char *FragmentSource = + R"GLSL( + + in v2 DestP; + in v2 DestHalfSize; + in v2 DestCenter; +in v2 SourceP; +in v4 Color; + in r32 CornerRadius; + in r32 EdgeSoftness; + in r32 BorderThickness; +flat in s32 TextureIndex; +)GLSL" + "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" + R"GLSL( + +out v4 Out_Color; + +r32 RoundedRect(v2 P, v2 Center, v2 HalfSize, r32 r) +{ +v2 d2 = AbsoluteValue(Center - P) - HalfSize + r; +r32 Result = Min(Max(d2.x, d2.y), 0) + Length(Max(d2, 0)) - r; +return(Result); +} + +void main() +{ +r32 SoftnessPadding = Max(0, EdgeSoftness*2-1); + +r32 Dist = RoundedRect(DestP, DestCenter, DestHalfSize - SoftnessPadding, CornerRadius); +r32 SDFFactor = 1 - smoothstep(0, 2*EdgeSoftness, Dist); + +r32 BorderFactor = 1; +if(BorderThickness != 0) +{ +v2 InteriorHalfSize = DestHalfSize - BorderThickness; + +r32 InteriorRadiusReduce = Min(InteriorHalfSize.x / DestHalfSize.x, InteriorHalfSize.y / DestHalfSize.y); +r32 InteriorCornerRadius = CornerRadius*InteriorRadiusReduce*InteriorRadiusReduce; + +r32 InsideDist = RoundedRect(DestP, DestCenter, InteriorHalfSize - SoftnessPadding, InteriorCornerRadius); +BorderFactor = smoothstep(0, 2*EdgeSoftness, InsideDist); +} + +v4 TextureFactor = texture(TextureSamplers[TextureIndex], SourceP); +Out_Color = Color*SDFFactor*BorderFactor*TextureFactor; +} + +)GLSL"; + + instanced_quad_program Program = {}; + Program.ID = OpenGL_CompileShaderProgram(VertexSource, FragmentSource); + + Program.DestID = glGetAttribLocation(Program.ID, "In_Dest"); + Program.SourceID = glGetAttribLocation(Program.ID, "In_Source"); + Program.TextureIndexID = glGetAttribLocation(Program.ID, "In_TextureIndex"); + Program.ColorID[0] = glGetAttribLocation(Program.ID, "In_Color[0]"); + Program.ColorID[1] = glGetAttribLocation(Program.ID, "In_Color[1]"); + Program.ColorID[2] = glGetAttribLocation(Program.ID, "In_Color[2]"); + Program.ColorID[3] = glGetAttribLocation(Program.ID, "In_Color[3]"); + Program.CornerRadiusID = glGetAttribLocation(Program.ID, "In_CornerRadius"); + Program.EdgeSoftnessID = glGetAttribLocation(Program.ID, "In_EdgeSoftness"); + Program.BorderThicknessID = glGetAttribLocation(Program.ID, "In_BorderThickness"); + + glUseProgram(Program.ID); + Program.UniformResolutionLocation = glGetUniformLocation(Program.ID, "Uniform_Resolution"); + + temporary_memory Scratch = GetScratch(0, 0); + for(s32 TextureIndex = 0; + TextureIndex < MAX_BOUND_TEXTURES; + ++TextureIndex) + { + string Name = PushFormat(Scratch.Arena, "TextureSamplers[%i]", TextureIndex); + s32 Location = glGetUniformLocation(Program.ID, (char *)Name.Data); + glUniform1i(Location, TextureIndex); + } + ReleaseScratch(Scratch); + + glUseProgram(0); + + return(Program); +} + #define OpenGL_EnableFloatVertexAttribute(Index, Size, Type, type, Member)\ if(Index != -1)\ {\ glEnableVertexAttribArray(Index);\ glVertexAttribPointer(Index, Size, Type, GL_FALSE, sizeof(type), (void *)OffsetOf(type, Member));\ +if(VN_USE_INSTANCING)\ +{\ +glVertexAttribDivisor(Index, 1);\ +}\ } #define OpenGL_EnableIntegerVertexAttribute(Index, Size, Type, type, Member)\ @@ -365,6 +511,10 @@ if(Index != -1)\ {\ glEnableVertexAttribArray(Index);\ glVertexAttribIPointer(Index, Size, Type, sizeof(type), (void *)OffsetOf(type, Member));\ +if(VN_USE_INSTANCING)\ +{\ +glVertexAttribDivisor(Index, 1);\ +}\ } #define OpenGL_DisableVertexAttribute(Index)\ @@ -403,6 +553,38 @@ static void OpenGL_EndProgram(quad_program *Program) glUseProgram(0); } +static void OpenGL_BeginProgram(instanced_quad_program *Program) +{ + glUseProgram(Program->ID); + + OpenGL_EnableFloatVertexAttribute(Program->DestID, 4, GL_FLOAT, instanced_quad, Dest); + OpenGL_EnableFloatVertexAttribute(Program->SourceID, 4, GL_FLOAT, instanced_quad, Source); + OpenGL_EnableIntegerVertexAttribute(Program->TextureIndexID, 1, GL_UNSIGNED_INT, instanced_quad, TextureIndex); + OpenGL_EnableIntegerVertexAttribute(Program->ColorID[0], 1, GL_UNSIGNED_INT, instanced_quad, Color[0]); + OpenGL_EnableIntegerVertexAttribute(Program->ColorID[1], 1, GL_UNSIGNED_INT, instanced_quad, Color[1]); + OpenGL_EnableIntegerVertexAttribute(Program->ColorID[2], 1, GL_UNSIGNED_INT, instanced_quad, Color[2]); + OpenGL_EnableIntegerVertexAttribute(Program->ColorID[3], 1, GL_UNSIGNED_INT, instanced_quad, Color[3]); + OpenGL_EnableFloatVertexAttribute(Program->CornerRadiusID, 1, GL_FLOAT, instanced_quad, CornerRadius); + OpenGL_EnableFloatVertexAttribute(Program->EdgeSoftnessID, 1, GL_FLOAT, instanced_quad, EdgeSoftness); + OpenGL_EnableFloatVertexAttribute(Program->BorderThicknessID, 1, GL_FLOAT, instanced_quad, BorderThickness); +} + +static void OpenGL_EndProgram(instanced_quad_program *Program) +{ + OpenGL_DisableVertexAttribute(Program->DestID); + OpenGL_DisableVertexAttribute(Program->SourceID); + OpenGL_DisableVertexAttribute(Program->TextureIndexID); + OpenGL_DisableVertexAttribute(Program->ColorID[0]); + OpenGL_DisableVertexAttribute(Program->ColorID[1]); + OpenGL_DisableVertexAttribute(Program->ColorID[2]); + OpenGL_DisableVertexAttribute(Program->ColorID[3]); + OpenGL_DisableVertexAttribute(Program->CornerRadiusID); + OpenGL_DisableVertexAttribute(Program->EdgeSoftnessID); + OpenGL_DisableVertexAttribute(Program->BorderThicknessID); + + glUseProgram(0); +} + static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, umm MaxPushBufferSize) { opengl_context Context = {}; @@ -410,22 +592,35 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um RenderCommands->MaxPushBufferSize = MaxPushBufferSize; RenderCommands->PushBufferBase = (u8 *)OpenGL_AllocateMemory(RenderCommands->MaxPushBufferSize); +#if VN_USE_INSTANCING + RenderCommands->MaxInstancedQuadCount = MAX_QUAD_COUNT; + RenderCommands->InstancedQuadBase = (instanced_quad *)OpenGL_AllocateMemory(RenderCommands->MaxInstancedQuadCount*sizeof(instanced_quad)); +#else RenderCommands->MaxQuadVertexCount = MAX_QUAD_COUNT*4; RenderCommands->QuadVertexBase = (quad_vertex *)OpenGL_AllocateMemory(RenderCommands->MaxQuadVertexCount*sizeof(quad_vertex)); RenderCommands->MaxQuadIndexCount = MAX_QUAD_COUNT*6; RenderCommands->QuadIndexBase = (s32 *)OpenGL_AllocateMemory(RenderCommands->MaxQuadIndexCount*sizeof(s32)); - -#if 0 - RenderCommands->MaxInstancedQuadCount = MAX_QUAD_COUNT; - RenderCommands->InstancedQuadBase = (instanced_quad *)OpenGL_AllocateMemory(RenderCommands->MaxInstancedQuadCount*sizeof(instanced_quad)); #endif +#if VN_USE_INSTANCING + Context.InstancedQuadProgram = OpenGL_CompileInstacedQuadProgram(); +#else Context.QuadProgram = OpenGL_CompileQuadProgram(); +#endif +#if VN_USE_INSTANCING + glGenBuffers(1, &Context.InstancedQuadBuffer); +#else glGenBuffers(1, &Context.VertexBuffer); glGenBuffers(1, &Context.IndexBuffer); +#endif +#if VN_USE_INSTANCING + glBindBuffer(GL_ARRAY_BUFFER, Context.InstancedQuadBuffer); + glBufferData(GL_ARRAY_BUFFER, RenderCommands->MaxInstancedQuadCount*sizeof(instanced_quad), 0, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#else glBindBuffer(GL_ARRAY_BUFFER, Context.VertexBuffer); glBufferData(GL_ARRAY_BUFFER, RenderCommands->MaxQuadVertexCount*sizeof(quad_vertex), 0, GL_STREAM_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -433,6 +628,7 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context.IndexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, RenderCommands->MaxQuadIndexCount*sizeof(s32), 0, GL_STREAM_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#endif u32 WhiteData = 0xFFFFFFFF; RenderCommands->WhiteTexture = OpenGL_AllocateTexture(V2S32(1, 1), Render_TextureFormat_RGBA8, &WhiteData); @@ -440,7 +636,7 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um RenderCommands->AllocateTexture = OpenGL_AllocateTexture; RenderCommands->FillRegion = OpenGL_FillRegion; -#if VN_SLOW +#if VN_SLOW&&0 glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(OpenGL_DebugMessageCallback, 0); @@ -456,10 +652,11 @@ static opengl_context OpenGL_SetupContext(vn_render_commands *RenderCommands, um static void OpenGL_BeginFrame(vn_render_commands *RenderCommands, v2 RenderDim) { RenderCommands->PushBufferAt = RenderCommands->PushBufferBase; +#if VN_USE_INSTANCING + RenderCommands->InstancedQuadCount = 0; +#else RenderCommands->QuadVertexCount = 0; RenderCommands->QuadIndexCount = 0; -#if 0 - RenderCommands->InstancedQuadCount = 0; #endif RenderCommands->RenderDim = RenderDim; @@ -475,6 +672,7 @@ static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderC glEnable(GL_SCISSOR_TEST); +#if !VN_USE_INSTANCING glBindBuffer(GL_ARRAY_BUFFER, Context->VertexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context->IndexBuffer); @@ -485,10 +683,7 @@ static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderC void *IndexData = RenderCommands->QuadIndexBase; umm IndexSize = RenderCommands->QuadIndexCount*sizeof(s32); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, IndexSize, IndexData); - - OpenGL_BeginProgram(&Context->QuadProgram); - glUniform2f(Context->QuadProgram.UniformResolutionLocation, - RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); +#endif for(u8 *PushBufferAt = RenderCommands->PushBufferBase; PushBufferAt < RenderCommands->PushBufferAt;) @@ -507,60 +702,90 @@ static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderC glClear(GL_COLOR_BUFFER_BIT); } break; - case Render_Command_render_command_quads: - { - render_command_quads *Command = (render_command_quads *)PushBufferAt; - PushBufferAt += sizeof(*Command); - - for(s32 TextureIndex = 0; - TextureIndex < Command->TexturesUsed; - ++TextureIndex) - { - opengl_texture Texture = OpenGL_GetTextureFromHandle(Command->Textures[TextureIndex]); - glActiveTexture(GL_TEXTURE0 + TextureIndex); - glBindTexture(GL_TEXTURE_2D, Texture.ID); - } - - glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0); - - glBindTexture(GL_TEXTURE_2D, 0); - } break; - -#if 0 +#if VN_USE_INSTANCING case Render_Command_render_command_instanced_quads: { - render_command_quads *Command = (render_command_quads *)PushBufferAt; + render_command_instanced_quads *Command = (render_command_instanced_quads *)PushBufferAt; PushBufferAt += sizeof(*Command); + render_texture_mapping *Mapping = &Command->Mapping; + for(s32 TextureIndex = 0; - TextureIndex < Command->TexturesUsed; + TextureIndex < Mapping->TexturesUsed; ++TextureIndex) { - opengl_texture Texture = OpenGL_GetTextureFromHandle(Command->Textures[TextureIndex]); + opengl_texture Texture = OpenGL_GetTextureFromHandle(Mapping->Textures[TextureIndex]); glActiveTexture(GL_TEXTURE0 + TextureIndex); glBindTexture(GL_TEXTURE_2D, Texture.ID); } - glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ARRAY_BUFFER, Context->InstancedQuadBuffer); + void *VertexData = RenderCommands->InstancedQuadBase+Command->QuadBufferIndex; + umm VertexSize = Command->QuadCount*sizeof(instanced_quad); + glBufferSubData(GL_ARRAY_BUFFER, 0, VertexSize, VertexData); + + OpenGL_BeginProgram(&Context->InstancedQuadProgram); + glUniform2f(Context->InstancedQuadProgram.UniformResolutionLocation, + RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, Command->QuadCount); + + OpenGL_EndProgram(&Context->InstancedQuadProgram); glBindTexture(GL_TEXTURE_2D, 0); } break; -#endif case Render_Command_render_command_clip: { render_command_clip *Command = (render_command_clip *)PushBufferAt; PushBufferAt += sizeof(*Command); -#if 0 - glScissor(Command->ClipRect.P.x, RenderCommands->RenderDim.y - Command->ClipRect.P.y - Command->ClipRect.Dim.y, - Command->ClipRect.Dim.x, Command->ClipRect.Dim.y); -#endif + v2_r32 P = Command->ClipRect.Min; + P.x = Max(Round(P.x), 0.0f); + P.y = Max(Round(P.y), 0.0f); + + v2_r32 Dim = DimOfRange(Command->ClipRect); + + Dim.x = Max(Round(Dim.x), 0.0f); + Dim.y = Max(Round(Dim.y), 0.0f); + + v2_r32 FlippedP = V2R32(P.x, RenderCommands->RenderDim.y-Dim.y-P.y); + + glScissor(FlippedP.x, FlippedP.y, Dim.x, Dim.y); } break; + +#else + case Render_Command_render_command_quads: + { + render_command_quads *Command = (render_command_quads *)PushBufferAt; + PushBufferAt += sizeof(*Command); + + OpenGL_BeginProgram(&Context->QuadProgram); + glUniform2f(Context->QuadProgram.UniformResolutionLocation, + RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); + + render_texture_mapping *Mapping = &Command->Mapping; + + for(s32 TextureIndex = 0; + TextureIndex < Mapping->TexturesUsed; + ++TextureIndex) + { + opengl_texture Texture = OpenGL_GetTextureFromHandle(Mapping->Textures[TextureIndex]); + glActiveTexture(GL_TEXTURE0 + TextureIndex); + glBindTexture(GL_TEXTURE_2D, Texture.ID); + } + + glDrawElements(GL_TRIANGLES, Command->QuadCount*6, GL_UNSIGNED_INT, 0); + + OpenGL_EndProgram(&Context->QuadProgram); + + glBindTexture(GL_TEXTURE_2D, 0); + } break; +#endif + + InvalidDefaultCase; } } - OpenGL_EndProgram(&Context->QuadProgram); - glBindBuffer(GL_ARRAY_BUFFER, 0); } \ No newline at end of file diff --git a/code/opengl_render.h b/code/opengl_render.h index 0a927cd..7af0e6c 100644 --- a/code/opengl_render.h +++ b/code/opengl_render.h @@ -3,8 +3,8 @@ #ifndef OPENGL_RENDER_H #define OPENGL_RENDER_H -#include "vn_opengl_defines.h" -#include "generated/vn_opengl_functions.meta.h" +#include "opengl_defines.h" +#include "generated/opengl_functions.meta.h" struct opengl_texture { @@ -30,12 +30,31 @@ struct quad_program u32 UniformResolutionLocation; }; +struct instanced_quad_program +{ + u32 ID; + + u32 DestID; + u32 SourceID; + u32 TextureIndexID; + u32 ColorID[4]; + u32 CornerRadiusID; + u32 EdgeSoftnessID; + u32 BorderThicknessID; + + u32 UniformResolutionLocation; +}; + struct opengl_context { +#if VN_USE_INSTANCING + u32 InstancedQuadBuffer; + instanced_quad_program InstancedQuadProgram; +#else u32 VertexBuffer; u32 IndexBuffer; - quad_program QuadProgram; +#endif }; #endif //OPENGL_RENDER_H diff --git a/code/vn.cpp b/code/vn.cpp index b261af1..9872f07 100644 --- a/code/vn.cpp +++ b/code/vn.cpp @@ -22,10 +22,11 @@ global debug_settings *DEBUG_DebugSettings = 0; #include "vn_text_op.h" #include "vn_ui.h" #include "vn_ui_utils.h" +#include "vn_scene.h" +#include "vn_scene_view.h" #include "vn_workspace.h" #include "vn_animation_curve.h" #include "vn_theme_dark.h" -#include "vn_scene.h" #include "vn_tokenizer.cpp" #include "vn_config.cpp" @@ -34,12 +35,15 @@ global debug_settings *DEBUG_DebugSettings = 0; #include "vn_ui.cpp" #include "vn_ui_utils.cpp" #include "vn_animation_curve.cpp" -#include "vn_workspace.cpp" #include "vn_scene.cpp" +#include "vn_scene_view.cpp" +#include "vn_workspace.cpp" struct vn_state { memory_arena *Arena; + memory_arena *FrameArena; + glyph_atlas *GlyphAtlas; config *Config; @@ -50,8 +54,7 @@ struct vn_state render_handle BackgroundTexture; - memory_arena *SceneArena; - compiled_scene CompiledScene; + scene_view SceneView; #if VN_INTERNAL debug_settings DebugSettings; @@ -92,6 +95,7 @@ static render_handle CreateTextureFromPath(vn_render_commands *Commands, string return(Result); } + VN_UPDATE_AND_RENDER(VN_UpdateAndRender) { SetThreadContext(ThreadContext); @@ -105,12 +109,13 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) memory_arena *Arena = ArenaAllocate(Gigabytes(1)); State = Memory->State = PushStruct(Arena, vn_state); State->Arena = Arena; + State->FrameArena = ArenaAllocate(Gigabytes(1)); State->GlyphAtlas = CreateGlyphAtlas(RenderCommands); State->Config = CreateConfig(); //- sixten: load assets - //State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/Futon_Room.png")); + State->BackgroundTexture = CreateTextureFromPath(RenderCommands, StrLit("data/backgrounds/test.png")); //- sixten: setup config binds and load current config { @@ -123,17 +128,17 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1); #endif - Config_ReadFile(State->Config, StrLit("config.vn")); } //- sixten: load startup scene - State->SceneArena = ArenaAllocate(Gigabytes(1)); - State->CompiledScene = S_ScriptFromText(State->SceneArena, Platform_ReadEntireFile(State->SceneArena, StrLit("data/compiler_test.vns"))); + scene_view *SceneView = &State->SceneView; + SV_Init(SceneView, State->Arena); + SceneView->BackgroundTexture = State->BackgroundTexture; UI_Init(&State->UI); - Workspace_Init(&State->Workspace); - AnimationCurve_Init(&State->AnimationCurveState); + W_Init(&State->Workspace); + AC_Init(&State->AnimationCurveState); } #if VN_INTERNAL @@ -141,17 +146,30 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) #endif //- sixten: begin new frame - AnimationCurve_NewFrame(&State->AnimationCurveState, Input->dtForFrame); + ArenaClear(State->FrameArena); + AC_NewFrame(&State->AnimationCurveState, Input->dtForFrame); UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, State->GlyphAtlas); + SV_SetState(&State->SceneView); //- sixten: build the ui UI_BeginBuild(RenderCommands->RenderDim); { - Workspace_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas); + b32 EditorMode = true; + if(EditorMode) + { + W_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas); + } + else + { + SV_BuildSceneView(Input); + } } UI_EndBuild(); + //- sixten: update the scene + SV_Update(State->FrameArena, Input); + //- sixten: consume all remaining evetns for(platform_event *Event = Input->EventList->First; Event != 0; @@ -171,13 +189,6 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender) render_group Group = BeginRenderGroup(RenderCommands); PushClear(&Group, V3(0.1, 0.1, 0.1)); -#if 0 - PushTexturedQuad(&Group, - Range2R32(V2R32(0, 0), RenderCommands->RenderDim), - Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(State->BackgroundTexture))), - Color_White, Color_White, Color_White, Color_White, 0, 0, 0, State->BackgroundTexture); -#endif - UI_RenderFrame(&Group, State->GlyphAtlas); if(DEBUG_DebugSettings->ListHotAndActive) diff --git a/code/vn_animation_curve.cpp b/code/vn_animation_curve.cpp index 45ff5f0..93de928 100644 --- a/code/vn_animation_curve.cpp +++ b/code/vn_animation_curve.cpp @@ -1,21 +1,24 @@ global animation_curve_state *Global_AnimationCurveState = 0; -inline animation_curve_state *AnimationCurve_GetState(void) +//////////////////////////////// +//~ sixten: Animation Curve Functions + +inline animation_curve_state *AC_GetState(void) { return(Global_AnimationCurveState); } -inline void AnimationCurve_SetState(animation_curve_state *State) +inline void AC_SetState(animation_curve_state *State) { Global_AnimationCurveState = State; } -static void AnimationCurve_Init(animation_curve_state *State) +static void AC_Init(animation_curve_state *State) { State->Arena = ArenaAllocate(Gigabytes(1)); } -inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String) +inline animation_curve_key AC_GenerateKeyFromString(string String) { animation_curve_key Key; Key.Value = HashString(String); @@ -23,9 +26,9 @@ inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String) return(Key); } -static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key Key, r32 Initial) +static animation_curve_entry *AC_GetEntryByKey(animation_curve_key Key, r32 Initial) { - animation_curve_state *State = AnimationCurve_GetState(); + animation_curve_state *State = AC_GetState(); u64 Hash = Key.Value; u64 Slot = Hash % ArrayCount(State->Buckets); @@ -67,26 +70,26 @@ static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key K return(Result); } -inline r32 AnimationCurve_GetValue(string Name, r32 Initial) +inline r32 AC_GetValue(string Name, r32 Initial) { - animation_curve_key Key = AnimationCurve_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AnimationCurve_GetEntryByKey(Key, Initial); + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); r32 Result = Entry->Value; return(Result); } -inline void AnimationCurve_SetValue(string Name, r32 Value) +inline void AC_SetValue(string Name, r32 Value) { - animation_curve_key Key = AnimationCurve_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AnimationCurve_GetEntryByKey(Key, Value); + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Value); Entry->Value = Value; } -inline r32 AnimationCurve_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value) +inline r32 AC_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value) { - animation_curve_state *State = AnimationCurve_GetState(); + animation_curve_state *State = AC_GetState(); r32 Result = *Value; @@ -96,16 +99,16 @@ inline r32 AnimationCurve_AnimateValueDirect(r32 Target, r32 Duration, r32 *Valu return(Result); } -inline r32 AnimationCurve_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name) +inline r32 AC_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name) { - animation_curve_key Key = AnimationCurve_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AnimationCurve_GetEntryByKey(Key, Initial); + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); - r32 Result = AnimationCurve_AnimateValueDirect(Target, Duration, &Entry->Value); + r32 Result = AC_AnimateValueDirect(Target, Duration, &Entry->Value); return(Result); } -inline r32 AnimationCurve_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...) +inline r32 AC_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...) { temporary_memory Scratch = GetScratch(0, 0); @@ -114,16 +117,16 @@ inline r32 AnimationCurve_AnimateValueF(r32 Target, r32 Initial, r32 Duration, c string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); va_end(Arguments); - r32 Result = AnimationCurve_AnimateValue(Target, Initial, Duration, String); + r32 Result = AC_AnimateValue(Target, Initial, Duration, String); ReleaseScratch(Scratch); return(Result); } -static void AnimationCurve_NewFrame(animation_curve_state *State, r32 dtForFrame) +static void AC_NewFrame(animation_curve_state *State, r32 dtForFrame) { - AnimationCurve_SetState(State); + AC_SetState(State); State->dtForFrame = dtForFrame; // sixten: Prune untouched entries. diff --git a/code/vn_animation_curve.h b/code/vn_animation_curve.h index 5cf9d34..77c329d 100644 --- a/code/vn_animation_curve.h +++ b/code/vn_animation_curve.h @@ -3,17 +3,14 @@ #ifndef VN_ANIMATION_CURVE_H #define VN_ANIMATION_CURVE_H +//////////////////////////////// +//~ sixten: Animation Curve Types + struct animation_curve_key { u64 Value; }; -inline b32 AreEqual(animation_curve_key A, animation_curve_key B) -{ - b32 Result = (A.Value == B.Value); - return(Result); -} - struct animation_curve_entry { animation_curve_key Key; @@ -46,15 +43,24 @@ struct animation_curve_state animation_curve_entry *LastFreeEntry; }; -inline void AnimationCurve_SetState(animation_curve_state *State); -inline animation_curve_key AnimationCurve_GenerateKeyFromString(string String); -static void AnimationCurve_Init(animation_curve_state *State); -static animation_curve_entry *AnimationCurve_GetEntryByKey(animation_curve_key Key, r32 Initial = 0); -inline r32 AnimationCurve_GetValue(string Name, r32 Initial); -inline void AnimationCurve_SetValue(string Name, r32 Value); -inline r32 AnimationCurve_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value); -inline r32 AnimationCurve_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name); -inline r32 AnimationCurve_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...); -static void AnimationCurve_NewFrame(animation_curve_state *State, r32 dtForFrame); +//////////////////////////////// +//~ sixten: Animation Curve Functions + +inline b32 AreEqual(animation_curve_key A, animation_curve_key B) +{ + b32 Result = (A.Value == B.Value); + return(Result); +} + +static void AC_SetState(animation_curve_state *State); +static animation_curve_key AC_GenerateKeyFromString(string String); +static void AC_Init(animation_curve_state *State); +static animation_curve_entry *AC_GetEntryByKey(animation_curve_key Key, r32 Initial = 0); +static r32 AC_GetValue(string Name, r32 Initial); +static void AC_SetValue(string Name, r32 Value); +static r32 AC_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value); +static r32 AC_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name); +static r32 AC_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...); +static void AC_NewFrame(animation_curve_state *State, r32 dtForFrame); #endif //VN_ANIMATION_CURVE_H diff --git a/code/vn_config.cpp b/code/vn_config.cpp index 7d8a2f4..be782e4 100644 --- a/code/vn_config.cpp +++ b/code/vn_config.cpp @@ -252,7 +252,7 @@ static void Config_WriteFile(config *Config, string Path) string_list Out = {}; temporary_memory Scratch = GetScratch(); - string LastDir = MakeString(0, 0); + string LastDir = MakeString(0, 0LL); for(config_entry *Entry = Config->FirstInternal; Entry != 0; Entry = Entry->NextInternal) @@ -265,7 +265,7 @@ static void Config_WriteFile(config *Config, string Path) if(!AreEqual(Dir, LastDir)) { - if(!AreEqual(LastDir, MakeString(0, 0))) + if(!AreEqual(LastDir, MakeString(0, 0LL))) { AppendString(&Out, StrLit("}\n\n"), Scratch.Arena); } @@ -309,7 +309,7 @@ static void Config_WriteFile(config *Config, string Path) AppendString(&Out, StrLit(";\n"), Scratch.Arena); } - if(!AreEqual(LastDir, MakeString(0, 0))) + if(!AreEqual(LastDir, MakeString(0, 0LL))) { AppendString(&Out, StrLit("}"), Scratch.Arena); } diff --git a/code/vn_font.cpp b/code/vn_font.cpp index 1465b3e..f8e535a 100644 --- a/code/vn_font.cpp +++ b/code/vn_font.cpp @@ -122,6 +122,8 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands, Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono.ttf")); Atlas->Fonts[Font_MonospaceOblique].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono-Oblique.ttf")); Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf")); + Atlas->Fonts[Font_Fancy].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Merriweather-Regular.ttf")); + Atlas->Fonts[Font_Japanese].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/NotoSansJP-Regular.ttf")); Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/icons.ttf")); for(s32 FontIndex = 0; diff --git a/code/vn_font.h b/code/vn_font.h index f69dc3c..a33cd83 100644 --- a/code/vn_font.h +++ b/code/vn_font.h @@ -10,6 +10,8 @@ enum font_id Font_Monospace, Font_MonospaceOblique, Font_Hand, + Font_Fancy, + Font_Japanese, Font_Icons, Font_Count, @@ -41,7 +43,7 @@ enum font_id #define FontIcon_Reply 0xf112 #define FontIcon_Terminal 0xf120 #define FontIcon_Ellipsis 0xf141 -#define FontIcon_Document 0xf15b +#define FontIcon_Document 0xe819 #define FontIcon_DocumentText 0xf15c #define FontIcon_Eyedropper 0xf1fb #define FontIcon_WindowMaximize 0xf2d0 @@ -60,6 +62,19 @@ enum font_id #define FontIcon_DocumentFileImage 0xf1c5 #define FontIcon_DocumentFileAudio 0xf1c7 #define FontIcon_DocumentFileCode 0xf1c9 +#define FontIcon_UserPlus 0xf234 +#define FontIcon_UserTimes 0xf235 +#define FontIcon_History 0xf235 +#define FontIcon_Trash 0xf1f8 +#define FontIcon_Debug 0xf188 +#define FontIcon_Gamepad 0xf11b +#define FontIcon_Paste 0xf0ea +#define FontIcon_Cut 0xe825 +#define FontIcon_Lock 0xe821 +#define FontIcon_LockOpen 0xe822 +#define FontIcon_Pin 0xe820 +#define FontIcon_User 0xe81e +#define FontIcon_Users 0xe81f struct glyph { @@ -78,7 +93,7 @@ struct glyph }; #define DEFAULT_GLYPH_ATLAS_DIM 1024*4 -#define MAX_GLYPH_SIZE 64 +#define MAX_GLYPH_SIZE 128 #define STB_TRUETYPE_IMPLEMENTATION #include "third_party/stb_truetype.h" diff --git a/code/vn_platform.h b/code/vn_platform.h index 8717bc1..0831bc8 100644 --- a/code/vn_platform.h +++ b/code/vn_platform.h @@ -151,6 +151,11 @@ struct vn_render_commands u8 *PushBufferBase; u8 *PushBufferAt; +#if VN_USE_INSTANCING + s32 MaxInstancedQuadCount; + instanced_quad *InstancedQuadBase; + s32 InstancedQuadCount; +#else s32 MaxQuadVertexCount; quad_vertex *QuadVertexBase; s32 QuadVertexCount; @@ -158,6 +163,7 @@ struct vn_render_commands s32 MaxQuadIndexCount; s32 *QuadIndexBase; s32 QuadIndexCount; +#endif v2 RenderDim; }; diff --git a/code/vn_platform.md b/code/vn_platform.md index 86c768b..1bb4cc8 100644 --- a/code/vn_platform.md +++ b/code/vn_platform.md @@ -15,6 +15,8 @@ { BeginFileIter begin_file_iter BEGIN_FILE_ITER `platform_file_iter *` `memory_arena *Arena, string Path` } { AdvanceFileIter advance_file_iter ADVANCE_FILE_ITER `b32` `memory_arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo` } { EndFileIter end_file_iter END_FILE_ITER `void` `platform_file_iter *Iter` } + { SetClipboard set_clipboard SET_CLIPBOARD `void` `string String` } + { GetClipboard get_clipboard GET_CLIPBOARD `string` `memory_arena *Arena` } } @table_gen diff --git a/code/vn_render.cpp b/code/vn_render.cpp index fd2c62d..9a012bf 100644 --- a/code/vn_render.cpp +++ b/code/vn_render.cpp @@ -66,7 +66,7 @@ inline void PushClear(render_group *Group, v3 Color) Command->Color = Color; } -inline s32 GetTextureIndexForCommand(render_command_quads *Command, render_handle Handle) +inline s32 GetTextureIndexForCommand(render_texture_mapping *Mapping, render_handle Handle) { s32 Result = -1; @@ -74,15 +74,15 @@ inline s32 GetTextureIndexForCommand(render_command_quads *Command, render_handl TextureIndex < MAX_BOUND_TEXTURES; ++TextureIndex) { - if(AreEqual(Command->Textures[TextureIndex], EmptyRenderHandle())) + if(AreEqual(Mapping->Textures[TextureIndex], EmptyRenderHandle())) { - Assert(Command->TexturesUsed == TextureIndex); + Assert(Mapping->TexturesUsed == TextureIndex); - Command->Textures[TextureIndex] = Handle; - ++Command->TexturesUsed; + Mapping->Textures[TextureIndex] = Handle; + ++Mapping->TexturesUsed; } - if(AreEqual(Command->Textures[TextureIndex], Handle)) + if(AreEqual(Mapping->Textures[TextureIndex], Handle)) { Result = TextureIndex; break; @@ -102,46 +102,55 @@ inline u32 PackV4ToU32(v4 V) return(Result); } -#if 0 -static void PushTexturedQuadAsInstances(render_group *Group, - range2_r32 Dest, - range2_r32 Source, - v4 Color00, v4 Color10, v4 Color01, v4 Color11, - r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness, - render_handle Texture) +#if VN_USE_INSTANCING +static void PushTexturedQuad(render_group *Group, + range2_r32 Dest, + range2_r32 Source, + v4 Color00, v4 Color10, v4 Color01, v4 Color11, + r32 CornerRadius, r32 EdgeSoftness, r32 BorderThickness, + render_handle Texture) { vn_render_commands *Commands = Group->Commands; render_command_instanced_quads *Command = (render_command_instanced_quads *)(Group->CurrentCommand + 1); s32 TextureIndex; if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_instanced_quads && - (TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture)) != -1)) + (TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture)) != -1)) { Command = PushCommand(Group, render_command_instanced_quads); - TextureIndex = GetTextureIndexForCommand((render_command_quads *)Command, Texture); - Command->QuadBufferIndex = Commands->QuadIndexCount; + TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture); + Command->QuadBufferIndex = Commands->InstancedQuadCount; } - range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed]; - range2_r32 FinalDest = Intersection(Dest, Clip); - - if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max)) + //if(InRange(Clip, Dest.Min) || InRange(Clip, Dest.Max)) { + v2_s32 TextureSize = DimFromTexture(Texture); + v2_r32 TextureSizeReal = ConvertV2ToR32(TextureSize); + + Source.Min /= TextureSizeReal; + Source.Max /= TextureSizeReal; + +#if 0 + Dest.Min.x = Round(Dest.Min.x); + Dest.Min.y = Round(Dest.Min.y); +#endif + instanced_quad *Quad = Commands->InstancedQuadBase + Commands->InstancedQuadCount++; Quad->Dest = Dest; Quad->Source = Source; Quad->TextureIndex = TextureIndex; - Quad->Colors[0] = PackV4ToU32(Color00); - Quad->Colors[1] = PackV4ToU32(Color10); - Quad->Colors[2] = PackV4ToU32(Color01); - Quad->Colors[3] = PackV4ToU32(Color11); + Quad->Color[0] = PackV4ToU32(Color00); + Quad->Color[1] = PackV4ToU32(Color01); + Quad->Color[2] = PackV4ToU32(Color10); + Quad->Color[3] = PackV4ToU32(Color11); Quad->CornerRadius = CornerRadius; Quad->EdgeSoftness = EdgeSoftness; Quad->BorderThickness = BorderThickness; + + Command->QuadCount += 1; } } -#endif - +#else static void PushTexturedQuad(render_group *Group, range2_r32 Dest, range2_r32 Source, @@ -154,11 +163,11 @@ static void PushTexturedQuad(render_group *Group, render_command_quads *Command = (render_command_quads *)(Group->CurrentCommand + 1); s32 TextureIndex; if(!(Group->CurrentCommand && Group->CurrentCommand->Type == Render_Command_render_command_quads && - (TextureIndex = GetTextureIndexForCommand(Command, Texture)) != -1)) + (TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture)) != -1)) { Command = PushCommand(Group, render_command_quads); Command->QuadBufferIndex = Commands->QuadIndexCount; - TextureIndex = GetTextureIndexForCommand(Command, Texture); + TextureIndex = GetTextureIndexForCommand(&Command->Mapping, Texture); } range2_r32 Clip = Group->ClipStack[Group->ClipStackUsed]; @@ -242,13 +251,8 @@ static void PushTexturedQuad(render_group *Group, ++Command->QuadCount; } -#if 0 - else - { - int BreakMe = 0; - } -#endif } +#endif inline void PushQuad(render_group *Group, range2_r32 Dest, v4 Color00, v4 Color01, v4 Color10, v4 Color11, @@ -270,12 +274,21 @@ inline void PushClip(render_group *Group, range2_r32 Clip) { Assert(Group->ClipStackUsed + 1 < ArrayCount(Group->ClipStack)); +#if VN_USE_INSTANCING + render_command_clip *Command = PushCommand(Group, render_command_clip); + Command->ClipRect = Group->ClipStack[++Group->ClipStackUsed] = Clip; +#else Group->ClipStack[++Group->ClipStackUsed] = Clip; +#endif } inline void PopClip(render_group *Group) { Assert(Group->ClipStackUsed > 0); - - Group->ClipStack[--Group->ClipStackUsed]; +#if VN_USE_INSTANCING + render_command_clip *Command = PushCommand(Group, render_command_clip); + Command->ClipRect = Group->ClipStack[--Group->ClipStackUsed]; +#else + --Group->ClipStackUsed; +#endif } \ No newline at end of file diff --git a/code/vn_render.h b/code/vn_render.h index aff2041..5bd29ec 100644 --- a/code/vn_render.h +++ b/code/vn_render.h @@ -44,7 +44,7 @@ enum render_command_type { Render_Command_render_command_clear, Render_Command_render_command_quads, - //Render_Command_render_command_instanced_quads, + Render_Command_render_command_instanced_quads, Render_Command_render_command_clip, }; @@ -53,15 +53,26 @@ struct render_command_clear v3 Color; }; -struct render_command_quads +struct render_texture_mapping { - u64 QuadCount; - u64 QuadBufferIndex; - s32 TexturesUsed; render_handle Textures[16]; }; +struct render_command_quads +{ + render_texture_mapping Mapping; + u64 QuadCount; + u64 QuadBufferIndex; +}; + +struct render_command_instanced_quads +{ + render_texture_mapping Mapping; + u64 QuadCount; + u64 QuadBufferIndex; +}; + struct render_command_clip { range2_r32 ClipRect; @@ -82,18 +93,16 @@ struct render_group s32 ClipStackUsed; }; -#if 0 struct instanced_quad { range2_r32 Dest; range2_r32 Source; u32 TextureIndex; - u32 Colors[4]; + u32 Color[4]; r32 CornerRadius; r32 EdgeSoftness; r32 BorderThickness; }; -#endif struct quad_vertex { diff --git a/code/vn_scene.cpp b/code/vn_scene.cpp index ae3b8a0..0d6cf68 100644 --- a/code/vn_scene.cpp +++ b/code/vn_scene.cpp @@ -1,14 +1,29 @@ #include "generated/vn_scene.meta.h" #include "generated/vn_scene.meta.c" +//////////////////////////////// +//~ sixten: Scene Compiler Functions + +static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset) +{ + Compiler->EncounteredError = true; + Compiler->InPanicMode = true; + scene_compile_error *Error = PushStruct(Compiler->Arena, scene_compile_error); + Error->Message = PushFormat(Compiler->Arena, Message); + Error->Token = Compiler->At[TokenOffset]; + QueuePush(Compiler->Errors.First, Compiler->Errors.Last, Error); + Compiler->Errors.Count += 1; +} + static void S_EmitByte(scene_compiler *Compiler, u8 Byte) { - scene_annotated_bytecode_bucket *Bucket = Compiler->CurrentBucket; + scene_emission_target *Target = &Compiler->TargetStack[Compiler->TargetStackIndex]; + scene_annotated_bytecode_bucket *Bucket = Target->Bucket; scene_annotated_bytecode_chunk *Chunk = Bucket->Last; - if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || !AreEqual(Chunk->Name, Compiler->CurrentName)) + if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || (Target->Type == S_EmissionTarget_Named && !AreEqual(Chunk->Name, Target->Name))) { - Chunk = PushStruct(Compiler->Arena, scene_annotated_bytecode_chunk); - Chunk->Name = Compiler->CurrentName; + Chunk = PushStruct(Target->Arena, scene_annotated_bytecode_chunk); + Chunk->Name = Target->Name; QueuePush(Bucket->First, Bucket->Last, Chunk); Bucket->Count += 1; @@ -17,6 +32,17 @@ static void S_EmitByte(scene_compiler *Compiler, u8 Byte) Chunk->Count += 1; } +static void S_EmitBucket(scene_compiler *Compiler, scene_annotated_bytecode_bucket *Bucket) +{ + for(scene_annotated_bytecode_chunk *Chunk = Bucket->First; Chunk != 0; Chunk = Chunk->Next) + { + for(s64 Index = 0; Index < Chunk->Count; Index += 1) + { + S_EmitByte(Compiler, Chunk->Data[Index]); + } + } +} + static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value) { scene_value_chunk *Chunk = Compiler->LastValueChunk; @@ -32,27 +58,30 @@ static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value) return(Result); } -static void S_EmitVariableLength(scene_compiler *Compiler, u64 Value) +static s64 S_EmitVariableLength(scene_compiler *Compiler, u64 Value) { + s64 Length = 1; u64 Index = Value; for(;Index > 0x7F; Index >>= 7) { S_EmitByte(Compiler, Index|0x80); + Length += 1; InvalidCodepath; } S_EmitByte(Compiler, Index); + return(Length); } -static u64 S_ReadVariableLength(u8 **BytePtr) +static scene_variable_read S_ReadVariableLength(u8 *Byte) { - u64 Result = 0; - u8 *Byte = *BytePtr; + scene_variable_read Result = {}; + u8 *StartByte = Byte; for(;*Byte & 0x80; Byte += 1) { - Result = (Result<<7)|(*Byte & 0x7F); + Result.Value = (Result.Value<<7)|(*Byte & 0x7F); } - Result = (Result<<7)|(*Byte & 0x7F); - *BytePtr = Byte; + Result.Value = (Result.Value<<7)|(*Byte & 0x7F); + Result.Size = Byte-StartByte+1; return(Result); } @@ -62,19 +91,20 @@ static void S_EmitConstant(scene_compiler *Compiler, scene_value Value) S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, Value)); } -static void S_SetEmissionTarget(scene_compiler *Compiler, string Target) +static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target) { - if(AreEqual(Target, StrLit(""))) - { - Compiler->CurrentBucket = &Compiler->GlobalScope; - Compiler->CurrentName = StrLit("Global Scope"); - } - else - { - u64 Hash = HashString(Target); - Compiler->CurrentBucket = &Compiler->ProcBuckets[Hash % ArrayCount(Compiler->ProcBuckets)]; - Compiler->CurrentName = Target; - } + Compiler->TargetStack[Compiler->TargetStackIndex] = Target; +} + +static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target) +{ + Compiler->TargetStackIndex += 1; + Compiler->TargetStack[Compiler->TargetStackIndex] = Target; +} + +static void S_PopEmissionTarget(scene_compiler *Compiler) +{ + Compiler->TargetStackIndex -= 1; } static scene_annotated_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name) @@ -166,7 +196,8 @@ static void S_ParseTopLevelDeclaration(scene_compiler *Compiler) } else { - S_ParseError(Compiler, "Expected top-level declaration (proc or var).."); + S_ParseError(Compiler, "Expected top-level declaration (proc or var).", 0); + Compiler->At += 1; } } @@ -175,22 +206,26 @@ static void S_ParseProcedure(scene_compiler *Compiler) token NameToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected procedure name after 'proc'"); S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after procedure name."); - S_SetEmissionTarget(Compiler, T_StringFromToken(Compiler->Text, NameToken)); - - for(;Compiler->At < Compiler->TokensEnd;) + if(!Compiler->InPanicMode) { - if(Compiler->At[0].Kind == TokenKind_CurlyClose) + string Name = T_StringFromToken(Compiler->Text, NameToken); + scene_annotated_bytecode_bucket *Bucket = &Compiler->ProcBuckets[HashString(Name)%ArrayCount(Compiler->ProcBuckets)]; + S_SetEmissionTarget(Compiler, S_NamedEmissionTarget(Compiler->Arena, Bucket, Name)); + + for(;Compiler->At < Compiler->TokensEnd;) { - Compiler->At += 1; - break; - } - else - { - S_ParseDeclaration(Compiler); + if(Compiler->At[0].Kind == TokenKind_CurlyClose) + { + Compiler->At += 1; + break; + } + else + { + S_ParseDeclaration(Compiler); + } } } } - static void S_ParseDeclaration(scene_compiler *Compiler) { switch(Compiler->At[0].Kind) @@ -205,17 +240,51 @@ static void S_ParseDeclaration(scene_compiler *Compiler) Compiler->At += 1; S_ParseLineEntry(Compiler); } break; + case TokenKind_Jump: + { + Compiler->At += 1; + S_ParseJumpStatement(Compiler); + } break; + case TokenKind_Branch: + { + Compiler->At += 1; + S_ParseBranchStatement(Compiler); + } break; default: { S_ParseStatement(Compiler); } break; } + + if(Compiler->InPanicMode) + { + for(;Compiler->At < Compiler->TokensEnd;) + { + if(Compiler->At[0].Kind == TokenKind_Semicolon) + { + goto End; + } + + switch(Compiler->At[-1].Kind) + { + case TokenKind_Var: goto End; + case TokenKind_StringLiteral: goto End; + case TokenKind_Jump: goto End; + case TokenKind_Branch: goto End; + } + + Compiler->At += 1; + } + + End: + Compiler->InPanicMode = false; + } } static void S_ParseVariableDeclaration(scene_compiler *Compiler) { S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected variable name."); - u64 NameConstant = S_MakeConstant(Compiler, S_MakePointer(&Compiler->At[-1])); + u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(Compiler->At[-1])); if(Compiler->At[0].Kind == TokenKind_Equal) { @@ -241,12 +310,12 @@ static void S_ParseVariableDeclaration(scene_compiler *Compiler) static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign) { - S_ParseNamedVariable(Compiler, &Compiler->At[-1], CanAssign); + S_ParseNamedVariable(Compiler, Compiler->At[-1], CanAssign); } -static void S_ParseNamedVariable(scene_compiler *Compiler, token *Token, b32 CanAssign) +static void S_ParseNamedVariable(scene_compiler *Compiler, token Token, b32 CanAssign) { - u64 NameConstant = S_MakeConstant(Compiler, S_MakePointer(Token)); + u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(Token)); if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) { Compiler->At += 1; @@ -262,7 +331,7 @@ static void S_ParseNamedVariable(scene_compiler *Compiler, token *Token, b32 Can static void S_ParseLineEntry(scene_compiler *Compiler) { - token *LineToken = &Compiler->At[-1]; + token LineToken = Compiler->At[-1]; b32 EmitAwait = true; @@ -290,13 +359,129 @@ static void S_ParseLineEntry(scene_compiler *Compiler) S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after line entry."); S_EmitByte(Compiler, S_Op_LineEntry|Flags); - S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakePointer(LineToken))); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(LineToken))); if(EmitAwait) { S_EmitByte(Compiler, S_Op_AwaitInput); } } +static void S_ParseJumpStatement(scene_compiler *Compiler) +{ + S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected jump destination."); + token DestToken = Compiler->At[-1]; + + S_EmitByte(Compiler, S_Op_Jump); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(DestToken))); + + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after jump statement."); +} + +static void S_ParseBranchStatement(scene_compiler *Compiler) +{ + temporary_memory Scratch = GetScratch(); + + scene_branch_case *FirstBranch = 0, *LastBranch = 0; + + S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after 'branch'."); + for(;Compiler->At < Compiler->TokensEnd;) + { + if(Compiler->At[0].Kind == TokenKind_CurlyClose) + { + Compiler->At += 1; + break; + } + else + { + //- sixten: parse branch declarations + switch(Compiler->At[0].Kind) + { + case TokenKind_StringLiteral: + { + scene_branch_case *Branch = S_ParseBranchCase(Compiler, Scratch.Arena); + QueuePush(FirstBranch, LastBranch, Branch); + } break; + + default: + { + S_ParseError(Compiler, "Expected branch case."); + Compiler->At += 1; + } break; + } + } + } + + //- sixten: emit add_branch calls + for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next) + { + S_EmitByte(Compiler, S_Op_AddBranch); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(Branch->Name))); + S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeOffset(0))); + scene_value_chunk *Chunk = Compiler->LastValueChunk; + Branch->OffsetValue = &Chunk->Values[Chunk->Count-1]; + } + + S_EmitByte(Compiler, S_Op_Halt); + + // sixten: We need to keep track of the amount of bytes that have been emitted between branch cases, such that we can patch our relative jumps + // to point to the correct address. + s64 BaseOffset = 0; + + //- sixten: emit branch contents + for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next) + { + //- sixten: patch branch jump dest + Branch->OffsetValue->Offset = BaseOffset; + + if(Branch->Bucket.Count > 0) + { + S_EmitBucket(Compiler, &Branch->Bucket); + BaseOffset += (Branch->Bucket.Count - 1)*ArrayCount(scene_bytecode_chunk::Data)+Branch->Bucket.Last->Count; + } + + S_EmitByte(Compiler, S_Op_JumpClose); + BaseOffset += 1; + + BaseOffset += S_EmitVariableLength(Compiler, S_MakeConstant(Compiler, S_MakeOffset(BaseOffset))); + + scene_value_chunk *Chunk = Compiler->LastValueChunk; + Branch->EndOffsetValue = &Chunk->Values[Chunk->Count-1]; + } + + //- sixten: patch the last jump + for(scene_branch_case *Branch = FirstBranch; Branch != 0; Branch = Branch->Next) + { + Branch->EndOffsetValue->Offset = BaseOffset-Branch->EndOffsetValue->Offset; + } + + ReleaseScratch(Scratch); +} + +static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, memory_arena *Arena) +{ + scene_branch_case *Branch = PushStruct(Arena, scene_branch_case); + Branch->Name = S_ConsumeToken(Compiler, TokenKind_StringLiteral, "Expected branch label at start of branch case."); + + S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after branch label."); + + S_PushEmissionTarget(Compiler, S_RawEmissionTarget(Arena, &Branch->Bucket)); + for(;Compiler->At < Compiler->TokensEnd;) + { + if(Compiler->At[0].Kind == TokenKind_CurlyClose) + { + Compiler->At += 1; + break; + } + else + { + S_ParseDeclaration(Compiler); + } + } + S_PopEmissionTarget(Compiler); + + return(Branch); +} + static void S_ParseStatement(scene_compiler *Compiler) { S_ParseExpression(Compiler); @@ -404,15 +589,16 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by u8 *ChunkBegin = Chunk->Data; u8 *ChunkEnd = ChunkBegin + Chunk->Count; - for(u8 *Data = ChunkBegin; Data < ChunkEnd; Data += 1) + for(u8 *Data = ChunkBegin; Data < ChunkEnd;) { switch(*Data) { case S_Op_Constant: { Data += 1; - u64 ValueIndex = S_ReadVariableLength(&Data); - scene_value Value = Compiler->FirstValueChunk->Values[ValueIndex]; + scene_variable_read VariableRead = S_ReadVariableLength(Data); + Data += VariableRead.Size; + scene_value Value = Compiler->FirstValueChunk->Values[VariableRead.Value]; AppendString(&List, StrLit("Constant: "), Scratch.Arena); switch(Value.Kind) { @@ -421,24 +607,24 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by case S_ValueKind_Pointer: { AppendString(&List, PushFormat(Scratch.Arena, "%x (pointer)\n", Value.Pointer), Scratch.Arena); } break; } } break; - case S_Op_Nil: { AppendString(&List, StrLit("Nil\n"), Scratch.Arena); } break; - case S_Op_True: { AppendString(&List, StrLit("True\n"), Scratch.Arena); } break; - case S_Op_False: { AppendString(&List, StrLit("False\n"), Scratch.Arena); } break; - case S_Op_Negate: { AppendString(&List, StrLit("Negate\n"), Scratch.Arena); } break; - case S_Op_Not: { AppendString(&List, StrLit("Not\n"), Scratch.Arena); } break; - case S_Op_Add: { AppendString(&List, StrLit("Add\n"), Scratch.Arena); } break; - case S_Op_Subtract: { AppendString(&List, StrLit("Subtract\n"), Scratch.Arena); } break; - case S_Op_Multiply: { AppendString(&List, StrLit("Multiply\n"), Scratch.Arena); } break; - case S_Op_Divide: { AppendString(&List, StrLit("Divide\n"), Scratch.Arena); } break; - case S_Op_Equal: { AppendString(&List, StrLit("Equal\n"), Scratch.Arena); } break; - case S_Op_Greater: { AppendString(&List, StrLit("Greater\n"), Scratch.Arena); } break; - case S_Op_Less: { AppendString(&List, StrLit("Less\n"), Scratch.Arena); } break; + case S_Op_Nil: { AppendString(&List, StrLit("Nil\n"), Scratch.Arena); Data += 1; } break; + case S_Op_True: { AppendString(&List, StrLit("True\n"), Scratch.Arena); Data += 1; } break; + case S_Op_False: { AppendString(&List, StrLit("False\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Negate: { AppendString(&List, StrLit("Negate\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Not: { AppendString(&List, StrLit("Not\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Add: { AppendString(&List, StrLit("Add\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Subtract: { AppendString(&List, StrLit("Subtract\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Multiply: { AppendString(&List, StrLit("Multiply\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Divide: { AppendString(&List, StrLit("Divide\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Equal: { AppendString(&List, StrLit("Equal\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Greater: { AppendString(&List, StrLit("Greater\n"), Scratch.Arena); Data += 1; } break; + case S_Op_Less: { AppendString(&List, StrLit("Less\n"), Scratch.Arena); Data += 1; } break; case S_Op_DefineGlobal: { Data += 1; - - u64 Index = S_ReadVariableLength(&Data); - u64 Pointer = Compiler->FirstValueChunk->Values[Index].Pointer; + scene_variable_read VariableRead = S_ReadVariableLength(Data); + Data += VariableRead.Size; + u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].Pointer; token *Token = (token *)Pointer; string String = T_StringFromToken(Compiler->Text, *Token); AppendString(&List, StrLit("Define Global: "), Scratch.Arena); @@ -449,7 +635,9 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by case S_Op_GetGlobal: { Data += 1; - u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + scene_variable_read VariableRead = S_ReadVariableLength(Data); + Data += VariableRead.Size; + u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].Pointer; token *Token = (token *)Pointer; string String = T_StringFromToken(Compiler->Text, *Token); AppendString(&List, PushFormat(Scratch.Arena, "Get Global: %S\n", String), Scratch.Arena); @@ -457,7 +645,9 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by case S_Op_SetGlobal: { Data += 1; - u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + scene_variable_read VariableRead = S_ReadVariableLength(Data); + Data += VariableRead.Size; + u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].Pointer; token *Token = (token *)Pointer; string String = T_StringFromToken(Compiler->Text, *Token); AppendString(&List, PushFormat(Scratch.Arena, "Set Global: %S\n", String), Scratch.Arena); @@ -468,7 +658,9 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by if(*Data & S_Op_LineEntry) { Data += 1; - u64 Pointer = Compiler->FirstValueChunk->Values[S_ReadVariableLength(&Data)].Pointer; + scene_variable_read VariableRead = S_ReadVariableLength(Data); + Data += VariableRead.Size; + u64 Pointer = Compiler->FirstValueChunk->Values[VariableRead.Value].Pointer; token *Token = (token *)Pointer; string String = Substring(Compiler->Text, Pad(Token->Range, -1)); AppendString(&List, PushFormat(Scratch.Arena, "Line Entry: %S\n", String), Scratch.Arena); @@ -476,6 +668,7 @@ static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_by else { AppendString(&List, StrLit("Unknown Op\n"), Scratch.Arena); + Data += 1; } } break; } @@ -539,9 +732,6 @@ static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text) temporary_memory Scratch = GetScratch(&Arena, 1); tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text, T_IsIrregular); - // sixten(TODO): append token errors - - //- sixten: tokens -> bytecode scene_compiler Compiler = {}; { Compiler.Arena = Scratch.Arena; @@ -550,12 +740,26 @@ static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text) Compiler.TokensEnd = Compiler.TokensBegin+TokenizeResult.Tokens.Count; Compiler.At = Compiler.TokensBegin; }; + S_SetEmissionTarget(&Compiler, S_RawEmissionTarget(Compiler.Arena, &Compiler.GlobalScope)); - S_SetEmissionTarget(&Compiler, StrLit("")); - - for(;Compiler.At < Compiler.TokensEnd;) + //- sixten: append tokenization errors + b32 FoundTokenizationError = false; + for(tokenizer_message *Message = TokenizeResult.Messages.First; Message != 0; Message = Message->Next) { - S_ParseTopLevelDeclaration(&Compiler); + if(Message->Kind == T_MessageKind_Error) + { + S_ParseError(&Compiler, (char *)Message->String.Data); + FoundTokenizationError = true; + } + } + + //- sixten: tokens -> bytecode + if(!FoundTokenizationError) + { + for(;Compiler.At < Compiler.TokensEnd;) + { + S_ParseTopLevelDeclaration(&Compiler); + } } //- sixten: bake compiled chunks @@ -579,14 +783,252 @@ static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text) scene_value *Dest = Result.Values; for(scene_value_chunk *Chunk = Compiler.FirstValueChunk; Chunk != 0; Chunk = Chunk->Next) { - Copy(Dest, Chunk->Values, Chunk->Count); + Copy(Dest, Chunk->Values, Chunk->Count*sizeof(scene_value)); Dest += Chunk->Count; } } + //- sixten: copy errors + for(scene_compile_error *Error = Compiler.Errors.First; Error != 0; Error = Error->Next) + { + scene_compile_error *New = PushStruct(Arena, scene_compile_error); + New->Message = PushString(Arena, Error->Message); + New->Token = Error->Token; + QueuePush(Result.Errors.First, Result.Errors.Last, New); + } + + Result.Errors.Count = Compiler.Errors.Count; + // sixten(IMPORTANT): The text is assumed to remain in memory for the duration of the scene. Result.Source = Text; + Result.IsValid = !Compiler.EncounteredError; + ReleaseScratch(Scratch); return(Result); +} + +static compiled_scene S_CopyCompiledScene(memory_arena *Arena, compiled_scene *Compiled) +{ + compiled_scene Result = {}; + + Assert(Compiled->Errors.Count == 0); + + //- sixten: copy the procs + for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiled->Buckets); BucketIndex += 1) + { + scene_proc_bucket *SourceBucket = &Compiled->Buckets[BucketIndex]; + scene_proc_bucket *DestBucket = &Result.Buckets[BucketIndex]; + for(scene_proc *Proc = SourceBucket->First; Proc != 0; Proc = Proc->Next) + { + scene_proc *NewProc = PushStruct(Arena, scene_proc); + NewProc->Name = PushString(Arena, Proc->Name); + NewProc->Data = PushArray(Arena, u8, Proc->Count); + Copy(NewProc->Data, Proc->Data, Proc->Count); + NewProc->Count = Proc->Count; + QueuePush(DestBucket->First, DestBucket->Last, NewProc); + } + } + + //- sixten: copy the values + Result.Values = PushArray(Arena, scene_value, Compiled->ValueCount); + Copy(Result.Values, Compiled->Values, Compiled->ValueCount*sizeof(scene_value)); + Result.ValueCount = Compiled->ValueCount; + + //- sixten: copy the source + Result.Source = PushString(Arena, Compiled->Source); + + Result.IsValid = true;//Compiled->IsValid; + + return(Result); +} + +//////////////////////////////// +//~ sixten: Scene Runtime Functions +static void S_RuntimeError(scene_runtime *Runtime, string Message) +{ + scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error); + Error->Message = PushString(Runtime->ErrorArena, Message); + DLLInsertLast(Runtime->FirstError, Runtime->LastError, Error); +} + +static void S_RuntimeErrorF(scene_runtime *Runtime, char *Format, ...) +{ + va_list Arguments; + va_start(Arguments, Format); + + scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error); + Error->Message = PushFormatVariadic(Runtime->ErrorArena, Format, Arguments); + DLLInsertLast(Runtime->FirstError, Runtime->LastError, Error); + + va_end(Arguments); +} + +static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name) +{ + scene_proc *Result = 0; + u64 Hash = HashString(Name); + scene_proc_bucket *Bucket = &Compiled->Buckets[Hash%ArrayCount(Compiled->Buckets)]; + for(scene_proc *Proc = Bucket->First; Proc != 0; Proc = Proc->Next) + { + if(AreEqual(Proc->Name, Name)) + { + Result = Proc; + break; + } + } + return(Result); +} + +static scene_runtime_result S_Run(scene_runtime *Runtime, memory_arena *FrameArena, b32 AdvanceOnAwait) +{ + scene_runtime_result Result = {}; + compiled_scene *Compiled = &Runtime->Compiled; + + Assert(Runtime != 0); + Assert(Compiled->IsValid); + + if(Runtime->CurrentProc == 0) + { + Runtime->CurrentProc = S_FindProcByName(Compiled, StrLit("main")); + } + + if(Runtime->CurrentProc) + { + if(Runtime->IP < Runtime->CurrentProc->Count) + { + u8 *Data = Runtime->CurrentProc->Data; + + switch(Data[Runtime->IP]) + { + case S_Op_Jump: + { + Runtime->IP += 1; + scene_variable_read VariableRead = S_ReadVariableLength(&Data[Runtime->IP]); + Runtime->IP += VariableRead.Size; + scene_value Value = Compiled->Values[VariableRead.Value]; + if(Value.Kind == S_ValueKind_SourceRef) + { + string JumpDest = Substring(Compiled->Source, Value.SourceRef); + scene_proc *Dest = S_FindProcByName(Compiled, JumpDest); + if(Dest) + { + Runtime->CurrentProc = Dest; + Runtime->IP = 0; + } + else + { + S_RuntimeErrorF(Runtime, "Trying to jump to unknown proc: %S", JumpDest); + } + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving jump destination."); + } + } break; + + case S_Op_JumpClose: + { + Runtime->IP += 1; + scene_variable_read OffsetVariableRead = S_ReadVariableLength(&Data[Runtime->IP]); + scene_value OffsetValue = Compiled->Values[OffsetVariableRead.Value]; + if(OffsetValue.Kind == S_ValueKind_Offset) + { + Runtime->IP += OffsetValue.Offset; + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving offset."); + } + } break; + + case S_Op_AddBranch: + { + Runtime->IP += 1; + scene_variable_read BranchVariableRead = S_ReadVariableLength(&Data[Runtime->IP]); + scene_value BranchName = Compiled->Values[BranchVariableRead.Value]; + Runtime->IP += BranchVariableRead.Size; + if(BranchName.Kind == S_ValueKind_SourceRef) + { + scene_variable_read OffsetVariableRead = S_ReadVariableLength(&Data[Runtime->IP]); + scene_value Offset = Compiled->Values[OffsetVariableRead.Value]; + Runtime->IP += OffsetVariableRead.Size; + if(Offset.Kind == S_ValueKind_Offset) + { + branch_case *Branch = &Runtime->Branches[Runtime->BranchCount]; + Branch->Name = Substring(Compiled->Source, Pad(BranchName.SourceRef, -1)); + Branch->Offset = Offset.Offset; + Runtime->BranchCount += 1; + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving offset."); + } + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving branch name."); + } + } break; + + case S_Op_Halt: { Result.ReachedAwait = true; } break; + + case S_Op_AwaitInput: + { + if(AdvanceOnAwait) + { + Runtime->IP += 1; + } + Result.ReachedAwait = true; + } break; + + default: + { + if(Data[Runtime->IP] & S_Op_LineEntry) + { + textbox_action *Action = PushStructNoClear(FrameArena, textbox_action); + if(Data[Runtime->IP] & S_LineEntryFlag_NoClear) + { + Action->Kind = TextboxActionKind_Append; + } + else + { + Action->Kind = TextboxActionKind_Set; + } + + Runtime->IP += 1; + scene_variable_read VariableRead = S_ReadVariableLength(&Data[Runtime->IP]); + Runtime->IP += VariableRead.Size; + scene_value Value = Compiled->Values[VariableRead.Value]; + if(Value.Kind == S_ValueKind_SourceRef) + { + Action->String = Substring(Compiled->Source, Pad(Value.SourceRef, -1)); + QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action); + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving line entry."); + } + } + else + { + S_RuntimeErrorF(Runtime, "Unknown bytecode op: 0x%02x", Data[Runtime->IP]); + } + } break; + } + } + else + { + S_RuntimeErrorF(Runtime, "Reached end of proc."); + } + } + else + { + S_RuntimeErrorF(Runtime, "No main entry was found"); + } + + Result.HadError = !DLLIsEmpty(Runtime->FirstError); + + Runtime->LastResult = Result; + return(Result); } \ No newline at end of file diff --git a/code/vn_scene.h b/code/vn_scene.h index b41cf5e..40b7cb7 100644 --- a/code/vn_scene.h +++ b/code/vn_scene.h @@ -12,6 +12,20 @@ //////////////////////////////// //~ sixten: Scene Compilation Types +struct scene_compile_error +{ + scene_compile_error *Next; + string Message; + token Token; +}; + +struct scene_compile_error_list +{ + scene_compile_error *First; + scene_compile_error *Last; + s64 Count; +}; + typedef u64 scene_line_entry_flag; enum { @@ -44,6 +58,11 @@ enum scene_opcode S_Op_GetGlobal, S_Op_SetGlobal, + S_Op_Jump, + S_Op_JumpClose, + S_Op_AddBranch, + S_Op_Halt, + S_Op_AwaitInput, S_Op_LineEntry = 0x80, // sixten(NOTE): All opcoodes above are reserved. @@ -76,6 +95,8 @@ enum scene_value_kind S_ValueKind_Number, S_ValueKind_Boolean, S_ValueKind_Pointer, + S_ValueKind_SourceRef, + S_ValueKind_Offset, }; struct scene_value @@ -86,6 +107,8 @@ struct scene_value r64 Number; b32 Boolean; u64 Pointer; + range1_s64 SourceRef; + s64 Offset; }; }; @@ -96,6 +119,12 @@ struct scene_value_chunk scene_value Values[512]; }; +struct scene_variable_read +{ + u64 Value; + s64 Size; +}; + enum scene_precedence { S_Precedence_None, @@ -120,10 +149,37 @@ struct scene_parse_rule scene_precedence Precedence; }; +enum scene_emission_target_type +{ + S_EmissionTarget_Raw, + S_EmissionTarget_Named, +}; + +struct scene_emission_target +{ + memory_arena *Arena; + scene_annotated_bytecode_bucket *Bucket; + scene_emission_target_type Type; + string Name; +}; + +struct scene_branch_case +{ + scene_branch_case *Next; + token Name; + scene_annotated_bytecode_bucket Bucket; + scene_value *OffsetValue; + scene_value *EndOffsetValue; +}; + struct scene_compiler { memory_arena *Arena; + b32 InPanicMode; + b32 EncounteredError; + scene_compile_error_list Errors; + string Text; token *TokensBegin; token *TokensEnd; @@ -132,8 +188,8 @@ struct scene_compiler scene_annotated_bytecode_bucket GlobalScope; scene_annotated_bytecode_bucket ProcBuckets[32]; - scene_annotated_bytecode_bucket *CurrentBucket; - string CurrentName; + scene_emission_target TargetStack[16]; + s32 TargetStackIndex; scene_value_chunk *FirstValueChunk; scene_value_chunk *LastValueChunk; @@ -165,10 +221,71 @@ struct compiled_scene scene_value *Values; s64 ValueCount; string Source; + scene_compile_error_list Errors; + b32 IsValid; +}; + +//////////////////////////////// +//~ sixten: Scene Runtime Types +struct scene_runtime_error +{ + scene_runtime_error *Next; + scene_runtime_error *Prev; + string Message; +}; + +struct scene_runtime_result +{ + b32 HadError; + b32 ReachedAwait; +}; + +enum textbox_action_kind +{ + TextboxActionKind_Set, + TextboxActionKind_Append, +}; + +struct textbox_action +{ + textbox_action *Next; + textbox_action_kind Kind; + string String; +}; + +struct branch_case +{ + string Name; + s64 Offset; +}; + +struct scene_runtime +{ + compiled_scene Compiled; + + // sixten: runtime state + scene_proc *CurrentProc; + s64 IP; + + // sixten: errors + memory_arena *ErrorArena; + scene_runtime_error *FirstError; + scene_runtime_error *LastError; + + // sixten: branches + branch_case Branches[16]; + s64 BranchCount; + + // sixten: result + textbox_action *FirstTextboxAction; + textbox_action *LastTextboxAction; + branch_case *FirstBranchCase; + scene_runtime_result LastResult; }; //////////////////////////////// //~ sixten: Scene Compiler Functions + //- sixten: value helpers inline scene_value S_MakeNumber(r64 Value) { @@ -194,18 +311,56 @@ inline scene_value S_MakePointer(void *Value) return(Result); } +inline scene_value S_MakeSourceRef(token Token) +{ + scene_value Result; + Result.Kind = S_ValueKind_SourceRef; + Result.SourceRef = Token.Range; + return(Result); +} + +inline scene_value S_MakeOffset(s64 Offset) +{ + scene_value Result; + Result.Kind = S_ValueKind_Offset; + Result.Offset = Offset; + return(Result); +} + //- sixten: error messaging -static void S_ParseError(scene_compiler *Compiler, char *Message) { InvalidCodepath; } +static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset = -1); //- sixten: bytecode helpers static void S_EmitByte(scene_compiler *Compiler, u8 Byte); +static void S_EmitBucket(scene_compiler *Compiler, scene_annotated_bytecode_bucket *Bucket); static u64 S_MakeConstant(scene_compiler *Compiler, scene_value Value); -static void S_EmitVariableLength(scene_compiler *Compiler, u64 Value); -static u64 S_ReadVariableLength(u8 **BytePtr); +static s64 S_EmitVariableLength(scene_compiler *Compiler, u64 Value); +static scene_variable_read S_ReadVariableLength(u8 *Byte); static void S_EmitConstant(scene_compiler *Compiler, scene_value Value); -static void S_SetEmissionTarget(scene_compiler *Compiler, string Target); +static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target); +static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target); +static void S_PopEmissionTarget(scene_compiler *Compiler); static scene_annotated_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name); +inline scene_emission_target S_RawEmissionTarget(memory_arena *Arena, scene_annotated_bytecode_bucket *Bucket) +{ + scene_emission_target Target = {}; + Target.Arena = Arena; + Target.Bucket = Bucket; + Target.Type = S_EmissionTarget_Raw; + return(Target); +} + +inline scene_emission_target S_NamedEmissionTarget(memory_arena *Arena, scene_annotated_bytecode_bucket *Bucket, string Name) +{ + scene_emission_target Target = {}; + Target.Arena = Arena; + Target.Bucket = Bucket; + Target.Type = S_EmissionTarget_Named; + Target.Name = Name; + return(Target); +} + //- sixten: parsing helpers static void S_AdvanceCompiler(scene_compiler *Compiler); static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token); @@ -218,8 +373,11 @@ static void S_ParseProcedure(scene_compiler *Compiler); static void S_ParseDeclaration(scene_compiler *Compiler); static void S_ParseVariableDeclaration(scene_compiler *Compiler); static void S_ParseVariable(scene_compiler *Compiler, b32 CanAssign); -static void S_ParseNamedVariable(scene_compiler *Compiler, token *Token, b32 CanAssign); +static void S_ParseNamedVariable(scene_compiler *Compiler, token Token, b32 CanAssign); static void S_ParseLineEntry(scene_compiler *Compiler); +static void S_ParseJumpStatement(scene_compiler *Compiler); +static void S_ParseBranchStatement(scene_compiler *Compiler); +static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, memory_arena *Arena); static void S_ParseStatement(scene_compiler *Compiler); static void S_ParseExpression(scene_compiler *Compiler); static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign); @@ -232,9 +390,13 @@ static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precede //- sixten: debugging static string S_DisassembleBytecode(scene_compiler *Compiler, scene_annotated_bytecode_chunk *Chunk, memory_arena *Arena); -//- sixten: api +//- sixten: compilation static compiled_scene S_ScriptFromText(memory_arena *Arena, string Text); +static compiled_scene S_CopyCompiledScene(memory_arena *Arena, compiled_scene *Compiled); - +//////////////////////////////// +//~ sixten: Scene Runtime Functions +static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name); +static scene_runtime_result S_Run(scene_runtime *Runtime, memory_arena *FrameArena, b32 AdvanceOnAwait); #endif //VN_SCENE_H diff --git a/code/vn_scene_view.cpp b/code/vn_scene_view.cpp new file mode 100644 index 0000000..7405834 --- /dev/null +++ b/code/vn_scene_view.cpp @@ -0,0 +1,342 @@ +thread_local scene_view *ThreadLocal_SceneView = 0; + +static void SV_SetState(scene_view *View) +{ + ThreadLocal_SceneView = View; +} + +static scene_view *SV_GetState() +{ + return(ThreadLocal_SceneView); +} + +static void SV_SetCurrentSource(compiled_scene *Compiled) +{ + scene_view *SceneView = SV_GetState(); + + // sixten(TODO): extract runtime information required to seamlessly transition between compilations + + ArenaClear(SceneView->SceneArena); + SceneView->Runtime.Compiled = S_CopyCompiledScene(SceneView->SceneArena, Compiled); + SceneView->Runtime.IP = 0; +} + +static void SV_Init(scene_view *SceneView, memory_arena *TextboxArena) +{ + SV_SetState(SceneView); + + SceneView->SceneArena = ArenaAllocate(Gigabytes(1)); + SceneView->Runtime.ErrorArena = ArenaAllocate(Megabytes(1)); + + SceneView->Textbox.Capacity = 4096; + SceneView->Textbox.String.Data = PushArray(TextboxArena, u8, SceneView->Textbox.Capacity); + SceneView->Textbox.String.Count = 0; +} + +struct text_properties +{ + font_id Font; + r32 FontSize; + r32 LineHeight; +}; + +static void RenderAnimatedText(render_group *Group, glyph_atlas *Atlas, text_properties Properties, string Text, r32 CharsToRender, v2_r32 P, r32 WrapWidth) +{ + v2_r32 Offset = V2R32(0, 0); + + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + u8 *Byte = TextBegin; + + u8 *WordBegin = TextBegin; + u8 *WordEnd = WordBegin; + u8 *TrueWordEnd = WordBegin; + + string_decode LastVisibleDecode = {}; + + Byte = TextBegin; + for(;Byte<=TextEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); + + if(CharsToRender >= 1) + { + LastVisibleDecode = Decode; + } + + if(Decode.Codepoint == ' ' || Byte==TextEnd) + { + string TrueWord = MakeString(WordBegin, TrueWordEnd); + string Word = MakeString(WordBegin, WordEnd); + r32 WordWidth = CalculateRasterizedTextWidth(Atlas, Properties.Font, Properties.FontSize, TrueWord); + if(Offset.x+WordWidth > WrapWidth) + { + Offset.x = 0; + Offset.y += Properties.LineHeight; + } + + Offset.x += PushText(Group, Atlas, Properties.Font, P+Offset, Properties.FontSize, Color_White, Word); + + if(WordEnd != TrueWordEnd) + { + string LastChar = MakeString(WordEnd, LastVisibleDecode.Size); + r32 TransitionAmount = Mod(CharsToRender, 1); + Offset.y += (1-TransitionAmount)*5; + PushText(Group, Atlas, Properties.Font, P+Offset, Properties.FontSize, SetAlpha(Color_White, TransitionAmount), LastChar); + } + + WordBegin = TrueWordEnd; + + if(Byte==TextEnd || CharsToRender < 1) + { + break; + } + } + + Byte += Decode.Size; + TrueWordEnd += Decode.Size; + if(CharsToRender >= 1) + { + WordEnd += Decode.Size; + CharsToRender -= 1; + } + } +} + +static r32 CalculateGlobalScaleFromRootBox(ui_box *Box) +{ + v2 RenderDim = DimOfRange(Box->Rect); + r32 GlobalScale = SquareRoot(RenderDim.x*RenderDim.y)/45; + return(GlobalScale); +} + +struct scene_textbox_data +{ + textbox *Textbox; + ui_box *SceneViewBox; +}; + +UI_CUSTOM_DRAW_CALLBACK(BuildSceneTextboxDrawCallback) +{ + scene_textbox_data *TextboxData = (scene_textbox_data *)Data; + textbox *Textbox = TextboxData->Textbox; + + //- sixten: render textbox + v4 TopColor = V4(0, 0, 0, 0.8f); + v4 BottomColor = V4(0, 0, 0, 0.5f); + range2_r32 Dest = Range2R32(Box->Rect.Min, Box->Rect.Max); + PushQuad(Group, Dest, TopColor, TopColor, BottomColor, BottomColor, 0, 0, 0); + + //- sixten: render text + string Text = Textbox->String; + r32 CharsRevealed = Textbox->CharsRevealed; + r32 GlobalScale = CalculateGlobalScaleFromRootBox(TextboxData->SceneViewBox); + text_properties Properties = {}; + Properties.Font = Font_Japanese; + Properties.FontSize = GlobalScale; + Properties.LineHeight = GlobalScale*1.5f; + r32 Padding = 1.5f*GlobalScale; + v2 Offset = V2R32(Padding, Padding); + RenderAnimatedText(Group, Atlas, Properties, Text, CharsRevealed, Box->Rect.Min+Offset, DimOfRange(Box->Rect).x-2*Padding); +} + +// sixten(NOTE): This is most likely temporarary and just contains all the data required to render the scene. +struct scene_render_data +{ + render_handle BackgroundTexture; + scene_runtime *Runtime; +}; + +UI_CUSTOM_DRAW_CALLBACK(BuildSceneDrawCallback) +{ + scene_render_data *RenderData = (scene_render_data *)Data; + + //- sixten: render background + // sixten(TODO, but soon): Currently we add Box->Rect.Min to everything, but that should really be a transform + // on the render group. + v2 RenderDim = DimOfRange(Box->Rect); + range2_r32 Dest = Range2R32(Box->Rect.Min, RenderDim+Box->Rect.Min); + range2_r32 Source = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(RenderData->BackgroundTexture))); + PushTexturedQuad(Group, Dest, Source, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, RenderData->BackgroundTexture); +} + +static void BuildScene(scene_runtime *Runtime, render_handle BackgroundTexture, textbox *Textbox) +{ + UI_SetNextWidth(UI_Percent(1, 0)); + UI_SetNextHeight(UI_Percent(1, 0)); + UI_SetNextLayoutAxis(Axis2_Y); + ui_box *Box = UI_MakeBox(0, StrLit("Scene View")); + scene_render_data *Data = PushStruct(UI_FrameArena(), scene_render_data); + Data->BackgroundTexture = BackgroundTexture; + Data->Runtime = Runtime; + UI_EquipBoxCustomDrawCallback(Box, BuildSceneDrawCallback, Data); + + UI_Parent(Box) + { + UI_WidthFill UI_Height(UI_Percent(1, 0)) UI_Row() UI_FillPadding UI_Column() UI_FillPadding + { + b32 FoundOffset = false; + s64 Offset = 0; + for(s32 BranchIndex = 0; BranchIndex < Runtime->BranchCount; BranchIndex += 1) + { + branch_case *Branch = &Runtime->Branches[BranchIndex]; + if(UI_ButtonF("%S#%i", Branch->Name, BranchIndex).Clicked) + { + Offset = Branch->Offset; + FoundOffset = true; + } + } + if(FoundOffset) + { + Runtime->IP += 1+Offset; + Runtime->BranchCount = 0; + } + } + + UI_SetNextWidth(UI_Percent(1, 1)); + UI_SetNextHeight(UI_Percent(0.3, 1)); + ui_box *TextBox = UI_MakeBox(0, StrLit("Scene Textbox")); + scene_textbox_data *TextboxData = PushStruct(UI_FrameArena(), scene_textbox_data); + TextboxData->Textbox = Textbox; + TextboxData->SceneViewBox = Box; + UI_EquipBoxCustomDrawCallback(TextBox, BuildSceneTextboxDrawCallback, TextboxData); + } +} + +static void BuildErrorScreen(scene_runtime *Runtime, vn_input *Input) +{ + UI_SetNextLayoutAxis(Axis2_X); + UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("Container"))) + { + UI_Padding(UI_Em(3, 1)) UI_Width(UI_Percent(1, 0)) UI_Column() UI_Padding(UI_Em(3, 1)) + { + UI_Font(Font_Bold) UI_Size(UI_TextContent(0, 1), UI_TextContent(0, 1)) UI_FontSize(32) UI_LabelF("A runtime error has occurred"); + s64 ErrorIndex = 0; + for(scene_runtime_error *Error = Runtime->FirstError; Error != 0; Error = Error->Next, ErrorIndex += 1) + { + UI_Spacer(UI_Em(3, 1)); + UI_SetNextCornerRadius(3); + UI_Size(UI_Percent(1, 1), UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawDropShadow|UI_BoxFlag_DrawBorder, "%i", ErrorIndex)) + UI_Size(UI_TextContent(30, 1), UI_TextContent(30, 1)) + { + UI_LabelF("Message: %S", Error->Message); + } + } + UI_Spacer(UI_Em(3, 1)); + UI_Size(UI_Percent(1, 1), UI_Em(2, 1)) UI_Row() + UI_Width(UI_TextContent(30, 1)) UI_CornerRadius(4) + { + ui_signal IgnoreSignal = UI_ButtonF("Ignore"); + if(IgnoreSignal.Hovering) + { + UI_TooltipLabel(StrLit("Continue running the script, may lead to more errors."), UI_MouseP()); + } + if(IgnoreSignal.Clicked) + { + Runtime->FirstError = Runtime->LastError = 0; + ArenaClear(Runtime->ErrorArena); + } + + UI_Spacer(UI_Em(1, 1)); + + ui_signal RestartSignal = UI_ButtonF("Restart"); + if(RestartSignal.Hovering) + { + UI_TooltipLabel(StrLit("Restarts the script, may lose progress."), UI_MouseP()); + } + if(RestartSignal.Clicked) + { + Runtime->FirstError = Runtime->LastError = 0; + Runtime->IP = 0; + Runtime->CurrentProc = 0; + ArenaClear(Runtime->ErrorArena); + } + + UI_Spacer(UI_Em(1, 1)); + if(UI_ButtonF("Exit Program").Clicked) + { + Input->ExitRequested = true; + } + } + } + } +} + +static void SV_Update(memory_arena *FrameArena, vn_input *Input) +{ + scene_view *SceneView = SV_GetState(); + textbox *Textbox = &SceneView->Textbox; + scene_runtime *Runtime = &SceneView->Runtime; + platform_event_list *EventList = Input->EventList; + compiled_scene *Compiled = &Runtime->Compiled; + + if(Compiled && Compiled->IsValid) + { + b32 PlayerAction = (Platform_KeyPress(EventList, Key_Space)||Platform_KeyPress(EventList, Key_MouseLeft)); + + if(DLLIsEmpty(Runtime->FirstError)) + { + b32 AdvanceOnAwait = (Textbox->CharsRevealed >= Textbox->String.Count) && PlayerAction; + + for(;;) + { + scene_runtime_result RunResult = S_Run(Runtime, FrameArena, AdvanceOnAwait); + if(RunResult.ReachedAwait || RunResult.HadError) + { + break; + } + } + } + r32 CharsPerSecond = 15.0f;//35.0f; + Textbox->CharsRevealed += Input->dtForFrame*CharsPerSecond; + Textbox->CharsRevealed = Min(Textbox->CharsRevealed, (r32)Textbox->String.Count); + + if(Textbox->CharsRevealed < Textbox->String.Count && PlayerAction) + { + Textbox->CharsRevealed = Textbox->String.Count; + } + + //- sixten: apply the textbox actions + for(textbox_action *Action = Runtime->FirstTextboxAction; Action != 0; Action = Action->Next) + { + if(Action->Kind == TextboxActionKind_Set) + { + string ReplaceString = Action->String; + Textbox->String.Count = Min(ReplaceString.Count, Textbox->Capacity); + Copy(Textbox->String.Data, ReplaceString.Data, Textbox->String.Count); + Textbox->CharsRevealed = 0; + } + else if(Action->Kind == TextboxActionKind_Append) + { + string Addend = Action->String; + Textbox->CharsRevealed = Textbox->String.Count; + s64 NewCount = Min(Textbox->String.Count+Addend.Count, Textbox->Capacity-1); + Copy(Textbox->String.Data+Textbox->String.Count, Action->String.Data, NewCount-Textbox->String.Count); + Textbox->String.Count = NewCount; + } + else + { + InvalidCodepath; + } + } + Runtime->FirstTextboxAction = Runtime->LastTextboxAction = 0; + } +} + +static void SV_BuildSceneView(vn_input *Input) +{ + scene_view *SceneView = SV_GetState(); + scene_runtime_result LastRun = SceneView->Runtime.LastResult; + if(LastRun.HadError) + { + BuildErrorScreen(&SceneView->Runtime, Input); + } + else if(SceneView->Runtime.Compiled.IsValid) + { + BuildScene(&SceneView->Runtime, SceneView->BackgroundTexture, &SceneView->Textbox); + } + else + { + UI_LabelF("Invalid source"); + } +} \ No newline at end of file diff --git a/code/vn_scene_view.h b/code/vn_scene_view.h new file mode 100644 index 0000000..9de2483 --- /dev/null +++ b/code/vn_scene_view.h @@ -0,0 +1,28 @@ +/* date = August 14th 2023 5:00 pm */ + +#ifndef VN_SCENE_VIEW_H +#define VN_SCENE_VIEW_H + +struct textbox +{ + string String; + s64 Capacity; + r32 CharsRevealed; +}; + +struct scene_view +{ + memory_arena *SceneArena; + + scene_runtime Runtime; + textbox Textbox; + render_handle BackgroundTexture; +}; + +static void SV_SetState(scene_view *View); +static scene_view *SV_GetState(); +static void SV_SetCurrentSource(compiled_scene *Compiled); +static void SV_Init(scene_view *View, memory_arena *TextboxArena); +static void SV_BuildSceneView(vn_input *Input); + +#endif //VN_SCENE_VIEW_H diff --git a/code/vn_text_op.h b/code/vn_text_op.h index 3c57861..ca754ba 100644 --- a/code/vn_text_op.h +++ b/code/vn_text_op.h @@ -26,8 +26,10 @@ enum TextActionFlag_Delete = (1<<2), TextActionFlag_ZeroDeltaWithSelection = (1<<3), TextActionFlag_DeltaPicksSelectionSide = (1<<4), - TextActionFlag_OperateOnLine = (1<<5), - TextActionFlag_StopOnNewline = (1<<6), + TextActionFlag_Copy = (1<<5), + TextActionFlag_Paste = (1<<6), + TextActionFlag_OperateOnLine = (1<<7), + TextActionFlag_StopOnNewline = (1<<8), TextActionFlag_EmptyLineScan = TextActionFlag_WordScan, }; @@ -96,6 +98,21 @@ static text_action SingleLineTextActionFromEvent(platform_event *Event) Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection; } break; + case Key_C: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Copy; + } break; + + case Key_V: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Paste; + } break; + + case Key_X: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Copy|TextActionFlag_Delete; + } break; + default: {} break; } } @@ -171,6 +188,21 @@ static text_action MultiLineTextActionFromEvent(platform_event *Event) Action.Flags |= TextActionFlag_Delete|TextActionFlag_ZeroDeltaWithSelection; } break; + case Key_C: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Copy; + } break; + + case Key_V: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Paste; + } break; + + case Key_X: if(Event->Modifiers & PlatformModifier_Ctrl) + { + Action.Flags |= TextActionFlag_Copy|TextActionFlag_Delete; + } break; + default: {} break; } } @@ -308,6 +340,25 @@ static s64 WordScan(string String, s64 Index, s64 Delta) return(Result); } +static b32 StringIsWhitespace(string String) +{ + b32 Result = true; + + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + + for(u8 *Char = StringBegin; Char < StringEnd; Char += 1) + { + if(!IsWhitespace(*Char) && *Char != '\0') + { + Result = false; + break; + } + } + + return(Result); +} + static text_op TextOpFromAction(memory_arena *Arena, string String, text_edit_state *State, text_action *Action, range1_s64_array *Lines = 0, s64 LastColumnIndex = 0) @@ -319,17 +370,14 @@ static text_op TextOpFromAction(memory_arena *Arena, string String, Op.Range = Range1S64(0, 0); Op.ReplaceString = StrLit(""); + //- sixten: navtigation s64 Delta = 0; if(Action->Flags & TextActionFlag_OperateOnLine) { - if(Action->Flags & TextActionFlag_EmptyLineScan) + //- sixten: determine what line we are on. + s64 LineIndex = 0; + s64 ColumnIndex = 0; { - } - else - { - //- sixten: determine what line we are on. - s64 LineIndex = 0; - s64 ColumnIndex = 0; u8 *TextBegin = String.Data; u8 *TextEnd = TextBegin + State->Cursor; u8 *Char = TextBegin; @@ -342,24 +390,86 @@ static text_op TextOpFromAction(memory_arena *Arena, string String, ColumnIndex = 0; } } - u64 ColumnOffset = Max(LastColumnIndex - ColumnIndex, 0LLU); + } + + s64 LineDelta = Action->Delta; + + if(Action->Flags & TextActionFlag_EmptyLineScan) + { + // sixten: if the line we start on is blank, we want to make sure that there has been a text containing non-whitespace before stopping. + b32 IgnoreFirstWhitespace = StringIsWhitespace(Substring(String, Lines->Ranges[LineIndex])); - //- sixten: check that the line we are trying to access is inbouse, else just go to the start or end of the text. - if(InRange(Range1S64(0, Lines->Count), LineIndex + Action->Delta)) + for(;0 <= LineIndex && LineIndex < Lines->Count; LineIndex += Action->Delta) { - Delta = Lines->Ranges[LineIndex+Action->Delta].Min - Lines->Ranges[LineIndex].Min + ColumnOffset; - if(!InRange(Lines->Ranges[LineIndex+Action->Delta], State->Cursor + Delta)) + string Line = Substring(String, Lines->Ranges[LineIndex+Action->Delta]); + if(StringIsWhitespace(Line)) { - Delta = Lines->Ranges[LineIndex+Action->Delta].Max - State->Cursor - 1; + if(!IgnoreFirstWhitespace) + { + break; + } } - } - else - { - s64 TempDelta = (Action->Delta > 0)?S64_Max:S64_Min; - Delta = CodepointScan(String, State->Cursor, TempDelta, Action->Flags & TextActionFlag_StopOnNewline); + else + { + IgnoreFirstWhitespace = false; + } + +#if 0 + u8 *LineBegin = Line.Data; + u8 *LineEnd = LineBegin+Line.Count; + u8 *Char = (Action->Delta > 0)?LineBegin:LineEnd; + + if(Action->Delta > 0) + { + for(;Char <= LineEnd; Char += 1) + { + if(Char == LineEnd) + { + goto FoundLine; + } + if(!IsWhitespace(*Char)) + { + break; + } + } + } + else if(Action->Delta < 0) + { + for(;LineBegin <= Char; Char -= 1) + { + if(!IsWhitespace(*Char)) + { + break; + } + + if(Char == LineBegin) + { + goto FoundLine; + } + } + } +#endif } } + + u64 ColumnOffset = Max(LastColumnIndex - ColumnIndex, 0LLU); + + //- sixten: check that the line we are trying to access is inbounds, else just go to the start or end of the text. + if(InRange(Range1S64(0, Lines->Count), LineIndex + LineDelta)) + { + Delta = Lines->Ranges[LineIndex+LineDelta].Min - Lines->Ranges[LineIndex].Min + ColumnOffset; + if(!InRange(Lines->Ranges[LineIndex+LineDelta], State->Cursor + Delta)) + { + Delta = Lines->Ranges[LineIndex+LineDelta].Max - State->Cursor - 1; + } + } + else + { + s64 TempDelta = (LineDelta > 0)?S64_Max:S64_Min; + Delta = CodepointScan(String, State->Cursor, TempDelta, Action->Flags & TextActionFlag_StopOnNewline); + } } + else { if(Action->Flags & TextActionFlag_WordScan) @@ -391,9 +501,15 @@ static text_op TextOpFromAction(memory_arena *Arena, string String, Op.NewCursor = Minimum(State->Cursor, State->Mark); } } - else + + Op.NewCursor = State->Cursor + Delta; + + //- sixten: post-navigation + + if(Action->Flags & TextActionFlag_Copy) { - Op.NewCursor = State->Cursor + Delta; + string CopyString = Substring(String, Range1S64(Op.NewCursor, Op.NewMark)); + Platform.SetClipboard(CopyString); } if(Action->Flags & TextActionFlag_Delete) @@ -417,6 +533,12 @@ static text_op TextOpFromAction(memory_arena *Arena, string String, Op.Range = Range1S64(State->Cursor, State->Mark); } } + else if(Action->Flags & TextActionFlag_Paste) + { + Op.ReplaceString = Platform.GetClipboard(Arena); + Op.Range = Range1S64(State->Cursor, State->Cursor); + Op.NewCursor += Op.ReplaceString.Count; + } if(!(Action->Flags & TextActionFlag_KeepMark)) { diff --git a/code/vn_tokenizer.cpp b/code/vn_tokenizer.cpp index 0f91b15..e0721b3 100644 --- a/code/vn_tokenizer.cpp +++ b/code/vn_tokenizer.cpp @@ -1,6 +1,6 @@ - //////////////////////////////// //~ sixten: Token Type Functions + static string T_StringFromToken(string Text, token Token) { string Result = Substring(Text, Token.Range); @@ -60,6 +60,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Text, toke u8 *TextStart = Text.Data; u8 *TextEnd = TextStart + Text.Count; u8 *Byte = TextStart; + s64 Line = 0; //- sixten: scan string & produce tokens for(;Byte < TextEnd;) @@ -91,6 +92,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Text, toke TokenKind = TokenKind_Newline; TokenStart = Byte; TokenEnd = Byte + 1; + Line += 1; Byte += 1; } @@ -284,7 +286,7 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Text, toke //- sixten: push token if(TokenKind != 0 && (!ExcludeFilter || (ExcludeFilter && !ExcludeFilter(TokenKind))) && TokenStart != 0 && TokenEnd > TokenStart) { - token Token = {TokenKind, {TokenStart - TextStart, TokenEnd - TextStart}}; + token Token = {TokenKind, {TokenStart - TextStart, TokenEnd - TextStart}, Line}; T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096); } diff --git a/code/vn_tokenizer.h b/code/vn_tokenizer.h index b985ea2..86f669c 100644 --- a/code/vn_tokenizer.h +++ b/code/vn_tokenizer.h @@ -70,21 +70,11 @@ enum token_kind typedef b32 tokenizer_filter_function(token_kind Kind); -inline b32 T_IsComment(token_kind Kind) { return(Kind == TokenKind_Comment); } -inline b32 T_IsWhitespace(token_kind Kind) { return(Kind == TokenKind_Whitespace || - Kind == TokenKind_Newline); } -inline b32 T_IsIrregular(token_kind Kind) { return(T_IsComment(Kind) || - T_IsWhitespace(Kind)); } -inline b32 T_IsRegular(token_kind Kind) { return(!T_IsIrregular(Kind)); } -inline b32 T_IsInvalid(token_kind Kind) { return(Kind == TokenKind_None || - Kind == TokenKind_BrokenComment || - Kind == TokenKind_BrokenStringLiteral || - Kind == TokenKind_BadCharacter); } - struct token { token_kind Kind; range1_s64 Range; + s64 Line; }; struct token_chunk_node @@ -141,6 +131,19 @@ struct tokenize_result tokenizer_message_list Messages; }; +//////////////////////////////// +//~ sixten: Token Filter Helper Functions +inline b32 T_IsComment(token_kind Kind) { return(Kind == TokenKind_Comment); } +inline b32 T_IsWhitespace(token_kind Kind) { return(Kind == TokenKind_Whitespace || + Kind == TokenKind_Newline); } +inline b32 T_IsIrregular(token_kind Kind) { return(T_IsComment(Kind) || + T_IsWhitespace(Kind)); } +inline b32 T_IsRegular(token_kind Kind) { return(!T_IsIrregular(Kind)); } +inline b32 T_IsInvalid(token_kind Kind) { return(Kind == TokenKind_None || + Kind == TokenKind_BrokenComment || + Kind == TokenKind_BrokenStringLiteral || + Kind == TokenKind_BadCharacter); } + //////////////////////////////// //~ sixten: Token Type Functions static string T_StringFromToken(string Text, token Token); diff --git a/code/vn_ui.cpp b/code/vn_ui.cpp index a1525a2..bb27173 100644 --- a/code/vn_ui.cpp +++ b/code/vn_ui.cpp @@ -60,7 +60,13 @@ inline platform_event_list *UI_EventList(void) return(UI->EventList); } -inline v2 UI_MouseP(void) +inline memory_arena *UI_FrameArena(void) +{ + ui *UI = UI_GetState(); + return(UI->FrameArena); +} + +inline v2_r32 UI_MouseP(void) { ui *UI = UI_GetState(); return(UI->MouseP); @@ -322,6 +328,17 @@ inline void UI_SetNextHot(ui_key Key) } } +static void UI_EquipBoxText(ui_box *Box, string String) +{ + Box->String = PushString(UI_FrameArena(), String); +} + +static void UI_EquipBoxCustomDrawCallback(ui_box *Box, ui_custom_draw_callback *DrawCallback, void *Data) +{ + Box->DrawCallback = DrawCallback; + Box->DrawCallbackData = Data; +} + // sixten(NOTE): ClippingRect = Intersection(TrueClippingRect, Parent->Rect); static b32 UI_ChildrenContainsP(ui_box *Parent, v2 P, range2_r32 Clip) { @@ -473,6 +490,11 @@ static ui_signal UI_SignalFromBox(ui_box *Box) } } + if(AreEqual(UI->Hot, Box->Key)) + { + Platform.SetCursor(Box->HoverCursor); + } + Signal.MouseP = UI->MouseP; Signal.DragDelta = UI->MouseP - UI->DragStartP; Signal.Box = Box; @@ -784,8 +806,8 @@ static void UI_LayoutBox(ui_box *Box) { if(Child->Flags & UI_BoxFlag_AnimatePosition) { - Child->ApproachingRelativeP.x = AnimationCurve_AnimateValueF(Child->ComputedRelativeP.x, Child->ComputedRelativeP.x, 0.1, "Box P.X %p", Child); - Child->ApproachingRelativeP.y = AnimationCurve_AnimateValueF(Child->ComputedRelativeP.y, Child->ComputedRelativeP.y, 0.1, "Box P.Y %p", Child); + Child->ApproachingRelativeP.x = AC_AnimateValueF(Child->ComputedRelativeP.x, Child->ComputedRelativeP.x, 0.1, "Box P.X %p", Child); + Child->ApproachingRelativeP.y = AC_AnimateValueF(Child->ComputedRelativeP.y, Child->ComputedRelativeP.y, 0.1, "Box P.Y %p", Child); Child->Rect.Min = Box->Rect.Min + Child->ApproachingRelativeP + Box->Offset; Child->Rect.Max = Child->Rect.Min + Child->ComputedDim; @@ -824,6 +846,7 @@ static void UI_BeginBuild(v2 ScreenDim) UI_PushFontSize(15.0f); UI_PushOffsetX(0); UI_PushOffsetY(0); + UI_PushHoverCursor(PlatformCursor_Arrow); UI->RootNode = UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("UI Root Node")); UI->Stacks.ParentStack[0] = UI->RootNode; @@ -862,6 +885,7 @@ static void UI_EndBuild(void) UI_PopFontSize(); UI_PopOffsetX(); UI_PopOffsetY(); + UI_PopHoverCursor(); UI_LayoutBox(UI->RootNode); } diff --git a/code/vn_ui.h b/code/vn_ui.h index 8fdeb65..c953806 100644 --- a/code/vn_ui.h +++ b/code/vn_ui.h @@ -90,6 +90,7 @@ struct ui_box font_id Font; r32 FontSize; v2 Offset; + platform_cursor HoverCursor; ui_custom_draw_callback *DrawCallback; void *DrawCallbackData; @@ -159,11 +160,10 @@ struct ui //- sixten: State management inline void UI_SetState(ui *UI); inline ui *UI_GetState(void); - inline ui_key UI_GetHot(void); inline ui_key UI_GetActive(void); - inline platform_event_list *UI_EventList(void); +inline memory_arena *UI_FrameArena(void); inline v2 UI_MouseP(void); inline glyph_atlas *UI_GlyphAtlas(void); @@ -190,6 +190,8 @@ static ui_box *UI_GetBoxByKey(ui *UI, ui_key Key); //- sixten: Box creation static ui_box *UI_MakeBox(ui_box_flags Flags, string String); static ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...); +static void UI_EquipBoxText(ui_box *Box, string String); +static void UI_EquipBoxCustomDrawCallback(ui_box *Box, ui_custom_draw_callback *DrawCallback, void *Data); //- sixten: User interaction static ui_signal UI_SignalFromBox(ui_box *Box); diff --git a/code/vn_ui.md b/code/vn_ui.md index ed324af..181badc 100644 --- a/code/vn_ui.md +++ b/code/vn_ui.md @@ -1,21 +1,22 @@ @table(Name, Type, BoxName) UIStyleStacks: { - { Parent `ui_box *` `Parent` } - { Width `ui_size` `SemanticSize[Axis2_X]` } - { Height `ui_size` `SemanticSize[Axis2_Y]` } - { FixedX `r32` `FixedP.E[Axis2_X]` } - { FixedY `r32` `FixedP.E[Axis2_Y]` } - { TextColor `v4` `TextColor` } - { BackgroundColor `v4` `BackgroundColor` } - { BorderColor `v4` `BorderColor` } - { BorderThickness `r32` `BorderThickness` } - { LayoutAxis `axis2` `LayoutAxis` } - { CornerRadius `r32` `CornerRadius` } - { Font `font_id` `Font` } - { FontSize `r32` `FontSize` } - { OffsetX `r32` `Offset.x` } - { OffsetY `r32` `Offset.y` } + { Parent `ui_box *` `Parent` } + { Width `ui_size` `SemanticSize[Axis2_X]` } + { Height `ui_size` `SemanticSize[Axis2_Y]` } + { FixedX `r32` `FixedP.E[Axis2_X]` } + { FixedY `r32` `FixedP.E[Axis2_Y]` } + { TextColor `v4` `TextColor` } + { BackgroundColor `v4` `BackgroundColor` } + { BorderColor `v4` `BorderColor` } + { BorderThickness `r32` `BorderThickness` } + { LayoutAxis `axis2` `LayoutAxis` } + { CornerRadius `r32` `CornerRadius` } + { Font `font_id` `Font` } + { FontSize `r32` `FontSize` } + { OffsetX `r32` `Offset.x` } + { OffsetY `r32` `Offset.y` } + { HoverCursor `platform_cursor` `HoverCursor` } } @table_gen diff --git a/code/vn_ui_utils.cpp b/code/vn_ui_utils.cpp index bc1e714..25615ac 100644 --- a/code/vn_ui_utils.cpp +++ b/code/vn_ui_utils.cpp @@ -86,9 +86,9 @@ static ui_box *UI_NamedSpacerF(ui_size Size, char *Format, ...) return(Box); } -static void UI_Spacer(ui_size Size) +static ui_box *UI_Spacer(ui_size Size) { - UI_NamedSpacer(Size, StrLit("")); + return(UI_NamedSpacer(Size, StrLit(""))); } //- sixten: Scrollable regions @@ -119,6 +119,7 @@ static ui_box *UI_LabelF(char *Format, ...) static ui_signal UI_Button(string String) { + UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| UI_BoxFlag_DrawBackground| UI_BoxFlag_DrawBorder| @@ -134,6 +135,8 @@ static ui_signal UI_ButtonF(char *Format, ...) { temporary_memory Scratch = GetScratch(0, 0); + UI_SetNextHoverCursor(PlatformCursor_Hand); + va_list Arguments; va_start(Arguments, Format); string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); @@ -157,11 +160,11 @@ static ui_signal UI_Checkbox(b32 *Checked, string String) { UI_SetNextSize(UI_ChildrenSum(1, 1), UI_ChildrenSum(1, 1)); UI_SetNextLayoutAxis(Axis2_X); - + UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *ContainerBox = UI_MakeBox(UI_BoxFlag_Clickable, String); UI_Parent(ContainerBox) { - r32 OpacityTransition = AnimationCurve_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked); + r32 OpacityTransition = AC_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked); v4 TextColor = UI_TopTextColor(); TextColor.a = OpacityTransition; @@ -173,10 +176,6 @@ static ui_signal UI_Checkbox(b32 *Checked, string String) } ui_signal Signal = UI_SignalFromBox(ContainerBox); - if(Signal.Hovering) - { - Platform.SetCursor(PlatformCursor_Hand); - } if(Signal.Pressed) { @@ -223,41 +222,41 @@ static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset) return(Signal); } -static void UI_ScrollBegin(r32 *X, r32 *Y, string Name) +static void UI_ScrollBegin(r32 *X, r32 *Y, ui_box_flags Flags, string Name) { - u32 Flags = 0; b32 AllowOnX = (X != 0); b32 AllowOnY = (Y != 0); - UI_RowBegin(UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder, Name); + UI_RowBegin(Flags, Name); { UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); UI_ColumnBegin(); { + u32 ScrollFlags = 0; if(AllowOnX) { - Flags |= UI_BoxFlag_OverflowX; + ScrollFlags |= UI_BoxFlag_OverflowX; UI_SetNextOffsetX(-(*X)); } if(AllowOnY) { - Flags |= UI_BoxFlag_OverflowY; + ScrollFlags |= UI_BoxFlag_OverflowY; UI_SetNextOffsetY(-(*Y)); } UI_SetNextSize(AllowOnX?UI_ChildrenSum(1, 1):UI_Percent(1, 0), AllowOnY?UI_ChildrenSum(1, 1):UI_Percent(1, 0)); - ui_box *ScrollableBox = UI_MakeBox(Flags, StrLit("Scrollable Box")); + ui_box *ScrollableBox = UI_MakeBox(ScrollFlags, StrLit("Scrollable Box")); UI_PushParent(ScrollableBox); } } } -static void UI_ScrollEnd(r32 *X, r32 *Y) +static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags, string Name) { b32 AllowOnX = (X != 0); b32 AllowOnY = (Y != 0); @@ -294,7 +293,7 @@ static void UI_ScrollEnd(r32 *X, r32 *Y) r32 StartOffset = UI_GetDragR32(); r32 EndOffset = StartOffset + Signal.DragDelta.x/ScrollScale; - AnimationCurve_AnimateValueDirect(EndOffset, 0.2, X); + AC_AnimateValueDirect(EndOffset, 0.2, X); } } } @@ -332,7 +331,7 @@ static void UI_ScrollEnd(r32 *X, r32 *Y) r32 StartOffset = UI_GetDragR32(); r32 EndOffset = StartOffset + Signal.DragDelta.y/ScrollScale; - AnimationCurve_AnimateValueDirect(EndOffset, 0.2, Y); + AC_AnimateValueDirect(EndOffset, 0.2, Y); } } } @@ -348,4 +347,62 @@ static void UI_ScrollEnd(r32 *X, r32 *Y) } } UI_RowEnd(); +} + +static r32 UI_Slider(r32 Value, range1_r32 Range) +{ + r32 Result = Value; + UI_Column() + { + UI_Spacer(UI_Em(1, 1)); + UI_Height(UI_Em(1, 1)) UI_Row() + { + UI_SetNextWidth(UI_Em(20, 1)); + UI_SetNextCornerRadius(UI_TopFontSize()*0.5f); + ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Scrollable"); + UI_Parent(Container) + { + UI_SetNextCornerRadius(UI_TopFontSize()*0.5f); + UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); + UI_SetNextFixedX((DimOfRange(Container->Rect).x-UI_TopFontSize())*(Value-Range.Min)/DimOfRange(Range)); + ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground| + UI_BoxFlag_DrawBorder| + UI_BoxFlag_HotAnimation| + UI_BoxFlag_ActiveAnimation| + UI_BoxFlag_Clickable| + UI_BoxFlag_FloatingX| + 0, "Dragable"); + ui_signal Signal = UI_SignalFromBox(Box); + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragR32(Value); + } + + r32 StartT = UI_GetDragR32(); + r32 EndT = StartT + Signal.DragDelta.x/(DimOfRange(Container->Rect).x-UI_TopFontSize()); + Result = EndT*DimOfRange(Range)+Range.Min; + } + } + } + } + return(Result); +} + +static void UI_TooltipLabel(string Label, v2 P) +{ + UI_Tooltip + { + UI_SetNextSize(UI_TextContent(7, 1), UI_TextContent(3, 1)); + UI_CornerRadius(4); + UI_SetNextFixedP(P+V2R32(15.0f, 0.0f)); + UI_MakeBox(UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawText | + UI_BoxFlag_DrawDropShadow | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, + Label); + } } \ No newline at end of file diff --git a/code/vn_ui_utils.h b/code/vn_ui_utils.h index 4915f37..3bc1bf7 100644 --- a/code/vn_ui_utils.h +++ b/code/vn_ui_utils.h @@ -19,6 +19,9 @@ inline void UI_SetNextAxisSize(axis2 Axis, ui_size Size); #define UI_AxisSize(Axis, Size) DeferLoop(UI_PushAxisSize(Axis, Size), UI_PopAxisSize(Axis)) +#define UI_WidthFill UI_Width(UI_Percent(1, 0)) +#define UI_HeightFill UI_Height(UI_Percent(1, 0)) + #define UI_Size(Width, Height) UI_Width(Width) UI_Height(Height) #define UI_PushSize(Width, Height) UI_PushWidth(Width); UI_PushHeight(Height) #define UI_PopSize() UI_PopWidth(); UI_PopHeight() @@ -30,7 +33,7 @@ inline void UI_SetNextAxisSize(axis2 Axis, ui_size Size); #define UI_SetNextOffset(x, y) UI_SetNextOffsetX(x); UI_SetNextOffsetY(y) #define UI_FixedP(Value) UI_FixedX(Value.x) UI_FixedY(Value.y) -#define UI_SetNextFixedP(Value) UI_SetNextFixedX(Value.x); UI_SetNextFixedY(Value.y) +#define UI_SetNextFixedP(Value) UI_SetNextFixedX((Value).x); UI_SetNextFixedY((Value).y) //- sixten: Spacing static ui_box *UI_NamedSpacer(ui_size Size, string String); @@ -38,9 +41,11 @@ static ui_box *UI_NamedSpacerF(ui_size Size, char *Format, ...); #define UI_Padding(Size) DeferLoop(UI_Spacer(Size), UI_Spacer(Size)) +#define UI_FillPadding UI_Padding(UI_Percent(1, 0)) + //- sixten: Scrollable regions -static void UI_ScrollBegin(r32 *X, r32 *Y, string Name = StrLit("Scrollable Region Container")); -static void UI_ScrollEnd(r32 *X, r32 *Y, string Name); +static void UI_ScrollBegin(r32 *X, r32 *Y, ui_box_flags Flags=UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder, string Name = StrLit("Scrollable Region Container")); +static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags=UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder, string Name = StrLit("Scrollable Region Container")); #define UI_Scroll(...) DeferLoop(UI_ScrollBegin(__VA_ARGS__), UI_ScrollEnd(__VA_ARGS__)) @@ -53,4 +58,8 @@ static ui_signal UI_ButtonF(char *Format, ...); static ui_signal UI_Checkbox(b32 *Checked, string String); +static r32 UI_Slider(r32 Value, range1_r32 Range); + +static void UI_TooltipLabel(string Label, v2 P); + #endif //VN_UI_UTILS_H diff --git a/code/vn_workspace.cpp b/code/vn_workspace.cpp index 442c5b0..44be67a 100644 --- a/code/vn_workspace.cpp +++ b/code/vn_workspace.cpp @@ -6,27 +6,40 @@ //- sixten: State management per_thread workspace *ThreadLocal_Workspace; -static void Workspace_SetState(workspace *Workspace) +static workspace_keybind Workspace_Keybinds[] = +{ + {Key_P, PlatformModifier_Ctrl, W_Command_SplitPanelHorizontal}, + {Key_L, PlatformModifier_Ctrl, W_Command_SplitPanelVertical}, + + {Key_O, PlatformModifier_Ctrl, W_Command_OpenView, W_View_TextEditor}, + {Key_T, PlatformModifier_Ctrl, W_Command_Test}, + +#if VN_INTERNAL + {Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, W_Command_ToggleRenderUIDebugRects}, +#endif +}; + +static void W_SetState(workspace *Workspace) { ThreadLocal_Workspace = Workspace; } -static workspace *Workspace_GetState(void) +static workspace *W_GetState(void) { return(ThreadLocal_Workspace); } -static memory_arena *Workspace_FrameArena(void) +static memory_arena *W_FrameArena(void) { return(ThreadLocal_Workspace->FrameArena); } //- sixten: Commands -static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0) +static void W_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0) { workspace_command *Result = 0; - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); if(Workspace->FirstFreeCommand) { @@ -44,9 +57,9 @@ static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument = 0) DLLInsertLast(Workspace->FirstCommand, Workspace->LastCommand, Result); } -static void Workspace_ProcessCommands(void) +static void W_ProcessCommands(void) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); workspace_command *Command = Workspace->FirstCommand; while(Command != 0) @@ -62,9 +75,9 @@ static void Workspace_ProcessCommands(void) } } -static void Workspace_ProcessKeyBinds() +static void W_ProcessKeyBinds() { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); platform_event_list *EventList = Workspace->EventList; @@ -75,13 +88,13 @@ static void Workspace_ProcessKeyBinds() if(Event->Type == PlatformEvent_Press) { for(s32 KeybindIndex = 0; - KeybindIndex < Workspace->KeybindCount; + KeybindIndex < ArrayCount(Workspace_Keybinds); ++KeybindIndex) { - workspace_keybind *Keybind = Workspace->Keybinds + KeybindIndex; + workspace_keybind *Keybind = Workspace_Keybinds + KeybindIndex; if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers)) { - Workspace_IssueCommand(Keybind->Command, Keybind->Argument); + W_IssueCommand(Keybind->Command, Keybind->Argument); } } } @@ -90,9 +103,9 @@ static void Workspace_ProcessKeyBinds() //- sixten: Builder code -static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu) +static ui_signal W_BuildToolbarButton(char *Text, toolbar_menu Menu) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); UI_SetNextWidth(UI_TextContent(20, 1)); UI_SetNextHeight(UI_Pixels(30, 1)); @@ -134,11 +147,12 @@ static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu) return(Signal); } -static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut) +static ui_signal W_BuildMenuItem(u32 Icon, char *Text, char *Shortcut) { temporary_memory Scratch = GetScratch(0, 0); UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawBorder | @@ -163,9 +177,9 @@ static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut) return(Signal); } -static void Workspace_BuildToolbar() +static void W_BuildToolbar() { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); UI_SetNextLayoutAxis(Axis2_X); UI_SetNextHeight(UI_Pixels(30, 1)); @@ -176,9 +190,9 @@ static void Workspace_BuildToolbar() UI_Parent(ToolbarBox) { - Workspace_BuildToolbarButton("Panel", ToolbarMenu_Panel); - Workspace_BuildToolbarButton("View", ToolbarMenu_View); - Workspace_BuildToolbarButton("Window", ToolbarMenu_Window); + W_BuildToolbarButton("Panel", ToolbarMenu_Panel); + W_BuildToolbarButton("View", ToolbarMenu_View); + W_BuildToolbarButton("Window", ToolbarMenu_Window); } if(Workspace->Menu != ToolbarMenu_None) @@ -206,14 +220,14 @@ static void Workspace_BuildToolbar() { if(Workspace->Menu == ToolbarMenu_Panel) { - if(Workspace_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked) + if(W_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked) { - Workspace_IssueCommand(Workspace_Command_SplitPanelHorizontal); + W_IssueCommand(W_Command_SplitPanelHorizontal); Workspace->Menu = ToolbarMenu_None; } - if(Workspace_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked) + if(W_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked) { - Workspace_IssueCommand(Workspace_Command_SplitPanelVertical); + W_IssueCommand(W_Command_SplitPanelVertical); Workspace->Menu = ToolbarMenu_None; } } @@ -225,32 +239,37 @@ static void Workspace_BuildToolbar() // commmands instead. (since we want all things that deals with major state changes to occur at a fixed // point in our programs execution) - if(Workspace_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked) + if(W_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked) { - Workspace_CreateNewView(Workspace_View_Startup, CurrentPanel); + W_CreateNewView(W_View_Startup, CurrentPanel); Workspace->Menu = ToolbarMenu_None; } - if(Workspace_BuildMenuItem(FontIcon_Pencil, "Script Editor", "Ctrl + O").Clicked) + if(W_BuildMenuItem(FontIcon_Pencil, "Script Editor", "Ctrl + O").Clicked) { - Workspace_CreateNewView(Workspace_View_TextEditor, CurrentPanel); + W_CreateNewView(W_View_TextEditor, CurrentPanel); Workspace->Menu = ToolbarMenu_None; } - if(Workspace_BuildMenuItem(FontIcon_Wrench, "Settings", "").Clicked) + if(W_BuildMenuItem(FontIcon_Terminal, "Scene View", "").Clicked) { - Workspace_CreateNewView(Workspace_View_Settings, CurrentPanel); + W_CreateNewView(W_View_SceneView, CurrentPanel); + Workspace->Menu = ToolbarMenu_None; + } + if(W_BuildMenuItem(FontIcon_Wrench, "Settings", "").Clicked) + { + W_CreateNewView(W_View_Settings, CurrentPanel); Workspace->Menu = ToolbarMenu_None; } } else if(Workspace->Menu == ToolbarMenu_Window) { - if(Workspace_BuildMenuItem(FontIcon_WindowMaximize, "ToggleFullscreen", "Alt + Enter").Clicked) + if(W_BuildMenuItem(FontIcon_WindowMaximize, "ToggleFullscreen", "Alt + Enter").Clicked) { Platform.ToggleFullscreen(); Workspace->Menu = ToolbarMenu_None; } } - AnimationCurve_AnimateValueDirect(1, 0.1, &Workspace->MenuTransition); + AC_AnimateValueDirect(1, 0.1, &Workspace->MenuTransition); } // sixten: Unless the mouse press was captured, we close the menu. @@ -262,11 +281,11 @@ static void Workspace_BuildToolbar() } //- sixten: Panels -static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent) +static workspace_panel *W_CreateNewPanel(workspace_panel *Parent) { workspace_panel *Result = 0; - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); if(DLLIsEmpty(Workspace->FirstFreePanel)) { @@ -285,9 +304,9 @@ static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent) return(Result); } -static void Workspace_DeletePanel(workspace_panel *Panel) +static void W_DeletePanel(workspace_panel *Panel) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); if(Workspace->CurrentPanel == Panel) { @@ -298,9 +317,9 @@ static void Workspace_DeletePanel(workspace_panel *Panel) DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, Panel); } -static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis) +static void W_SplitPanel(workspace_panel *Panel, axis2 Axis) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); if(Panel) { @@ -308,14 +327,14 @@ static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis) if(Parent && (Parent->SplitAxis == Axis)) { - workspace_panel *NewPanel = Workspace_CreateNewPanel(Parent); + workspace_panel *NewPanel = W_CreateNewPanel(Parent); NewPanel->PercentOfParent = Panel->PercentOfParent = Panel->PercentOfParent * 0.5; DLLInsert_NP(Parent->First, Parent->Last, Panel, NewPanel, Next, Prev); } else { - workspace_panel *NewPanel = Workspace_CreateNewPanel(Panel); + workspace_panel *NewPanel = W_CreateNewPanel(Panel); NewPanel->FirstView = Panel->FirstView; NewPanel->LastView = Panel->LastView; @@ -331,7 +350,7 @@ static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis) NewPanel->PercentOfParent = 0.5; DLLInsertLast(Panel->First, Panel->Last, NewPanel); - NewPanel = Workspace_CreateNewPanel(Panel); + NewPanel = W_CreateNewPanel(Panel); NewPanel->PercentOfParent = 0.5; DLLInsertLast(Panel->First, Panel->Last, NewPanel); @@ -348,36 +367,36 @@ static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis) } } -inline void Workspace_BeginDrag(workspace_drag_payload *Payload) +inline void W_BeginDrag(workspace_drag_payload *Payload) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); // sixten(TODO): Right now, if you spam-click a draggable item, you can trigger this // assertion. I don't know what I want to do about this at the moment, but I'm sure // future me will have a soulution at hand. ^.^ - Assert(Workspace->DragPayloadState == Workspace_DragPayload_Inactive); + Assert(Workspace->DragPayloadState == W_DragPayload_Inactive); Workspace->DragPayload = *Payload; - Workspace->DragPayloadState = Workspace_DragPayload_Active; + Workspace->DragPayloadState = W_DragPayload_Active; } -inline b32 Workspace_GetDragPayload(workspace_drag_payload *Dest) +inline b32 W_GetDragPayload(workspace_drag_payload *Dest) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); - b32 Result = (Workspace->DragPayloadState != Workspace_DragPayload_Inactive); + b32 Result = (Workspace->DragPayloadState != W_DragPayload_Inactive); *Dest = Workspace->DragPayload; return(Result); } -static void Workspace_BuildTabItem(workspace_panel *Panel, workspace_view *View) +static void W_BuildTabItem(workspace_panel *Panel, workspace_view *View) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); b32 ViewIsCurrent = (Panel->CurrentView == View); b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); - string Name = Workspace_GetViewName(View); + string Name = W_GetViewName(View); v4 BackgroundColor = ViewIsCurrent ? (PanelIsCurrent ? Theme_HighlightBorderColor : Theme_BorderColor) : ColorFromHex(0x353738FF); @@ -405,21 +424,16 @@ static void Workspace_BuildTabItem(workspace_panel *Panel, workspace_view *View) { UI_SetNextFont(Font_Icons); UI_SetNextSize(UI_TextContent(1, 1), UI_Percent(1, 1)); + UI_SetNextHoverCursor(PlatformCursor_Hand); + ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_DrawText|UI_BoxFlag_Clickable, "%U", FontIcon_Cancel); - CloseBox->TextColor = LinearBlend(TabBox->BackgroundColor, Color_Black, - 0.3 - CloseBox->HotTransition*0.8); + CloseBox->TextColor = LinearBlend(TabBox->BackgroundColor, Color_Black, 0.3 - CloseBox->HotTransition*0.8); ui_signal Signal = UI_SignalFromBox(CloseBox); if(Signal.Clicked) { - DLLRemove(Panel->FirstView, Panel->LastView, View); - if(ViewIsCurrent) - { - Panel->CurrentView = Panel->FirstView; - } - - // sixten(TODO): Issue a "DeleteView" command. + W_IssueCommand(W_Command_CloseView, PointerToU64(View)); } } } @@ -440,14 +454,14 @@ static void Workspace_BuildTabItem(workspace_panel *Panel, workspace_view *View) Payload.View = View; Payload.Key = TabBox->Key; - Workspace_BeginDrag(&Payload); + W_BeginDrag(&Payload); } } } -static void Workspace_BuildPanelHeader(workspace_panel *Panel) +static void W_BuildPanelHeader(workspace_panel *Panel) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); UI_SetNextLayoutAxis(Axis2_X); UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); @@ -460,7 +474,7 @@ static void Workspace_BuildPanelHeader(workspace_panel *Panel) View != 0; View = View->Next) { - Workspace_BuildTabItem(Panel, View); + W_BuildTabItem(Panel, View); } // sixten: Panel Close Button @@ -473,6 +487,7 @@ static void Workspace_BuildPanelHeader(workspace_panel *Panel) UI_SetNextBorderColor(ColorFromHex(0xA6514288)); UI_SetNextBackgroundColor(ColorFromHex(0xC24630BB)); UI_SetNextCornerRadius(4); + UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_HotAnimation | UI_BoxFlag_ActiveAnimation | @@ -485,7 +500,7 @@ static void Workspace_BuildPanelHeader(workspace_panel *Panel) ui_signal Signal = UI_SignalFromBox(CloseBox); if(Signal.Clicked) { - Workspace_IssueCommand(Workspace_Command_ClosePanel, PointerToU64(Panel)); + W_IssueCommand(W_Command_ClosePanel, PointerToU64(Panel)); } } @@ -493,9 +508,9 @@ static void Workspace_BuildPanelHeader(workspace_panel *Panel) } } -static void Workspace_BuildPanel(workspace_panel *Panel) +static void W_BuildPanel(workspace_panel *Panel) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); // sixten: Fill remaining percent of parent. workspace_panel *Parent = Panel->Parent; @@ -520,7 +535,7 @@ static void Workspace_BuildPanel(workspace_panel *Panel) { if(DLLIsEmpty(Panel->First)) { - Workspace_BuildPanelHeader(Panel); + W_BuildPanelHeader(Panel); // sixten: Main body { @@ -528,7 +543,7 @@ static void Workspace_BuildPanel(workspace_panel *Panel) UI_PushSize(UI_Percent(1, 0), UI_Percent(1, 0)); - r32 HighlightTransition = AnimationCurve_AnimateValueF(PanelIsCurrent, 0, 0.25, "Workspace Panel Highlight %p", Panel); + r32 HighlightTransition = AC_AnimateValueF(PanelIsCurrent, 0, 0.25, "Workspace Panel Highlight %p", Panel); UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, HighlightTransition)); UI_SetNextBackgroundColor(Theme_BackgroundColor); @@ -562,7 +577,7 @@ static void Workspace_BuildPanel(workspace_panel *Panel) { ui_key CurrentActive = UI_GetActive(); - Workspace_BuildView(Panel->CurrentView); + W_BuildView(Panel->CurrentView); if(!AreEqual(CurrentActive, UI_GetActive())) { @@ -573,19 +588,19 @@ static void Workspace_BuildPanel(workspace_panel *Panel) // sixten: Draw dragged view overlay. { workspace_drag_payload Payload; - b32 DragActive = Workspace_GetDragPayload(&Payload); + b32 DragActive = W_GetDragPayload(&Payload); b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) && InRange(BodyBox->Rect, UI_GetState()->MouseP)); - if(OverlayActive && Workspace->DragPayloadState == Workspace_DragPayload_Released) + if(OverlayActive && Workspace->DragPayloadState == W_DragPayload_Released) { // sixten(NOTE): I need to be careful here. If something else sees that a payload has // been released and tries to act upon on it may lead to unwanted behaviour. Just // keep that in mind. // sixten(NOTE): On that previous note, I could just: - Workspace->DragPayloadState = Workspace_DragPayload_Inactive; + Workspace->DragPayloadState = W_DragPayload_Inactive; // sixten(TODO): Pull out the code below into separate functions. @@ -618,7 +633,7 @@ static void Workspace_BuildPanel(workspace_panel *Panel) } } - r32 OverlayTransition = AnimationCurve_AnimateValueF(OverlayActive, 0, 0.25, "Panel Drag Overlay %p", Panel); + r32 OverlayTransition = AC_AnimateValueF(OverlayActive, 0, 0.25, "Panel Drag Overlay %p", Panel); v4 OverlayColor = LinearBlend(Color_Grey, Theme_HighlightBorderColor, 0.75); OverlayColor.a = 0.5*OverlayTransition; @@ -670,7 +685,7 @@ static void Workspace_BuildPanel(workspace_panel *Panel) { UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0)); UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); - Workspace_BuildPanel(Child); + W_BuildPanel(Child); // sixten: Create drag area if(Child->Next) @@ -708,88 +723,68 @@ static void Workspace_BuildPanel(workspace_panel *Panel) } } -static void Workspace_BuildDragPayload() +static void W_BuildDragPayload() { - workspace *Workspace = Workspace_GetState(); - vn_input *Input = Workspace->Input; + workspace *Workspace = W_GetState(); workspace_drag_payload Payload; - if(Workspace_GetDragPayload(&Payload)) + if(W_GetDragPayload(&Payload)) { - if(Workspace->DragPayloadState == Workspace_DragPayload_Released) + if(Workspace->DragPayloadState == W_DragPayload_Released) { - Workspace->DragPayloadState = Workspace_DragPayload_Inactive; + Workspace->DragPayloadState = W_DragPayload_Inactive; } if(AreEqual(Payload.Key, UI_GetActive())) { workspace_view *DraggedView = Payload.View; - UI_Tooltip - { - UI_SetNextWidth(UI_TextContent(5, 1)); - UI_SetNextHeight(UI_TextContent(5, 1)); - - UI_SetNextFixedX(Input->MouseP.x + 10); - UI_SetNextFixedY(Input->MouseP.y); - - UI_MakeBox(UI_BoxFlag_DrawBorder | - UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawText | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY, - Workspace_GetViewName(DraggedView)); - } + UI_SetNextCornerRadius(4); + UI_TooltipLabel(W_GetViewName(DraggedView), UI_MouseP()); } else { - if(Workspace->DragPayloadState == Workspace_DragPayload_Active) + if(Workspace->DragPayloadState == W_DragPayload_Active) { - Workspace->DragPayloadState = Workspace_DragPayload_Released; + Workspace->DragPayloadState = W_DragPayload_Released; } } } } //- sixten: Workspace -static void Workspace_Init(workspace *Workspace) +static void W_Init(workspace *Workspace) { - Workspace_SetState(Workspace); + W_SetState(Workspace); Workspace->FrameArena = ArenaAllocate(Gigabytes(1)); Workspace->CommandArena = ArenaAllocate(Gigabytes(1)); Workspace->PanelArena = ArenaAllocate(Gigabytes(1)); - Workspace->RootPanel = Workspace->CurrentPanel = Workspace_CreateNewPanel(0); + Workspace->RootPanel = Workspace->CurrentPanel = W_CreateNewPanel(0); if(DEBUG_DebugSettings->ShowWelcomeMessage) { - Workspace_CreateNewView(Workspace_View_Startup, Workspace->RootPanel); + W_CreateNewView(W_View_Startup, Workspace->RootPanel); } - // sixten: Setup keybinds - { -#define BIND_COMMAND(...) Workspace->Keybinds[Workspace->KeybindCount++] = {__VA_ARGS__} - BIND_COMMAND(Key_P, PlatformModifier_Ctrl, Workspace_Command_SplitPanelHorizontal); - BIND_COMMAND(Key_L, PlatformModifier_Ctrl, Workspace_Command_SplitPanelVertical); - - BIND_COMMAND(Key_O, PlatformModifier_Ctrl, Workspace_Command_OpenView, Workspace_View_TextEditor); - -#if VN_INTERNAL - BIND_COMMAND(Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, Workspace_Command_ToggleRenderUIDebugRects); -#endif -#undef BIND_COMMAND - } + W_CreateNewView(W_View_TextEditor, Workspace->RootPanel); + W_SplitPanel(Workspace->RootPanel, Axis2_X); + W_CreateNewView(W_View_SceneView, Workspace->RootPanel->Last); } -static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCommands, - vn_input *Input, glyph_atlas *GlyphAtlas) +static void W_Update(workspace *Workspace, vn_render_commands *RenderCommands, + vn_input *Input, glyph_atlas *GlyphAtlas) { - Workspace_SetState(Workspace); + W_SetState(Workspace); ArenaClear(Workspace->FrameArena); Workspace->Input = Input; Workspace->EventList = Input->EventList; + // sixten: Process the keybinds and commands. + W_ProcessKeyBinds(); + W_ProcessCommands(); + // sixten: Make sure that a view/panel is always selected. if(!Workspace->CurrentPanel) { @@ -802,16 +797,12 @@ static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCom // sixten: Build the UI. { - Workspace_BuildToolbar(); + W_BuildToolbar(); UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0)); - Workspace_BuildPanel(Workspace->RootPanel); + W_BuildPanel(Workspace->RootPanel); - Workspace_BuildDragPayload(); + W_BuildDragPayload(); } - // sixten: Process the keybinds and commands. - Workspace_ProcessKeyBinds(); - Workspace_ProcessCommands(); - } \ No newline at end of file diff --git a/code/vn_workspace.h b/code/vn_workspace.h index c06f5e8..7f46935 100644 --- a/code/vn_workspace.h +++ b/code/vn_workspace.h @@ -3,8 +3,8 @@ #ifndef VN_WORKSPACE_H #define VN_WORKSPACE_H -#include "vn_workspace_editor.h" -#include "vn_workspace_text_editor.h" +//////////////////////////////// +//~ sixten: Workspace Types // sixten(TODO): Remove this type entirely. enum toolbar_menu @@ -32,8 +32,6 @@ struct workspace_panel r32 PercentOfParent; }; -#include "vn_workspace_view.h" - #define WORKSPACE_COMMAND(name, ...) void name(u64 Argument) typedef WORKSPACE_COMMAND(workspace_command_sig); @@ -56,9 +54,9 @@ struct workspace_keybind enum workspace_drag_payload_state { - Workspace_DragPayload_Inactive = 0, - Workspace_DragPayload_Active, - Workspace_DragPayload_Released, + W_DragPayload_Inactive = 0, + W_DragPayload_Active, + W_DragPayload_Released, }; struct workspace_drag_payload @@ -84,10 +82,6 @@ struct workspace workspace_command *FirstCommand; workspace_command *LastCommand; - // sixten: Keybinds - workspace_keybind Keybinds[256]; - s32 KeybindCount; - // sixten: Panels memory_arena *PanelArena; workspace_panel *FirstFreePanel; @@ -104,28 +98,34 @@ struct workspace workspace_panel *CurrentPanel; }; +#include "vn_workspace_editor.h" +#include "vn_workspace_text_editor.h" +#include "vn_workspace_view.h" + +//////////////////////////////// +//~ sixten: Workspace Functions + //- sixten: State management -static void Workspace_SetState(workspace *Workspace); -static workspace *Workspace_GetState(void); -static memory_arena *Workspace_FrameArena(void); +static void W_SetState(workspace *Workspace); +static workspace *W_GetState(void); +static memory_arena *W_FrameArena(void); //- sixten: Commands -static void Workspace_IssueCommand(workspace_command_sig *Sig, u64 Argument); -static void Workspace_ProcessCommands(void); +static void W_IssueCommand(workspace_command_sig *Sig, u64 Argument); +static void W_ProcessCommands(void); //- sixten: Panels -static workspace_panel *Workspace_CreateNewPanel(workspace_panel *Parent); -static void Workspace_DeletePanel(workspace_panel *Panel); -static void Workspace_SplitPanel(workspace_panel *Panel, axis2 Axis); +static workspace_panel *W_CreateNewPanel(workspace_panel *Parent); +static void W_DeletePanel(workspace_panel *Panel); +static void W_SplitPanel(workspace_panel *Panel, axis2 Axis); //- sixten: Builder code -static ui_signal Workspace_BuildToolbarButton(char *Text, toolbar_menu Menu); -static ui_signal Workspace_BuildMenuItem(u32 Icon, char *Text, char *Shortcut); -static void Workspace_BuildToolbar(r32 dtForFrame); +static ui_signal W_BuildToolbarButton(char *Text, toolbar_menu Menu); +static ui_signal W_BuildMenuItem(u32 Icon, char *Text, char *Shortcut); +static void W_BuildToolbar(r32 dtForFrame); //- sixten: Workspace -static void Workspace_Init(workspace *Workspace); -static void Workspace_Update(workspace *Workspace, vn_render_commands *RenderCommands, - vn_input *Input, glyph_atlas *GlyphAtlas); +static void W_Init(workspace *Workspace); +static void W_Update(workspace *Workspace, vn_render_commands *RenderCommands, vn_input *Input, glyph_atlas *GlyphAtlas); #endif //VN_WORKSPACE_H diff --git a/code/vn_workspace_commands.cpp b/code/vn_workspace_commands.cpp index 7374679..58e295f 100644 --- a/code/vn_workspace_commands.cpp +++ b/code/vn_workspace_commands.cpp @@ -1,18 +1,18 @@ -WORKSPACE_COMMAND(Workspace_Command_SplitPanelHorizontal) +WORKSPACE_COMMAND(W_Command_SplitPanelHorizontal) { - workspace *Workspace = Workspace_GetState(); - Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_X); + workspace *Workspace = W_GetState(); + W_SplitPanel(Workspace->CurrentPanel, Axis2_X); } -WORKSPACE_COMMAND(Workspace_Command_SplitPanelVertical) +WORKSPACE_COMMAND(W_Command_SplitPanelVertical) { - workspace *Workspace = Workspace_GetState(); - Workspace_SplitPanel(Workspace->CurrentPanel, Axis2_Y); + workspace *Workspace = W_GetState(); + W_SplitPanel(Workspace->CurrentPanel, Axis2_Y); } -WORKSPACE_COMMAND(Workspace_Command_ClosePanel) +WORKSPACE_COMMAND(W_Command_ClosePanel) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); workspace_panel *Panel = (workspace_panel *)Argument; if(!Panel) @@ -54,7 +54,7 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel) } DLLRemove(Parent->First, Parent->Last, Child); - Workspace_DeletePanel(Child); + W_DeletePanel(Child); } else { @@ -76,25 +76,50 @@ WORKSPACE_COMMAND(Workspace_Command_ClosePanel) } // sixten: Delete all child views. + workspace_view *NextChild = 0; for(workspace_view *Child = Panel->FirstView; Child != 0; - Child = Child->Next) + Child = NextChild) { - //Workspace_DeleteView(Child); + NextChild = Child->Next; + W_DestroyView(Child); } - Workspace_DeletePanel(Panel); + W_DeletePanel(Panel); } -WORKSPACE_COMMAND(Workspace_Command_OpenView) +WORKSPACE_COMMAND(W_Command_OpenView) { - workspace *Workspace = Workspace_GetState(); - Workspace_CreateNewView((workspace_view_type)Argument, Workspace->CurrentPanel); + workspace *Workspace = W_GetState(); + W_CreateNewView((workspace_view_type)Argument, Workspace->CurrentPanel); +} + +WORKSPACE_COMMAND(W_Command_CloseView) +{ + workspace_view *View = (workspace_view *)U64ToPointer(Argument); + workspace_panel *Panel = View->Parent; + + DLLRemove(Panel->FirstView, Panel->LastView, View); + if(Panel->CurrentView == View) + { + Panel->CurrentView = Panel->FirstView; + } + + W_DestroyView(View); } #if VN_INTERNAL -WORKSPACE_COMMAND(Workspace_Command_ToggleRenderUIDebugRects) +WORKSPACE_COMMAND(W_Command_ToggleRenderUIDebugRects) { DEBUG_DebugSettings->RenderUIDebugRects = !DEBUG_DebugSettings->RenderUIDebugRects; } -#endif \ No newline at end of file +#endif + +WORKSPACE_COMMAND(W_Command_Test) +{ + for(s32 Index = 0; Index < 10000; Index += 1) + { + W_IssueCommand(W_Command_OpenView, W_View_TextEditor); + W_IssueCommand(W_Command_CloseView, 0); + } +} \ No newline at end of file diff --git a/code/vn_workspace_editor.cpp b/code/vn_workspace_editor.cpp index 2b97073..8ecb559 100644 --- a/code/vn_workspace_editor.cpp +++ b/code/vn_workspace_editor.cpp @@ -1,7 +1,7 @@ //- sixten: Managing nodes static workspace_editor_node *Workspace_GetNewEditorNode(workspace_view *View) { - Assert(View->Type == Workspace_View_Editor); + Assert(View->Type == W_View_Editor); workspace_view_editor *Editor = (workspace_view_editor *)View->Data; @@ -70,7 +70,7 @@ static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown UI_PushBackgroundColor(SetAlpha(Color_Black, 0.3)); UI_PushBorderColor(SetAlpha(Color_Black, 0.7)); - r32 HeightTransition = AnimationCurve_AnimateValueF(1, 0, 0.3, "Editor Lister Dropdown %p", ListerDropdown); + r32 HeightTransition = AC_AnimateValueF(1, 0, 0.3, "Editor Lister Dropdown %p", ListerDropdown); UI_SetNextSize(UI_Em(15, 1), UI_Em(25*HeightTransition, 1)); UI_SetNextCornerRadius(4.0); @@ -145,8 +145,8 @@ static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown UI_SetNextCornerRadius(3.0); UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); UI_SetNextBackgroundColor(SetAlpha(Theme_HighlightBorderColor, - AnimationCurve_AnimateValueF(TargetHot, TargetHot, 0.1, "Editor Lister %s%p", - Option->Text, ListerDropdown))); + AC_AnimateValueF(TargetHot, TargetHot, 0.1, "Editor Lister %s%p", + Option->Text, ListerDropdown))); UI_SetNextLayoutAxis(Axis2_X); ui_box *HighlightBox = UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit("")); @@ -219,7 +219,7 @@ static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas static void Workspace_BuildEditor(workspace_view *View) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); workspace_view_editor *Editor = (workspace_view_editor *)View->Data; @@ -227,10 +227,9 @@ static void Workspace_BuildEditor(workspace_view *View) UI_SetNextHeight(UI_Percent(1, 1)); ui_box *EditorBox = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "Workspace Editor %p", View); - EditorBox->DrawCallback = Workspace_EditorDrawCallback; - EditorBox->DrawCallbackData = Editor; + UI_EquipBoxCustomDrawCallback(EditorBox, Workspace_EditorDrawCallback, Editor); - r32 AnimatedZoomLevel = AnimationCurve_AnimateValueF(Editor->ZoomLevel, 0, 0.25, "Workspace Editor Zoom"); + r32 AnimatedZoomLevel = AC_AnimateValueF(Editor->ZoomLevel, 0, 0.25, "Workspace Editor Zoom"); Editor->Scale = Workspace_CalculateScaleFromZoomLevel(AnimatedZoomLevel); v2 EditorDim = DimOfRange(EditorBox->Rect); diff --git a/code/vn_workspace_text_editor.cpp b/code/vn_workspace_text_editor.cpp index 38661c9..21da7e8 100644 --- a/code/vn_workspace_text_editor.cpp +++ b/code/vn_workspace_text_editor.cpp @@ -6,7 +6,7 @@ static mutable_string MutableStringAllocate(u64 Size) mutable_string Result = {}; Result.Arena = ArenaAllocate(Size); ArenaSetAlign(Result.Arena, 1); - Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0); + Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0LL); return(Result); } @@ -60,9 +60,9 @@ static void AppendToHistory(memory_arena *Arena, history_list *List, history_ent } //////////////////////////////// -//~ sixten: Workspace Text Editor Functions +//~ sixten: Workspace Text Editing Functions -static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text) +static workspace_text_data W_TextDataFromString(memory_arena *Arena, string Text) { temporary_memory Scratch = GetScratch(&Arena, 1); @@ -102,14 +102,41 @@ static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *A return(Result); } -static void Workspace_TextEditorApplyChanges(workspace_view_text_editor *Editor) +static void W_TextEditorApplyChanges(workspace_view_text_editor *Editor) { - workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); + workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String); Editor->Tokens = TextData.Tokens; Editor->Lines = TextData.Lines; + Editor->Compiled = S_ScriptFromText(Editor->ProcessingArena, Editor->Text.String); + if(Editor->Compiled.IsValid) + { + SV_SetCurrentSource(&Editor->Compiled); + } } -static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) +static void W_SaveTextEditorToFile(workspace_view_text_editor *Editor) +{ + temporary_memory Scratch = GetScratch(); + + if(Editor->SavePoint != Editor->History.At) + { + string Path = PushFormat(Scratch.Arena, "%S/%S", Editor->FilePath, Editor->FileName); + platform_file_handle Handle = Platform.OpenFile(Path, PlatformAccess_Write); + if(Handle.IsValid) + { + Platform.WriteFile(Handle, Editor->Text.String.Data, 0, Editor->Text.String.Count); + Platform.CloseFile(Handle); + } + + Editor->SavePoint = Editor->History.At; + } + ReleaseScratch(Scratch); +} + +//////////////////////////////// +//~ sixten: Workspace Text Editor Builder Functions + +static UI_CUSTOM_DRAW_CALLBACK(W_TextEditorDrawCallback) { temporary_memory Scratch = GetScratch(); workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data; @@ -207,7 +234,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) if(Token->Kind == TokenKind_Comment) { Color = Color_Grey; Font = Font_MonospaceOblique; } else if(Token->Kind > TokenKind_SymbolsBegin && Token->Kind < TokenKind_SymbolsEnd) { Color = Color_Grey; } else if(Token->Kind == TokenKind_StringLiteral) { Color = ColorFromHex(0xffa900ff); } - else if(Token->Kind == TokenKind_Numeric) { Color = ColorFromHex(0xffa900ff); } + else if(Token->Kind == TokenKind_Numeric) { Color = ColorFromHex(0xffa900ff); } else if(Token->Kind > TokenKind_KeywordsBegin && Token->Kind < TokenKind_KeywordsEnd) { if(Token->Kind == TokenKind_True || Token->Kind == TokenKind_False) @@ -224,6 +251,18 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) Color = Theme_TextColor; } + //- sixten: check for errors + b32 ConsideredError = false; + for(scene_compile_error *Error = Editor->Compiled.Errors.First; Error != 0; Error = Error->Next) + { + if(Error->Token.Range.Min == Token->Range.Min && + Error->Token.Range.Max == Token->Range.Max) + { + ConsideredError = true; + break; + } + } + //- sixten: render & advance by token if(!(T_IsWhitespace(Token->Kind))) { @@ -251,7 +290,16 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) } else { - TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString); + r32 TokenWidth = PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString); + + //- sixten: render error highlight + if(ConsideredError) + { + range2_r32 Dest = Range2R32(TokenP+V2R32(0, LineHeight-3), TokenP+V2R32(TokenWidth, LineHeight)); + v4_r32 ErrorColor = V4R32(0.9f, 0.3f, 0.3f, 0.8f); + PushQuad(Group, Dest, ErrorColor, ErrorColor, ErrorColor, ErrorColor, 3, 0.4, 0); + } + TokenP.x += TokenWidth; } } else @@ -284,8 +332,8 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) s64 ColumnOffset = UTF8OffsetFromIndex(Line, ColumnIndex); v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+ColumnOffset*GlyphAdvance, LineIndex*LineHeight); - v2 CursorP = V2(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor), - AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor)); + v2 CursorP = V2(AC_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor), + AC_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor)); v2 CursorDim = V2(2, LineHeight); range2_r32 CursorRect = Range2R32(CursorP, CursorP+CursorDim); v4 CursorColor = ColorFromHex(0x10FF20FF); @@ -329,11 +377,60 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback) } ReleaseScratch(Scratch); } +UI_CUSTOM_DRAW_CALLBACK(W_TextEditorListerInputCallback) +{ + workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data; + s64 ClampedCursor = Clamp(0, Box->String.Count, Editor->ListerInputEditState.Cursor); + s64 ClampedMark = Clamp(0, Box->String.Count, Editor->ListerInputEditState.Mark); + string ToCursor = MakeString(Box->String.Data, ClampedCursor); + string ToMark = MakeString(Box->String.Data, ClampedMark); + + 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 + { + 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 b32 Workspace_BuildTextEditorListerItem(string Text, u32 Icon) +static b32 W_BuildTextEditorListerItem(string Text, u32 Icon) { b32 Result = false; UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHoverCursor(PlatformCursor_Hand); ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBorder | UI_BoxFlag_DrawBackground | UI_BoxFlag_Clickable | @@ -355,7 +452,7 @@ static b32 Workspace_BuildTextEditorListerItem(string Text, u32 Icon) return(Result); } -static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(workspace_view *View, workspace_view_text_editor *Editor) +static workspace_text_editor_lister_action W_BuildTextEditorLister(workspace_view *View, workspace_view_text_editor *Editor) { workspace_text_editor_lister_action ListerAction = {}; temporary_memory Scratch = GetScratch(); @@ -364,7 +461,7 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works UI_Height(UI_Em(2, 1)) { //- sixten: filename input field - if(Workspace_ViewIsCurrent(View)) + if(W_ViewIsCurrent(View)) { for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next) { @@ -402,7 +499,9 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works UI_MakeBox(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawText, Editor->Path); UI_SetNextWidth(UI_TextContent(15, 1)); - UI_Label(MakeString(Editor->ListerInput, Editor->ListerInputUsed)); + ui_box *InputBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Text Editor Lister Input")); + UI_EquipBoxText(InputBox, MakeString(Editor->ListerInput, Editor->ListerInputUsed)); + UI_EquipBoxCustomDrawCallback(InputBox, W_TextEditorListerInputCallback, Editor); UI_Padding(UI_Percent(1, 0)); @@ -419,7 +518,7 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works s64 LastSlash = LastIndexOf(Editor->Path, '/'); if(LastSlash != -1) { - if(Workspace_BuildTextEditorListerItem(StrLit("Parent Directory"), FontIcon_Reply)) + if(W_BuildTextEditorListerItem(StrLit("Parent Directory"), FontIcon_Reply)) { Editor->Path = Prefix(Editor->Path, LastSlash); Editor->ListerInputUsed = 0; @@ -438,7 +537,7 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works { if(FileInfo.IsDirectory) { - if(Workspace_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Folder)) + if(W_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Folder)) { Editor->Path = PushFormat(View->Arena, "%S/%S", Editor->Path, FileInfo.Name); Editor->ListerInputUsed = 0; @@ -455,7 +554,7 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works { if(!FileInfo.IsDirectory) { - if(Workspace_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Document)) + if(W_BuildTextEditorListerItem(FileInfo.Name, FontIcon_Document)) { ListerAction.HasRequestedFile = true; ListerAction.Name = PushString(View->Arena, FileInfo.Name); @@ -471,7 +570,7 @@ static workspace_text_editor_lister_action Workspace_BuildTextEditorLister(works return(ListerAction); } -static b32 Workspace_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event) +static b32 W_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event) { b32 CursorHasBeenModified = false; temporary_memory Scratch = GetScratch(); @@ -502,8 +601,9 @@ static b32 Workspace_ProcessTextEditorEvent(workspace_view_text_editor *Editor, List->At = List->Sentinel.Prev; } //- sixten: apply the text action - MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range); - Workspace_TextEditorApplyChanges(Editor); + string ReplaceString = RemoveAll(Scratch.Arena, Op.ReplaceString, '\r'); + MutableStringReplaceRange(&Editor->Text, ReplaceString, Op.Range); + W_TextEditorApplyChanges(Editor); } CursorHasBeenModified = true; @@ -514,14 +614,28 @@ static b32 Workspace_ProcessTextEditorEvent(workspace_view_text_editor *Editor, return(CursorHasBeenModified); } -static void Workspace_BuildTextEditor(workspace_view *View) +static void W_BuildTextEditorInfoBar(workspace_view_text_editor *Editor) +{ + UI_SetNextLayoutAxis(Axis2_X); + UI_WidthFill UI_Height(UI_Em(1.75f, 1)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawDropShadow, "")) UI_Padding(UI_Em(1, 1)) + { + UI_Width(UI_TextContent(0, 1)) + { + UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_Attention); + UI_Spacer(UI_Em(0.5f, 1)); + UI_LabelF("%i", Editor->Compiled.Errors.Count); + } + } +} + +static void W_BuildTextEditor(workspace_view *View) { workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; temporary_memory Scratch = GetScratch(); //- sixten: rendering properties - r32 FontSize = Editor->FontSize = 14.0f; + r32 FontSize = Editor->FontSize = 13.0f; r32 LineHeight = FontSize + 4.0f; //- sixten: calculate the dimensions of the glyphs @@ -537,7 +651,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) if(InFileListMode) { //- sixten: build & handle file lister - workspace_text_editor_lister_action Action = Workspace_BuildTextEditorLister(View, Editor); + workspace_text_editor_lister_action Action = W_BuildTextEditorLister(View, Editor); if(Action.HasRequestedFile) { //- sixten: try to load file @@ -553,7 +667,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) ReplaceString = RemoveAll(Scratch.Arena, ReplaceString, '\r'); MutableStringReplaceRange(&Editor->Text, ReplaceString, Range1S64(0, 0)); - Workspace_TextEditorApplyChanges(Editor); + W_TextEditorApplyChanges(Editor); Editor->FileName = Action.Name; Editor->FilePath = Action.Path; @@ -570,7 +684,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) { //- sixten: build & handle the text editor ui_box *EditorBox = 0; - UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0)); UI_Scroll(0, &Editor->Offset.y) { //- sixten: find the container box for the scrollable region @@ -578,13 +692,52 @@ static void Workspace_BuildTextEditor(workspace_view *View) UI_SetNextSize(UI_Pixels(Editor->TextDim.x, 1), UI_Pixels(Editor->TextDim.y, 1)); EditorBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, "Workspace Text Editor %p", View); - EditorBox->DrawCallback = Workspace_TextEditorDrawCallback; - EditorBox->DrawCallbackData = Editor; + UI_EquipBoxCustomDrawCallback(EditorBox, W_TextEditorDrawCallback, Editor); + } + + //- sixten: build footer + W_BuildTextEditorInfoBar(Editor); + r32 TargetFooterHeightEm = 2.25f*Min(Editor->Compiled.Errors.Count, 10LL); + UI_Size(UI_Percent(1, 0), UI_Em(AC_AnimateValueF(TargetFooterHeightEm, TargetFooterHeightEm, 0.3, "Error Lister %p", Editor), 1)) UI_Column() UI_Height(UI_TextContent(0, 1)) + { + s64 Index = 0; + for(scene_compile_error *Error = Editor->Compiled.Errors.First; Error != 0; Error = Error->Next, Index += 1) + { + UI_SetNextHeight(UI_ChildrenSum(1, 1)); + UI_SetNextLayoutAxis(Axis2_X); + UI_Parent(UI_MakeBoxF(0, "Editor Error Lister Container %p", Error)) UI_Padding(UI_Em(1, 1)) UI_Height(UI_Em(1.75f, 1)) + { + UI_SetNextBackgroundColor(SetAlpha(Theme_BorderColor, 0.8f)); + UI_SetNextCornerRadius(4); + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHoverCursor(PlatformCursor_Hand); + ui_box *ContainerBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_DrawBorder, "Container Box"); + + UI_Parent(ContainerBox) UI_Padding(UI_Em(1, 1)) UI_Width(UI_TextContent(0, 1)) + { + UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_Attention); + UI_Spacer(UI_Em(0.5f, 1)); + // sixten(TODO): this is dumb, slow and downright stupid. + text_point Point = TextPointFromOffset(Editor->Text.String, Error->Token.Range.Min); + UI_LabelF("%i:%i", Point.Line, Point.Column); + UI_Spacer(UI_Em(0.5f, 1)); + UI_Label(Error->Message); + UI_Spacer(UI_Percent(1, 0)); + } + + ui_signal Signal = UI_SignalFromBox(ContainerBox); + if(Signal.Hovering) + { + UI_TooltipLabel(StrLit("Goto in source"), UI_MouseP()); + } + } + UI_Spacer(UI_Em(0.5, 1)); + } } b32 CursorHasBeenModified = false; - if(Workspace_ViewIsCurrent(View)) + if(W_ViewIsCurrent(View)) { //- sixten: handle history { @@ -599,7 +752,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) //- sixten: get entry & apply history_entry Entry = Node->Backward; MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - Workspace_TextEditorApplyChanges(Editor); + W_TextEditorApplyChanges(Editor); Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; CursorHasBeenModified = true; @@ -616,7 +769,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) //- sixten: get entry & apply history_entry Entry = Node->Forward; MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - Workspace_TextEditorApplyChanges(Editor); + W_TextEditorApplyChanges(Editor); Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; CursorHasBeenModified = true; @@ -625,6 +778,12 @@ static void Workspace_BuildTextEditor(workspace_view *View) } } + //- sixten: save + if(Platform_KeyPress(UI_EventList(), Key_S, PlatformModifier_Ctrl)) + { + W_SaveTextEditorToFile(Editor); + } + //- sixten: select all if(Platform_KeyPress(UI_EventList(), Key_A, PlatformModifier_Ctrl)) { @@ -655,18 +814,31 @@ static void Workspace_BuildTextEditor(workspace_view *View) } } + //- sixten: auto close bracket if(Event->Codepoint == '{') { platform_event FakeEvent = {}; FakeEvent.Codepoint = '}'; FakeEvent.Type = PlatformEvent_Text; - CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeEvent); + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent); FakeEvent.Key = Key_Left;; FakeEvent.Type = PlatformEvent_Press; - CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeEvent); + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent); } - CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, Event); + //- sixten: auto close string literal + if(Event->Codepoint == '"') + { + platform_event FakeEvent = {}; + FakeEvent.Codepoint = '"'; + FakeEvent.Type = PlatformEvent_Text; + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent); + FakeEvent.Key = Key_Left;; + FakeEvent.Type = PlatformEvent_Press; + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent); + } + + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, Event); //- sixten: apply indent { @@ -676,7 +848,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) for(s64 IndentIndex = 0; IndentIndex < Indent; IndentIndex += 1) { - CursorHasBeenModified |= Workspace_ProcessTextEditorEvent(Editor, &FakeTab); + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeTab); } } } @@ -691,7 +863,7 @@ static void Workspace_BuildTextEditor(workspace_view *View) { UI_SetNextFixedP(Editor->DropdownP); UI_SetNextWidth(UI_Em(20, 1)); - UI_SetNextHeight(UI_ChildrenSum(AnimationCurve_AnimateValueDirect(1, 0.2, &Editor->DropdownTransition), 1)); + UI_SetNextHeight(UI_ChildrenSum(AC_AnimateValueDirect(1, 0.2, &Editor->DropdownTransition), 1)); UI_SetNextCornerRadius(4); UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawDropShadow | @@ -703,13 +875,31 @@ static void Workspace_BuildTextEditor(workspace_view *View) UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) UI_CornerRadius(2) { - UI_ButtonF("Hello"); - UI_ButtonF("Line"); - UI_ButtonF("Paint"); - UI_ButtonF("Color"); - UI_ButtonF("Design"); - UI_ButtonF("Address"); - UI_ButtonF("Brightness"); + if(W_BuildMenuItem(FontIcon_Gamepad, "Run in scene view", "").Clicked) + { + SV_SetCurrentSource(&Editor->Compiled); + Editor->DropdownActive = false; + } + if(Editor->EditState.Cursor != Editor->EditState.Mark) + { + if(W_BuildMenuItem(FontIcon_Document, "Copy", "Ctrl+C").Clicked) + { + Editor->DropdownActive = false; + } + if(W_BuildMenuItem(FontIcon_Cut, "Cut", "Ctrl+X").Clicked) + { + Editor->DropdownActive = false; + } + } + if(W_BuildMenuItem(FontIcon_Paste, "Paste", "Ctrl+V").Clicked) + { + Editor->DropdownActive = false; + } + if(W_BuildMenuItem(FontIcon_Floppy, "Save", "Ctrl+S").Clicked) + { + W_SaveTextEditorToFile(Editor); + Editor->DropdownActive = false; + } } } } diff --git a/code/vn_workspace_text_editor.h b/code/vn_workspace_text_editor.h index 0150fed..16455b6 100644 --- a/code/vn_workspace_text_editor.h +++ b/code/vn_workspace_text_editor.h @@ -57,6 +57,7 @@ struct workspace_view_text_editor memory_arena *ProcessingArena; token_array Tokens; range1_s64_array Lines; + compiled_scene Compiled; // sixten: text being edited string FileName; @@ -73,6 +74,7 @@ struct workspace_view_text_editor // sixten: history memory_arena *HistoryArena; history_list History; + history_node *SavePoint; // sixten: ui building & rendering ui_box *ContainerBox; @@ -104,10 +106,20 @@ static history_entry HistoryEntry(memory_arena *Arena, string ReplaceString, ran static void AppendToHistory(memory_arena *Arena, history_list *List, history_entry Forward, history_entry Backward); //////////////////////////////// -//~ sixten: Workspace Text Editor Functions +//~ sixten: Workspace Text Editing Functions -static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *Arena, string Text); -static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback); -static void Workspace_BuildTextEditor(workspace_view *View); +static workspace_text_data W_TextDataFromString(memory_arena *Arena, string Text); +static void W_TextEditorApplyChanges(workspace_view_text_editor *Editor); +static void W_SaveTextEditorToFile(workspace_view_text_editor *Editor); + +//////////////////////////////// +//~ sixten: Workspace Text Editor Builder Functions + +static UI_CUSTOM_DRAW_CALLBACK(W_TextEditorDrawCallback); +static b32 W_BuildTextEditorListerItem(string Text, u32 Icon); +static workspace_text_editor_lister_action W_BuildTextEditorLister(workspace_view *View, workspace_view_text_editor *Editor); +static b32 W_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event); +static void W_BuildTextEditorInfoBar(workspace_view_text_editor *Editor); +static void W_BuildTextEditor(workspace_view *View); #endif //VN_WORKSPACE_TEXT_EDITOR_H diff --git a/code/vn_workspace_view.cpp b/code/vn_workspace_view.cpp index dbfcef4..f715ab1 100644 --- a/code/vn_workspace_view.cpp +++ b/code/vn_workspace_view.cpp @@ -1,5 +1,5 @@ //- sixten: Views -inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent) +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); @@ -9,22 +9,22 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa switch(View->Type) { - case Workspace_View_Editor: + case W_View_Editor: { View->Data = PushStruct(View->Arena, workspace_view_editor); } break; - case Workspace_View_CommandPalette: + case W_View_CommandPalette: { View->Data = PushStruct(View->Arena, workspace_view_command_palette); } break; - case Workspace_View_Settings: + case W_View_Settings: { View->Data = PushStruct(View->Arena, workspace_view_settings); } break; - case Workspace_View_TextEditor: + case W_View_TextEditor: { View->Data = PushStruct(View->Arena, workspace_view_text_editor); @@ -35,8 +35,9 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa SenDLLInit(&Editor->History.Sentinel); Editor->History.At = &Editor->History.Sentinel; + Editor->SavePoint = Editor->History.At; - workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String); + workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String); Editor->Tokens = TextData.Tokens; Editor->Lines = TextData.Lines; } break; @@ -52,11 +53,11 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa return(View); } -inline void Workspace_DestroyView(workspace_view *View) +inline void W_DestroyView(workspace_view *View) { switch(View->Type) { - case Workspace_View_TextEditor: + case W_View_TextEditor: { workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; ArenaRelease(Editor->ProcessingArena); @@ -69,23 +70,23 @@ inline void Workspace_DestroyView(workspace_view *View) ArenaRelease(View->Arena); } -inline b32 Workspace_ViewIsCurrent(workspace_view *View) +inline b32 W_ViewIsCurrent(workspace_view *View) { - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View); return(Result); } -inline string Workspace_GetViewName(workspace_view *View) +inline string W_GetViewName(workspace_view *View) { string Result = StrLit("Unnamed view"); switch(View->Type) { - case Workspace_View_Startup: { Result = StrLit("Welcome"); } break; - case Workspace_View_Editor: { Result = StrLit("Editor"); } break; - case Workspace_View_Settings: { Result = StrLit("Settings"); } break; - case Workspace_View_TextEditor: + 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(""))) @@ -94,23 +95,25 @@ inline string Workspace_GetViewName(workspace_view *View) } else { - if(Editor->History.At == &Editor->History.Sentinel) + //if(Editor->History.At == &Editor->History.Sentinel) + if(Editor->History.At == Editor->SavePoint) { - Result = PushString(Workspace_FrameArena(), Editor->FileName); + Result = PushString(W_FrameArena(), Editor->FileName); } else { - Result = PushFormat(Workspace_FrameArena(), "* %S", Editor->FileName); + Result = PushFormat(W_FrameArena(), "* %S", Editor->FileName); } } } break; + case W_View_SceneView: { Result = StrLit("Scene View"); } break; } return(Result); } //- sixten: Builder code -static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data) +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); @@ -119,8 +122,8 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas * r32 TargetCursorX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToCursor); r32 TargetMarkerX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToMark); - r32 CursorX = AnimationCurve_AnimateValueF(TargetCursorX, 0, 0.175, "Workspace View Input Cursor %p", Box); - r32 MarkerX = AnimationCurve_AnimateValueF(TargetMarkerX, 0, 0.175, "Workspace View Input Mark %p", Box); + 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); @@ -158,11 +161,11 @@ static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas * } } -static void Workspace_BuildViewTypeLister(workspace_view *View) +static void W_BuildViewTypeLister(workspace_view *View) { workspace_view_command_palette *CommandPalette = (workspace_view_command_palette *)View->Data; - workspace *Workspace = Workspace_GetState(); + workspace *Workspace = W_GetState(); temporary_memory Scratch = GetScratch(0, 0); @@ -184,8 +187,8 @@ static void Workspace_BuildViewTypeLister(workspace_view *View) // sixten: Input Field. { - r32 SelectedTransition = AnimationCurve_AnimateValueF(CommandPalette->ListerFieldSelected ? 1.0 : 0.0, - 0, 0.125, "View Input Field %p", View); + 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; @@ -206,8 +209,7 @@ static void Workspace_BuildViewTypeLister(workspace_view *View) ui_box *InputTextBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Workspace View Lister")); InputTextBox->String = MakeString(CommandPalette->ListerInput, CommandPalette->ListerInputUsed); - InputTextBox->DrawCallback = Workspace_ViewListerInputCallback; - InputTextBox->DrawCallbackData = CommandPalette; + UI_EquipBoxCustomDrawCallback(InputTextBox, W_ViewListerInputCallback, CommandPalette); } UI_Spacer(UI_Pixels(4, 1)); @@ -282,11 +284,11 @@ static void Workspace_BuildViewTypeLister(workspace_view *View) ReleaseScratch(Scratch); } -static void Workspace_BuildSettingsTabButton(workspace_view_settings *Settings, char *Name, workspace_settings_category Category) +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, AnimationCurve_AnimateValueF(IsSelected, IsSelected, 0.3, "Workspace Settings %s %p", Name, Settings)); + 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)); @@ -346,7 +348,7 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 * ActiveInDropdown = true; } - r32 OpenTransition = AnimationCurve_AnimateValueF(*Open, 0, 0.175, "UI Dropdown %p%p", Alternatives, Open); + r32 OpenTransition = AC_AnimateValueF(*Open, 0, 0.175, "UI Dropdown %p%p", Alternatives, Open); if(OpenTransition > 0.1) { @@ -394,11 +396,16 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 * return(Result); } -static void Workspace_BuildSettings(workspace_view *View) +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 = Workspace_GetState(); + workspace *Workspace = W_GetState(); UI_Height(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) @@ -420,13 +427,13 @@ static void Workspace_BuildSettings(workspace_view *View) 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, "")) { - Workspace_BuildSettingsTabButton(Settings, "All", Workspace_Settings_All); + W_BuildSettingsTabButton(Settings, "All", W_Settings_All); UI_Spacer(UI_Pixels(30, 1)); - Workspace_BuildSettingsTabButton(Settings, "General", Workspace_Settings_General); + W_BuildSettingsTabButton(Settings, "General", W_Settings_General); UI_Spacer(UI_Pixels(30, 1)); - Workspace_BuildSettingsTabButton(Settings, "Theme", Workspace_Settings_Theme); + W_BuildSettingsTabButton(Settings, "Theme", W_Settings_Theme); UI_Spacer(UI_Pixels(30, 1)); - Workspace_BuildSettingsTabButton(Settings, "Developer", Workspace_Settings_Developer); + W_BuildSettingsTabButton(Settings, "Developer", W_Settings_Developer); UI_Spacer(UI_Pixels(150, 1)); } @@ -443,77 +450,80 @@ static void Workspace_BuildSettings(workspace_view *View) UI_Parent(UI_MakeBoxF(0, "Tab")) UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) { - workspace_settings_category Category = Settings->Category; - if(!Category || (Category == Workspace_Settings_General)) + UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); + UI_Scroll(0, &Settings->GlobalScroll, UI_BoxFlag_Clip) { - UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("General"); - - char *Alternatives[] = {"60 Hz", "120 Hz", "144 Hz", "Uncapped", "V-Sync"}; - s64 AlternativeMapping[] = {60, 120, 144, -1, 0}; - - s32 DropdownSelected; - FindIndexOfElement(DropdownSelected, AlternativeMapping, 0, Workspace->Input->RefreshRate); - - if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives), - &Settings->GeneralDropdownOpen, &DropdownSelected)) + workspace_settings_category Category = Settings->Category; + if(!Category || (Category == W_Settings_General)) { - Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected]; - Settings->GeneralDropdownOpen = false; + UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("General"); + + char *Alternatives[] = {"60 Hz", "120 Hz", "144 Hz", "Uncapped", "V-Sync"}; + s64 AlternativeMapping[] = {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)); } - UI_Spacer(UI_Pixels(50, 1)); - } - - if(!Category || (Category == Workspace_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) + if(!Category || (Category == W_Settings_Theme)) { - UI_Size(UI_Percent(1, 1), UI_Em(2, 1)) + 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) { - for(s32 Index = 0; - Index < 2; - ++Index) + UI_Size(UI_Percent(1, 1), UI_Em(2, 1)) { - 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); + 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)); } - UI_Spacer(UI_Pixels(50, 1)); - } - - if(!Category || (Category == Workspace_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)); + 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 Workspace_BuildView(workspace_view *View) +static void W_BuildView(workspace_view *View) { r32 ViewHighlightTransition = - AnimationCurve_AnimateValueF(Workspace_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View); + 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); @@ -527,7 +537,7 @@ static void Workspace_BuildView(workspace_view *View) UI_Parent(ViewBox) UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) { - if(View->Type == Workspace_View_Startup) + if(View->Type == 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)) @@ -544,21 +554,25 @@ static void Workspace_BuildView(workspace_view *View) } } } - else if(View->Type == Workspace_View_CommandPalette) + else if(View->Type == W_View_CommandPalette) { - Workspace_BuildViewTypeLister(View); + W_BuildViewTypeLister(View); } - else if(View->Type == Workspace_View_Editor) + else if(View->Type == W_View_Editor) { Workspace_BuildEditor(View); } - else if(View->Type == Workspace_View_Settings) + else if(View->Type == W_View_Settings) { - Workspace_BuildSettings(View); + W_BuildSettings(View); } - else if(View->Type == Workspace_View_TextEditor) + else if(View->Type == W_View_TextEditor) { - Workspace_BuildTextEditor(View); + W_BuildTextEditor(View); + } + else if(View->Type == W_View_SceneView) + { + W_BuildSceneView(View); } } diff --git a/code/vn_workspace_view.h b/code/vn_workspace_view.h index b5eb8e0..e917311 100644 --- a/code/vn_workspace_view.h +++ b/code/vn_workspace_view.h @@ -3,6 +3,9 @@ #ifndef VN_WORKSPACE_VIEW_H #define VN_WORKSPACE_VIEW_H +//////////////////////////////// +//~ sixten: Workspace View Types + struct workspace_view { memory_arena *Arena; @@ -17,11 +20,12 @@ struct workspace_view enum workspace_view_type { - Workspace_View_Startup, - Workspace_View_Editor, - Workspace_View_CommandPalette, - Workspace_View_Settings, - Workspace_View_TextEditor, + W_View_Startup, + W_View_Editor, + W_View_CommandPalette, + W_View_Settings, + W_View_TextEditor, + W_View_SceneView, }; struct workspace_view_editor @@ -47,15 +51,16 @@ struct workspace_view_command_palette enum workspace_settings_category { - Workspace_Settings_All, - Workspace_Settings_General, - Workspace_Settings_Theme, - Workspace_Settings_Developer, + W_Settings_All, + W_Settings_General, + W_Settings_Theme, + W_Settings_Developer, }; struct workspace_view_settings { workspace_settings_category Category; + r32 GlobalScroll; // sixten: General b32 GeneralDropdownOpen; @@ -64,15 +69,18 @@ struct workspace_view_settings r32 ThemeScroll; }; +//////////////////////////////// +//~ sixten: Workspace View Functions + //- sixten: Views -inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspace_panel *Parent); -inline void Workspace_DestroyView(workspace_view *View); -inline b32 Workspace_ViewIsCurrent(workspace_view *View); -inline string Workspace_GetViewName(workspace_view *View); +inline workspace_view *W_CreateNewView(workspace_view_type Type, workspace_panel *Parent); +inline void W_DestroyView(workspace_view *View); +inline b32 W_ViewIsCurrent(workspace_view *View); +inline string W_GetViewName(workspace_view *View); //- sixten: Builder code -static void Workspace_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data); -static void Workspace_BuildViewTypeLister(workspace_view *View); -static void Workspace_BuildView(workspace_view *View); +static void W_ViewListerInputCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data); +static void W_BuildViewTypeLister(workspace_view *View); +static void W_BuildView(workspace_view *View); #endif //VN_WORKSPACE_VIEW_H diff --git a/code/win32_main.cpp b/code/win32_main.cpp index aefa326..769a2f8 100644 --- a/code/win32_main.cpp +++ b/code/win32_main.cpp @@ -314,7 +314,7 @@ static void Win32_UpdateCode(win32_loaded_code *Code) *Code = Win32_LoadCode(); // sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over... - Sleep(200); + Sleep(500); } } @@ -348,6 +348,50 @@ static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen) } } +static PLATFORM_SET_CLIPBOARD(Win32_SetClipboard) +{ + temporary_memory Scratch = GetScratch(); + if(OpenClipboard(0)) + { + EmptyClipboard(); + + string16 String16 = String16FromString8(Scratch.Arena, String); + + HANDLE CopyHandle = GlobalAlloc(GMEM_MOVEABLE, String16.Count*sizeof(u16)+1); + if(CopyHandle) + { + u16 *CopyBuffer = (u16 *)GlobalLock(CopyHandle); + Copy(CopyBuffer, String16.Data, String16.Count*sizeof(u16)); + CopyBuffer[String.Count] = 0; + GlobalUnlock(CopyHandle); + SetClipboardData(CF_UNICODETEXT, CopyHandle); + } + CloseClipboard(); + } + ReleaseScratch(Scratch); +} + +static PLATFORM_GET_CLIPBOARD(Win32_GetClipboard) +{ + string Result = {}; + if(IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(0)) + { + HANDLE DataHandle = GetClipboardData(CF_UNICODETEXT); + if(DataHandle) + { + u16 *Data = (u16 *)GlobalLock(DataHandle); + if(Data) + { + s64 Count = StringLength16(Data); + Result = String8FromString16(Arena, MakeString16(Data, Count)); + GlobalUnlock(DataHandle); + } + } + CloseClipboard(); + } + return(Result); +} + inline v2 Win32_GetMouseP(HWND Window) { POINT Point; @@ -604,14 +648,7 @@ static void Win32_ProcessInput(vn_input *Input, HWND Window, r32 dtForFrame) { win32_state *State = &Global_Win32State; - { - if(State->EventArenaTemp.Arena) - { - EndTemporaryMemory(State->EventArenaTemp); - } - - State->EventArenaTemp = BeginTemporaryMemory(State->EventArena); - } + ArenaClear(State->EventArena); MSG Message; while(PeekMessage(&Message, Window, 0, 0, PM_REMOVE)) @@ -722,7 +759,7 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i { HWND Window = CreateWindowEx(0, WindowClass.lpszClassName, - "vn - July 2023 Build", + "vn - August 2023 Build", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, diff --git a/code/win32_main.h b/code/win32_main.h index fb643aa..2b62b1f 100644 --- a/code/win32_main.h +++ b/code/win32_main.h @@ -11,7 +11,6 @@ struct win32_state HWND Window; memory_arena *EventArena; - temporary_memory EventArenaTemp; platform_event_list EventList; char EXEPath[512]; diff --git a/data/backgrounds/test.jpg b/data/backgrounds/test.jpg new file mode 100644 index 0000000..5a7cc08 Binary files /dev/null and b/data/backgrounds/test.jpg differ diff --git a/data/backgrounds/test.png b/data/backgrounds/test.png new file mode 100644 index 0000000..8b74893 Binary files /dev/null and b/data/backgrounds/test.png differ diff --git a/data/compiler_test.vns b/data/compiler_test.vns index 2045487..f08b7b4 100644 --- a/data/compiler_test.vns +++ b/data/compiler_test.vns @@ -1,5 +1,6 @@ proc main { - "Hello!"; + "There's a ghost, in my home - but it's better than being alone. Reading read receipts with no replying."; + "Yes my house is haunted. That's just what I wanted. Read receipts with no replying."; "Scene test 123" #noclear; } \ No newline at end of file diff --git a/data/ddlc.vns b/data/ddlc.vns new file mode 100644 index 0000000..9e6a975 --- /dev/null +++ b/data/ddlc.vns @@ -0,0 +1,9 @@ +proc main +{ + "I see an annoying girl running toward me from the distance, waving her arms in the air like she's totally oblivious to any attention she might draw to herself."; + "That girl is Sayori, my neighbor and good friend since we were children."; + "You know, the kind of friend you'd never see yourself making today, but it just kind of works out because you've known each other for so long?"; + "We used to walk to school together on days like this, but starting around high school she would oversleep more and more frequently, and I would get tired of waiting up."; + "But if she's going to chase after me like this, I almost feel better off running away."; + "However, I just sigh and idle in front of the crosswalk and let Sayori catch up to me."; +} \ No newline at end of file diff --git a/data/gpt.vns b/data/gpt.vns new file mode 100644 index 0000000..c4e13b7 --- /dev/null +++ b/data/gpt.vns @@ -0,0 +1,51 @@ +proc main +{ + "morning, sunlight, birdsong, aroma, coffee, kitchen"; + + "You wake up to the sound of birds singing outside your window. The sunlight filters through the curtains, casting a warm glow on your room."; + + branch + { + "Get up and make coffee" + { + "You head to the kitchen and start brewing a fresh pot of coffee. The aroma fills the air, and you can't help but smile as you anticipate that first sip."; + + branch + { + "Enjoy the coffee in peace" + { + "You take your coffee to the cozy corner by the window. As you sip it, you watch the world go by outside, feeling a sense of calm wash over you."; + } + + "Invite your roommate to join" + { + "You hear your roommate stirring in their room. You decide to invite them over to share the morning coffee."; + + branch + { + "Roommate joins you" + { + "Your roommate joins you at the table. You both chat about your plans for the day and share a few laughs as you enjoy your coffee together."; + } + + "Roommate declines" + { + "Your roommate declines your invitation, mentioning they have some work to catch up on. You enjoy your coffee solo, lost in your thoughts."; + } + } + } + } + } + + "Stay in bed a little longer" + { + "You decide to indulge yourself and stay in bed a little longer. The warmth of the blankets cocoon you, and you drift in and out of pleasant dreams."; + + "Eventually, you decide it's time to start the day."; + } + } + + "Time seems to pass peacefully as you enjoy the simple pleasures of the morning."; + + jump main; +} \ No newline at end of file diff --git a/data/japanese.vns b/data/japanese.vns new file mode 100644 index 0000000..19d93d4 --- /dev/null +++ b/data/japanese.vns @@ -0,0 +1,34 @@ +proc main +{ + "人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは"; + "Yup, I added fucking support for japanese."; + "WHY DID I DO THIS!?!?!?!" #noawait; + branch + { + "Because you're stupid" + { + "HEY! You don't get to call me stupid!"; + } + + "Because you're dumb" + { + "Yeah, fair enough..."; + } + + "Because you're SOOO smart" + { + "HEY! NO SARCASM ALLOWED ON THIS CHRISTIAN MINECRAFT SERVER OKAY?"; + "I WANT TO SEE ABSOLUTELY ZERO, OKAY!?!?!?!"; + } + } + "Anyways, we're going to another function now, okay?"; + jump test; +} + +proc test +{ + "hi it's me sans undertale, from undertale"; + "let me show you some lit memmes"; + "DUHDUH DUH DUH - DUH DUH DUH DUHDUHDUH"; + jump main; +} \ No newline at end of file diff --git a/fonts/Merriweather-Regular.ttf b/fonts/Merriweather-Regular.ttf new file mode 100644 index 0000000..3fecc77 Binary files /dev/null and b/fonts/Merriweather-Regular.ttf differ diff --git a/fonts/NotoSansJP-Regular.ttf b/fonts/NotoSansJP-Regular.ttf new file mode 100644 index 0000000..1583096 Binary files /dev/null and b/fonts/NotoSansJP-Regular.ttf differ diff --git a/fonts/icons.ttf b/fonts/icons.ttf index 7d17c70..51f4741 100644 Binary files a/fonts/icons.ttf and b/fonts/icons.ttf differ