diff --git a/brightness.txt b/brightness.txt deleted file mode 100644 index 2d6c51f..0000000 --- a/brightness.txt +++ /dev/null @@ -1,45 +0,0 @@ -UI_Column() - { - UI_Spacer(UI_Em(1, 1)); - UI_Height(UI_Em(1, 1)) UI_Row() - { - UI_Width(UI_TextContent(15, 1)) UI_LabelF("Scrollbar T:"); - - 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())*ScrollbarT); - 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(ScrollbarT); - } - - r32 StartT = UI_GetDragR32(); - r32 EndT = StartT + Signal.DragDelta.x/(DimOfRange(Container->Rect).x-UI_TopFontSize()); - ScrollbarT = Clamp01(EndT); - } - } - - UI_Width(UI_TextContent(30, 1)) UI_Height(UI_Em(2, 1)) - { - if(UI_ButtonF("Reset Dialog").Pressed) - { - Time = 0.0f; - } - } - } - } \ No newline at end of file diff --git a/code/core/core.h b/code/core/core.h index be4a359..21c05f9 100644 --- a/code/core/core.h +++ b/code/core/core.h @@ -162,8 +162,7 @@ IsNull(p) ? (SetNull((n)->prev), (n)->next = (f), (IsNull(f) ? (0) : ((f)->prev #define DLLIsEmpty(First) ((First) == 0) #define SenDLLInit(Sentinel)\ -(Sentinel)->Next = (Sentinel);\ -(Sentinel)->Prev = (Sentinel); +do { (Sentinel)->Next = (Sentinel); (Sentinel)->Prev = (Sentinel); } while(0) #define SenDLLInsertFirst(Sentinel, Element)\ (Element)->Next = (Sentinel)->Next;\ @@ -199,42 +198,42 @@ auto __Temp = (Element)->Next->Prev;\ inline u64 AtomicExchangeU64(u64 volatile *Value, u64 New) { - u64 Result = _InterlockedExchange64((__int64 volatile *)Value, New); - return(Result); + u64 Result = _InterlockedExchange64((__int64 volatile *)Value, New); + return(Result); } inline u64 AtomicAddU64(u64 volatile *Value, u64 Addend) { - u64 Result = _InterlockedExchangeAdd64((__int64 volatile *)Value, Addend); - return(Result); + u64 Result = _InterlockedExchangeAdd64((__int64 volatile *)Value, Addend); + return(Result); } // sixten: Memory mutexes struct ticket_mutex { - u64 volatile Ticket; - u64 volatile Serving; + u64 volatile Ticket; + u64 volatile Serving; }; inline void BeginTicketMutex(ticket_mutex *Mutex) { - u64 Ticket = AtomicAddU64(&Mutex->Ticket, 1); - while(Ticket != Mutex->Serving) { _mm_pause(); } + u64 Ticket = AtomicAddU64(&Mutex->Ticket, 1); + while(Ticket != Mutex->Serving) { _mm_pause(); } } inline void EndTicketMutex(ticket_mutex *Mutex) { - AtomicAddU64(&Mutex->Serving, 1); + AtomicAddU64(&Mutex->Serving, 1); } #endif //- sixten: Axes enum axis2 { - Axis2_X = 0, - Axis2_Y = 1, - Axis2_Count + Axis2_X = 0, + Axis2_Y = 1, + Axis2_Count }; inline axis2 Opposite(axis2 Axis) { axis2 Result = (axis2)(!(u32)Axis); return(Result); } @@ -243,11 +242,11 @@ inline axis2 Opposite(axis2 Axis) { axis2 Result = (axis2)(!(u32)Axis); return(R //- sixten: Corners enum corner { - Corner_00 = 0, - Corner_10 = (1 << Axis2_X), - Corner_01 = (1 << Axis2_Y), - Corner_11 = (1 << Axis2_X)|(1 << Axis2_Y), - Corner_Count, + Corner_00 = 0, + Corner_10 = (1 << Axis2_X), + Corner_01 = (1 << Axis2_Y), + Corner_11 = (1 << Axis2_X)|(1 << Axis2_Y), + Corner_Count, }; #endif //CORE_H diff --git a/code/core/core_memory.cpp b/code/core/core_memory.cpp index 5ebb3c5..85a91f3 100644 --- a/code/core/core_memory.cpp +++ b/code/core/core_memory.cpp @@ -3,43 +3,43 @@ static void Copy(void *Dest, void *Source, umm Count) { - if(Count <= S32_Max) - { - u8 *Dest8 = (u8 *)Dest; - u8 *Source8 = (u8 *)Source; - - while(Count--) - { - *Dest8++ = *Source8++; - } - } + if(Count <= S32_Max) + { + u8 *Dest8 = (u8 *)Dest; + u8 *Source8 = (u8 *)Source; + + while(Count--) + { + *Dest8++ = *Source8++; + } + } } static void CopyReverse(void *Dest, void *Source, umm Count) { - if(Count <= S32_Max) - { - u8 *Dest8 = (u8 *)Dest + Count; - u8 *Source8 = (u8 *)Source + Count; - - while(Count--) - { - *--Dest8 = *--Source8; - } - } + if(Count <= S32_Max) + { + u8 *Dest8 = (u8 *)Dest + Count; + u8 *Source8 = (u8 *)Source + Count; + + while(Count--) + { + *--Dest8 = *--Source8; + } + } } static void Fill(void *Dest, u8 Value, umm Count) { - if(Count <= S32_Max) - { - u8 *Dest8 = (u8 *)Dest; - - while(Count--) - { - *Dest8++ = Value; - } - } + if(Count <= S32_Max) + { + u8 *Dest8 = (u8 *)Dest; + + while(Count--) + { + *Dest8++ = Value; + } + } } #if VN_ASAN_ENABLED @@ -51,199 +51,199 @@ static void Fill(void *Dest, u8 Value, umm Count) static arena *ArenaAlloc(u64 Size, b32 Chaining) { - arena *Arena = 0; - if(Chaining) - { - void *Memory = Platform.Allocate(Size); - Arena = (arena *)Memory; - + arena *Arena = 0; + if(Chaining) + { + void *Memory = Platform.Allocate(Size); + Arena = (arena *)Memory; + #if VN_ASAN_ENABLED - ASAN_POISON_MEMORY_REGION(Arena, Size); - ASAN_UNPOISON_MEMORY_REGION(Arena, sizeof(arena)); + ASAN_POISON_MEMORY_REGION(Arena, Size); + ASAN_UNPOISON_MEMORY_REGION(Arena, sizeof(arena)); #endif - - Arena->Chaining = true; - SenDLLInit(Arena); - - } - else - { - u64 RoundedSize = Size+Megabytes(64)-1; - RoundedSize -= RoundedSize&Megabytes(64); - void *Memory = Platform.Reserve(RoundedSize); - u64 InitialCommitSize = MEMORY_ARENA_COMMIT_SIZE; - Platform.Commit(Memory, InitialCommitSize); - Arena = (arena *)Memory; - } - Arena->Position = sizeof(arena); - Arena->Size = Size; - Arena->Align = 8; - return(Arena); + + Arena->Chaining = true; + SenDLLInit(Arena); + + } + else + { + u64 RoundedSize = Size+Megabytes(64)-1; + RoundedSize -= RoundedSize&Megabytes(64); + void *Memory = Platform.Reserve(RoundedSize); + u64 InitialCommitSize = MEMORY_ARENA_COMMIT_SIZE; + Platform.Commit(Memory, InitialCommitSize); + Arena = (arena *)Memory; + } + Arena->Position = sizeof(arena); + Arena->Size = Size; + Arena->Align = 8; + return(Arena); } static void ArenaRelease(arena *Arena) { - if(Arena->Chaining) - { - arena *Node = Arena->Prev; - for(;Node != Arena;) - { - arena *Next = Node->Prev; - Platform.Deallocate(Node); - Node = Next; - } + if(Arena->Chaining) + { + arena *Node = Arena->Prev; + for(;Node != Arena;) + { + arena *Next = Node->Prev; + Platform.Deallocate(Node); + Node = Next; + } #if VN_ASAN_ENABLED - ASAN_POISON_MEMORY_REGION(Arena, Arena->Size); + ASAN_POISON_MEMORY_REGION(Arena, Arena->Size); #endif - Platform.Deallocate(Arena); - } - else - { - Platform.Release(Arena); - } + Platform.Deallocate(Arena); + } + else + { + Platform.Release(Arena); + } } static void *ArenaPushNoClear(arena *Arena, u64 Size) { - void *Result = 0; - if(Arena->Chaining) - { - if(Size <= Arena->Size) - { - arena *Target = Arena->Prev; // sixten: We always append to the end of the list. - if(Target->Position + Size > Target->Size) - { - arena *New = ArenaAlloc(Arena->Size, true); - New->NotFirst = true; - SenDLLInsertLast(Arena, New); - Target = New; - } - u8 *Base = (u8 *)Target; - u64 PostAlignPos = (Target->Position+Target->Align-1); - PostAlignPos -= PostAlignPos%Target->Align; - Result = Base + PostAlignPos; - Target->Position = PostAlignPos+Size; - + void *Result = 0; + if(Arena->Chaining) + { + if(Size <= Arena->Size) + { + arena *Target = Arena->Prev; // sixten: We always append to the end of the list. + if(Target->Position + Size + Arena->Align > Target->Size) + { + arena *New = ArenaAlloc(Arena->Size, true); + New->NotFirst = true; + SenDLLInsertLast(Arena, New); + Target = New; + } + u8 *Base = (u8 *)Target; + u64 PostAlignPos = (Target->Position+Target->Align-1); + PostAlignPos -= PostAlignPos%Target->Align; + Result = Base + PostAlignPos; + Target->Position = PostAlignPos+Size; + #if VN_ASAN_ENABLED - ASAN_UNPOISON_MEMORY_REGION(Result, Size); + ASAN_UNPOISON_MEMORY_REGION(Result, Size); #endif - } - else - { - // sixten(NOTE): We can't really do anything in this situation. - } - } - else - { - if(Arena->Position + Size <= Arena->Size) - { - u8 *Base = (u8 *)Arena; - u64 PostAlignPos = (Arena->Position+Arena->Align-1); - PostAlignPos -= PostAlignPos%Arena->Align; - u64 Align = PostAlignPos - Arena->Position; - Result = Base + Arena->Position + Align; - Arena->Position += Size + Align; - if(Arena->CommitPosition < Arena->Position) - { - u64 ToCommit = Arena->Position - Arena->CommitPosition; - ToCommit += MEMORY_ARENA_COMMIT_SIZE - 1; - ToCommit -= ToCommit%MEMORY_ARENA_COMMIT_SIZE; - Platform.Commit(Base + Arena->CommitPosition, ToCommit); - Arena->CommitPosition += ToCommit; - } - } - else - { - InvalidCodepath; - } - } - return(Result); + } + else + { + // sixten(NOTE): We can't really do anything in this situation. + } + } + else + { + if(Arena->Position + Size <= Arena->Size) + { + u8 *Base = (u8 *)Arena; + u64 PostAlignPos = (Arena->Position+Arena->Align-1); + PostAlignPos -= PostAlignPos%Arena->Align; + u64 Align = PostAlignPos - Arena->Position; + Result = Base + Arena->Position + Align; + Arena->Position += Size + Align; + if(Arena->CommitPosition < Arena->Position) + { + u64 ToCommit = Arena->Position - Arena->CommitPosition; + ToCommit += MEMORY_ARENA_COMMIT_SIZE - 1; + ToCommit -= ToCommit%MEMORY_ARENA_COMMIT_SIZE; + Platform.Commit(Base + Arena->CommitPosition, ToCommit); + Arena->CommitPosition += ToCommit; + } + } + else + { + InvalidCodepath; + } + } + return(Result); } static void *ArenaPush(arena *Arena, u64 Size) { - void *Result = ArenaPushNoClear(Arena, Size); - Fill(Result, 0, Size); - return(Result); + void *Result = ArenaPushNoClear(Arena, Size); + Fill(Result, 0, Size); + return(Result); } static void ArenaPopTo(arena *Arena, u64 Position) { - if(Arena->Chaining) - { - // sixten: find the relevant arena - arena *Target = Arena; - for(int ArenaIndex = 0; ArenaIndex < Position/Arena->Size; ++ArenaIndex) - { - Target = Arena->Next; - } - - // sixten: deallocate all arenas that are no longer needed - { - arena *Node = Arena->Prev; - while(Node != Target) - { - arena *Prev = Node->Prev; - SenDLLRemove(Node); + if(Arena->Chaining) + { + // sixten: find the relevant arena + arena *Target = Arena; + for(int ArenaIndex = 0; ArenaIndex < Position/Arena->Size; ++ArenaIndex) + { + Target = Arena->Next; + } + + // sixten: deallocate all arenas that are no longer needed + { + arena *Node = Arena->Prev; + while(Node != Target) + { + arena *Prev = Node->Prev; + SenDLLRemove(Node); #if VN_ASAN_ENABLED - ASAN_POISON_MEMORY_REGION(Node, Node->Size); + ASAN_POISON_MEMORY_REGION(Node, Node->Size); #endif - Platform.Deallocate(Node); - Node = Prev; - } - } - - Target->Position = Position%Arena->Size; + Platform.Deallocate(Node); + Node = Prev; + } + } + + Target->Position = Position%Arena->Size; #if VN_ASAN_ENABLED - u64 UnpoisonPosition = Target->Position; - ASAN_POISON_MEMORY_REGION(Target, Target->Size); - ASAN_UNPOISON_MEMORY_REGION(Target, UnpoisonPosition); + u64 UnpoisonPosition = Target->Position; + ASAN_POISON_MEMORY_REGION(Target, Target->Size); + ASAN_UNPOISON_MEMORY_REGION(Target, UnpoisonPosition); #endif - } - else - { - u64 MinPosition = sizeof(*Arena); - u64 NewPosition = Maximum(MinPosition, Position); - Arena->Position = NewPosition; - u64 CommitAlignedPosition = Arena->Position+MEMORY_ARENA_COMMIT_SIZE-1; - CommitAlignedPosition -= CommitAlignedPosition%MEMORY_ARENA_COMMIT_SIZE; - if(CommitAlignedPosition + MEMORY_ARENA_DECOMMIT_THRESHOLD <= Arena->CommitPosition) - { - u8 *Base = (u8 *)Arena; - u64 ToDecommit = Arena->CommitPosition-CommitAlignedPosition; - Platform.Decommit(Base+CommitAlignedPosition, ToDecommit); - Arena->CommitPosition -= ToDecommit; - } - } + } + else + { + u64 MinPosition = sizeof(*Arena); + u64 NewPosition = Maximum(MinPosition, Position); + Arena->Position = NewPosition; + u64 CommitAlignedPosition = Arena->Position+MEMORY_ARENA_COMMIT_SIZE-1; + CommitAlignedPosition -= CommitAlignedPosition%MEMORY_ARENA_COMMIT_SIZE; + if(CommitAlignedPosition + MEMORY_ARENA_DECOMMIT_THRESHOLD <= Arena->CommitPosition) + { + u8 *Base = (u8 *)Arena; + u64 ToDecommit = Arena->CommitPosition-CommitAlignedPosition; + Platform.Decommit(Base+CommitAlignedPosition, ToDecommit); + Arena->CommitPosition -= ToDecommit; + } + } } static void ArenaPop(arena *Arena, u64 Amount) { - ArenaPopTo(Arena, Max(Arena->Position-Amount, (s64)sizeof(arena))); + ArenaPopTo(Arena, Max(Arena->Position-Amount, (s64)sizeof(arena))); } static void ArenaClear(arena *Arena) { - ArenaPopTo(Arena, sizeof(*Arena)); + ArenaPopTo(Arena, sizeof(*Arena)); } static void ArenaSetAlign(arena *Arena, u64 Align) { - Arena->Align = Align; + Arena->Align = Align; } //////////////////////////////// //- sixten: Temporary Memory Functions -static temporary_memory BeginTemporaryMemory(arena *Arena) +static temp BeginTemp(arena *Arena) { - temporary_memory Temp; - Temp.Arena = Arena; - Temp.Position = Arena->Position; - return(Temp); + temp Temp; + Temp.Arena = Arena; + Temp.Position = Arena->Position; + return(Temp); } -static void EndTemporaryMemory(temporary_memory Temp) +static void EndTemp(temp Temp) { - ArenaPopTo(Temp.Arena, Temp.Position); + ArenaPopTo(Temp.Arena, Temp.Position); } \ No newline at end of file diff --git a/code/core/core_memory.h b/code/core/core_memory.h index eb3c48f..c418c0b 100644 --- a/code/core/core_memory.h +++ b/code/core/core_memory.h @@ -34,21 +34,20 @@ static void Fill(void *Dest, u8 Value, umm Count); struct arena { - u64 Position; - u64 CommitPosition; - u64 Size; - u64 Align; - b32 Chaining; - b32 NotFirst; - arena *Next; - arena *Prev; - u64 Unused[1]; + u64 Position; + u64 CommitPosition; + u64 Size; + u64 Align; + b32 Chaining; + b32 NotFirst; + arena *Next; + arena *Prev; }; -struct temporary_memory +struct temp { - arena *Arena; - u64 Position; + arena *Arena; + u64 Position; }; //////////////////////////////// @@ -75,7 +74,7 @@ static void ArenaSetAlign(arena *Arena, u64 Align); //////////////////////////////// //- sixten: Temporary Memory Functions -static temporary_memory BeginTemporaryMemory(arena *Arena); -static void EndTemporaryMemory(temporary_memory Temp); +static temp BeginTemp(arena *Arena); +static void EndTemp(temp Temp); #endif //CORE_MEMORY_H diff --git a/code/core/core_string.cpp b/code/core/core_string.cpp index 677a9f7..8d45112 100644 --- a/code/core/core_string.cpp +++ b/code/core/core_string.cpp @@ -5,23 +5,23 @@ inline b32 IsWhitespace(char C) { - b32 Result = ((C == ' ') || - (C == '\n') || - (C == '\t') || - (C == '\r')); - return(Result); + b32 Result = ((C == ' ') || + (C == '\n') || + (C == '\t') || + (C == '\r')); + return(Result); } inline b32 IsDigit(char C) { - b32 Result = ((C >= '0') && (C <= '9')); - return(Result); + b32 Result = ((C >= '0') && (C <= '9')); + return(Result); } inline b32 IsLetter(char C) { - b32 Result = ((C >= 'A') && (C <= 'Z')) || ((C >= 'a') && (C <= 'z')); - return(Result); + b32 Result = ((C >= 'A') && (C <= 'Z')) || ((C >= 'a') && (C <= 'z')); + return(Result); } @@ -31,347 +31,347 @@ inline b32 IsLetter(char C) inline string MakeString(u8 *Data, s64 Count) { - string Result = {Count, Data}; - return(Result); + string Result = {Count, Data}; + return(Result); } inline string MakeString(u8 *Start, u8 *End) { - string Result = {(s64)(End-Start), Start}; - return(Result); + string Result = {(s64)(End-Start), Start}; + return(Result); } inline string MakeString(char *CString) { - string Result = {StringLength(CString), (u8 *)CString}; - return(Result); + string Result = {StringLength(CString), (u8 *)CString}; + return(Result); } inline string16 MakeString16(u16 *Data, s64 Count) { - string16 Result = {Count, Data}; - return(Result); + string16 Result = {Count, Data}; + return(Result); } //- sixten: Equality static b32 AreEqual(string A, string B) { - b32 Result = false; - if(A.Count == B.Count) - { - Result = true; - - for(s64 Index = 0; - Index < A.Count; - ++Index) - { - if(A.Data[Index] != B.Data[Index]) - { - Result = false; - break; - } - } - } - - return(Result); + b32 Result = false; + if(A.Count == B.Count) + { + Result = true; + + for(s64 Index = 0; + Index < A.Count; + ++Index) + { + if(A.Data[Index] != B.Data[Index]) + { + Result = false; + break; + } + } + } + + return(Result); } //- sixten: Substring static string Substring(string String, range1_s64 Range) { - string Result = MakeString(String.Data + Range.Min, DimOfRange(Range)); - return(Result); + string Result = MakeString(String.Data + Range.Min, DimOfRange(Range)); + return(Result); } static string Prefix(string String, s64 Count) { - range1_s64 Range = Range1S64(0, Count); - string Result = Substring(String, Range); - return(Result); + range1_s64 Range = Range1S64(0, Count); + string Result = Substring(String, Range); + return(Result); } static string Suffix(string String, s64 Count) { - range1_s64 Range = Range1S64(String.Count - Count, String.Count); - string Result = Substring(String, Range); - return(Result); + range1_s64 Range = Range1S64(String.Count - Count, String.Count); + string Result = Substring(String, Range); + return(Result); } //- sixten: Hashing static u64 HashString(string String) { - u64 Result = 5731; - for(s64 Index = 0; - Index < String.Count; - ++Index) - { - Result += String.Data[Index]; - Result ^= Result << 13; - Result ^= Result >> 7; - Result ^= Result << 17; - } - - return(Result); + u64 Result = 5731; + for(s64 Index = 0; + Index < String.Count; + ++Index) + { + Result += String.Data[Index]; + Result ^= Result << 13; + Result ^= Result >> 7; + Result ^= Result << 17; + } + + return(Result); } //- sixten: Searching static s64 FirstIndexOf(string String, char Char) { - s64 Result = -1; - for(s64 Index = 0; - Index < String.Count; - ++Index) - { - if(String.Data[Index] == Char) - { - Result = Index; - break; - } - } - return(Result); + s64 Result = -1; + for(s64 Index = 0; + Index < String.Count; + ++Index) + { + if(String.Data[Index] == Char) + { + Result = Index; + break; + } + } + return(Result); } static s64 LastIndexOf(string String, char Char) { - s64 Result = -1; - for(s64 Index = String.Count-1; - Index >= 0; - --Index) - { - if(String.Data[Index] == Char) - { - Result = Index; - break; - } - } - return(Result); + s64 Result = -1; + for(s64 Index = String.Count-1; + Index >= 0; + --Index) + { + if(String.Data[Index] == Char) + { + Result = Index; + break; + } + } + return(Result); } static s64 FirstIndexOf(string String, string Sub) { - s64 Result = -1; - if(String.Count >= Sub.Count) - { - for(s64 Index = 0; - Index < String.Count - Sub.Count; - ++Index) - { - string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count)); - if(AreEqual(ToCheck, Sub)) - { - Result = Index; - break; - } - } - } - - return(Result); + s64 Result = -1; + if(String.Count >= Sub.Count) + { + for(s64 Index = 0; + Index < String.Count - Sub.Count; + ++Index) + { + string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count)); + if(AreEqual(ToCheck, Sub)) + { + Result = Index; + break; + } + } + } + + return(Result); } static s64 LastIndexOf(string String, string Sub) { - s64 Result = -1; - if(String.Count >= Sub.Count) - { - for(s64 Index = String.Count - Sub.Count - 1; - Index >= 0; - --Index) - { - string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count)); - if(AreEqual(ToCheck, Sub)) - { - Result = Index; - break; - } - } - } - - return(Result); + s64 Result = -1; + if(String.Count >= Sub.Count) + { + for(s64 Index = String.Count - Sub.Count - 1; + Index >= 0; + --Index) + { + string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count)); + if(AreEqual(ToCheck, Sub)) + { + Result = Index; + break; + } + } + } + + return(Result); } //- sixten: Allocation static string PushString(arena *Arena, string String) { - string Result; - Result.Data = PushArrayNoClear(Arena, u8, String.Count); - Result.Count = String.Count; - Copy(Result.Data, String.Data, String.Count); - - return(Result); + string Result; + Result.Data = PushArrayNoClear(Arena, u8, String.Count); + Result.Count = String.Count; + Copy(Result.Data, String.Data, String.Count); + + return(Result); } static string PushFormatVariadic(arena *Arena, char *Format, va_list Arguments) { - va_list ArgumentsCopy; - va_copy(ArgumentsCopy, Arguments); - - string Result; - Result.Count = stbsp_vsnprintf(0, 0, Format, ArgumentsCopy); - Result.Data = PushArrayNoClear(Arena, u8, Result.Count + 1); - Result.Data[Result.Count] = 0; - - stbsp_vsnprintf((char *)Result.Data, (s32)Result.Count + 1, Format, Arguments); - - return(Result); + va_list ArgumentsCopy; + va_copy(ArgumentsCopy, Arguments); + + string Result; + Result.Count = stbsp_vsnprintf(0, 0, Format, ArgumentsCopy); + Result.Data = PushArrayNoClear(Arena, u8, Result.Count + 1); + Result.Data[Result.Count] = 0; + + stbsp_vsnprintf((char *)Result.Data, (s32)Result.Count + 1, Format, Arguments); + + return(Result); } static string PushFormat(arena *Arena, char *Format, ...) { - va_list Arguments; - va_start(Arguments, Format); - string Result = PushFormatVariadic(Arena, Format, Arguments); - va_end(Arguments); - - return(Result); + va_list Arguments; + va_start(Arguments, Format); + string Result = PushFormatVariadic(Arena, Format, Arguments); + va_end(Arguments); + + return(Result); } static string PushCString(arena *Arena, char *CString) { - string String = MakeString(CString); - string Result = PushString(Arena, String); - return(Result); + string String = MakeString(CString); + string Result = PushString(Arena, String); + return(Result); } //- sixten: Conversion static s64 ConvertStringToS64(string String) { - s64 Result = 0; - b32 IsNegative = false; - - s64 Index = 0; - if(String.Data[Index] == '-') - { - IsNegative = true; - ++Index; - } - - for(;Index < String.Count; ++Index) - { - u8 Char = String.Data[Index]; - Assert(IsDigit(Char)); - Result = Result*10 + (Char-'0'); - } - - if(IsNegative) - { - Result = -Result;; - } - - return(Result); + s64 Result = 0; + b32 IsNegative = false; + + s64 Index = 0; + if(String.Data[Index] == '-') + { + IsNegative = true; + ++Index; + } + + for(;Index < String.Count; ++Index) + { + u8 Char = String.Data[Index]; + Assert(IsDigit(Char)); + Result = Result*10 + (Char-'0'); + } + + if(IsNegative) + { + Result = -Result;; + } + + return(Result); } static string ConvertS64ToString(arena *Arena, s64 Value) { - b32 IsNegative = (Value < 0); - if(IsNegative) - { - Value = -Value; - } - - s64 DigitCount = (s64)Floor(Log(Max(Value, 1LL)) / Log(10)) + 1; - - s64 TotalBufferCount = DigitCount + IsNegative; - - string String = {TotalBufferCount, PushArray(Arena, u8, TotalBufferCount + 1)}; - String.Data[TotalBufferCount] = 0; - - if(IsNegative) - { - String.Data[0] = '-'; - } - - for(s64 Index = 0; - Index < DigitCount; - ++Index) - { - String.Data[TotalBufferCount - 1 - Index] = '0' + (Value % 10); - Value /= 10; - } - - return(String); + b32 IsNegative = (Value < 0); + if(IsNegative) + { + Value = -Value; + } + + s64 DigitCount = (s64)Floor(Log(Max(Value, 1LL)) / Log(10)) + 1; + + s64 TotalBufferCount = DigitCount + IsNegative; + + string String = {TotalBufferCount, PushArray(Arena, u8, TotalBufferCount + 1)}; + String.Data[TotalBufferCount] = 0; + + if(IsNegative) + { + String.Data[0] = '-'; + } + + for(s64 Index = 0; + Index < DigitCount; + ++Index) + { + String.Data[TotalBufferCount - 1 - Index] = '0' + (Value % 10); + Value /= 10; + } + + return(String); } static string StringFromCodepoint(arena *Arena, u32 Codepoint) { - char Buffer[5] = {}; - UTF8FromCodepoint((u8 *)Buffer, Codepoint); - - string Result = PushCString(Arena, Buffer); - return(Result); + char Buffer[5] = {}; + UTF8FromCodepoint((u8 *)Buffer, Codepoint); + + string Result = PushCString(Arena, Buffer); + return(Result); } static r64 DoubleFromString(string String) { - temporary_memory Scratch = GetScratch(); - string NullTerminated = PushString(Scratch.Arena, String); - r64 Result = strtod((char *)NullTerminated.Data, 0); - ReleaseScratch(Scratch); - return(Result); + temp Scratch = GetScratch(); + string NullTerminated = PushString(Scratch.Arena, String); + r64 Result = strtod((char *)NullTerminated.Data, 0); + ReleaseScratch(Scratch); + return(Result); } //- sixten: Replacing static string RemoveAll(arena *Arena, string Text, char ToRemove) { - //- sixten: get new count - s64 Occurrences = 0; - u8 *TextBegin = Text.Data; - u8 *TextEnd = TextBegin+Text.Count; - for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) - { - if(*Char == ToRemove) - { - Occurrences += 1; - } - } - - //- sixten: copy over all other bytes - s64 Count = Text.Count - Occurrences; - u8 *Data = PushArrayNoClear(Arena, u8, Count + 1); - Data[Count] = 0; - - s64 Index = 0; - for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) - { - if(*Char != ToRemove) - { - Data[Index] = *Char; - Index += 1; - } - } - - string Result = MakeString(Data, Count); - return(Result); + //- sixten: get new count + s64 Occurrences = 0; + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) + { + if(*Char == ToRemove) + { + Occurrences += 1; + } + } + + //- sixten: copy over all other bytes + s64 Count = Text.Count - Occurrences; + u8 *Data = PushArrayNoClear(Arena, u8, Count + 1); + Data[Count] = 0; + + s64 Index = 0; + for(u8 *Char = TextBegin; Char != TextEnd; Char += 1) + { + if(*Char != ToRemove) + { + Data[Index] = *Char; + Index += 1; + } + } + + string Result = MakeString(Data, Count); + return(Result); } //- sixten: "C Style" strings static s64 StringLength(char *String) { - s64 Result = 0; - while(*String++) - { - ++Result; - } - - return(Result); + s64 Result = 0; + while(*String++) + { + ++Result; + } + + return(Result); } static s64 StringLength16(u16 *String) { - s64 Result = 0; - while(*String++) - { - ++Result; - } - - return(Result); + s64 Result = 0; + while(*String++) + { + ++Result; + } + + return(Result); } @@ -379,148 +379,148 @@ static s64 StringLength16(u16 *String) static void AppendString(string_list *List, string String, arena *Arena) { - string_node *Node = PushStruct(Arena, string_node); - Node->String = String; - - List->TotalCount += String.Count; - - DLLInsertLast(List->First, List->Last, Node); + string_node *Node = PushStruct(Arena, string_node); + Node->String = String; + + List->TotalCount += String.Count; + + DLLInsertLast(List->First, List->Last, Node); } static string JoinStringList(string_list *List, arena *Arena) { - u8 *Buffer = PushArray(Arena, u8, List->TotalCount + 1); - Buffer[List->TotalCount] = 0; - - s64 GlobalIndex = 0; - - for(string_node *Node = List->First; - Node != 0; - Node = Node->Next) - { - string String = Node->String; - for(s64 Index = 0; - Index < String.Count; - ++Index) - { - Buffer[GlobalIndex++] = String.Data[Index]; - } - } - - string Result = MakeString(Buffer, List->TotalCount); - return(Result); + u8 *Buffer = PushArray(Arena, u8, List->TotalCount + 1); + Buffer[List->TotalCount] = 0; + + s64 GlobalIndex = 0; + + for(string_node *Node = List->First; + Node != 0; + Node = Node->Next) + { + string String = Node->String; + for(s64 Index = 0; + Index < String.Count; + ++Index) + { + Buffer[GlobalIndex++] = String.Data[Index]; + } + } + + string Result = MakeString(Buffer, List->TotalCount); + return(Result); } ///////////////////////////////////// //~ sixten: String Chunk Functions static string_chunk_list MakeStringChunkList(s64 ChunkSize) { - string_chunk_list Result = {}; - Result.ChunkSize = ChunkSize; - return(Result); + string_chunk_list Result = {}; + Result.ChunkSize = ChunkSize; + return(Result); } static string JoinStringChunkList(arena *Arena, string_chunk_list *List) { - string Result = {}; - Result.Count = List->TotalCount; - Result.Data = PushArrayNoClear(Arena, u8, List->TotalCount + 1); - s64 Index = 0; - s64 CountRemaining = List->TotalCount; - for(string_node *Node = List->First; Node != 0; Node = Node->Next) - { - string String = Node->String; - Copy(Result.Data + Index, String.Data, Min(CountRemaining, List->ChunkSize)); - CountRemaining -= List->ChunkSize; - Index += String.Count; - } - return(Result); + string Result = {}; + Result.Count = List->TotalCount; + Result.Data = PushArrayNoClear(Arena, u8, List->TotalCount + 1); + s64 Index = 0; + s64 CountRemaining = List->TotalCount; + for(string_node *Node = List->First; Node != 0; Node = Node->Next) + { + string String = Node->String; + Copy(Result.Data + Index, String.Data, Min(CountRemaining, List->ChunkSize)); + CountRemaining -= List->ChunkSize; + Index += String.Count; + } + return(Result); } // sixten(TODO): Incomplete, remove maybe? static void ReplaceRange(arena *Arena, string_chunk_list *List, string Text, range1_s64 Range) { - s64 NewTotalCount = Max(0ULL, List->TotalCount - DimOfRange(Range)) + Text.Count; - - //- sixten: do we need to allocate more chunks? - if(List->ChunkSize*List->ChunkCount < NewTotalCount) - { - s64 ChunksToAlloc = (NewTotalCount - List->ChunkSize*List->ChunkCount)/List->ChunkSize + 1; - for(s64 Index = 0; Index < ChunksToAlloc; Index += 1) - { - if(DLLIsEmpty(List->FirstFree)) - { - string_node *Node = PushStructNoClear(Arena, string_node); - Node->String.Count = 0; - Node->String.Data = PushArrayNoClear(Arena, u8, List->ChunkSize); - DLLInsertLast(List->First, List->Last, Node); - } - else - { - string_node *Node = List->FirstFree; - Node->String.Count = 0; - DLLRemove(List->FirstFree, List->LastFree, Node); - DLLInsertLast(List->First, List->Last, Node); - } - } - - List->ChunkCount += ChunksToAlloc; - } - - s64 CountDelta = NewTotalCount - List->TotalCount; - - // sixten: I cannot be bothered enough to figure out the correct implementation for this. However, if I do this - remember that you can rearrange - // the ordering of the linked list, instead of actually copying over the bytes for the majority of this. - Assert(AbsoluteValue(CountDelta) < List->ChunkSize); - - //- sixten: find the first and last affected nodes - s64 FirstAffectedNodeIndex = Range.Min/List->ChunkSize; - s64 LastAffectedNodeIndex = Range.Max/List->ChunkSize; - string_node *FirstAffectedNode = List->First; - for(s64 WalkIndex = 0; WalkIndex < FirstAffectedNodeIndex; WalkIndex += 1) - { - FirstAffectedNode = FirstAffectedNode->Next; - } - string_node *LastAffectedNode = FirstAffectedNode; - for(s64 WalkIndex = 0; WalkIndex < LastAffectedNodeIndex-FirstAffectedNodeIndex; WalkIndex += 1) - { - LastAffectedNode = LastAffectedNode->Next; - } - - if(CountDelta >= 0) - { - //- sixten: insertion - make room and the copy the data - s64 WriteOffset = Range.Min%List->ChunkSize; - for(string_node *Node = List->Last; Node != 0; Node = Node->Prev) - { - CopyReverse(Node->String.Data+CountDelta+WriteOffset, Node->String.Data+WriteOffset, List->ChunkSize-CountDelta-WriteOffset); - - if(Node == LastAffectedNode) - { - break; - } - else - { - Copy(Node->String.Data, Node->Prev->String.Data+List->ChunkSize-CountDelta, CountDelta); - } - } - s64 SourceOffset = 0; - for(string_node *Node = FirstAffectedNode; Node != 0; Node = Node->Next) - { - Copy(Node->String.Data+WriteOffset, Text.Data+SourceOffset, Min(List->ChunkSize-WriteOffset, Text.Count-SourceOffset)); - SourceOffset += List->ChunkSize; - if(Node == LastAffectedNode) - { - break; - } - } - } - else if(CountDelta < 0) - { - //- sixten: deletion - } - - List->TotalCount = NewTotalCount; + s64 NewTotalCount = Max(0ULL, List->TotalCount - DimOfRange(Range)) + Text.Count; + + //- sixten: do we need to allocate more chunks? + if(List->ChunkSize*List->ChunkCount < NewTotalCount) + { + s64 ChunksToAlloc = (NewTotalCount - List->ChunkSize*List->ChunkCount)/List->ChunkSize + 1; + for(s64 Index = 0; Index < ChunksToAlloc; Index += 1) + { + if(DLLIsEmpty(List->FirstFree)) + { + string_node *Node = PushStructNoClear(Arena, string_node); + Node->String.Count = 0; + Node->String.Data = PushArrayNoClear(Arena, u8, List->ChunkSize); + DLLInsertLast(List->First, List->Last, Node); + } + else + { + string_node *Node = List->FirstFree; + Node->String.Count = 0; + DLLRemove(List->FirstFree, List->LastFree, Node); + DLLInsertLast(List->First, List->Last, Node); + } + } + + List->ChunkCount += ChunksToAlloc; + } + + s64 CountDelta = NewTotalCount - List->TotalCount; + + // sixten: I cannot be bothered enough to figure out the correct implementation for this. However, if I do this - remember that you can rearrange + // the ordering of the linked list, instead of actually copying over the bytes for the majority of this. + Assert(AbsoluteValue(CountDelta) < List->ChunkSize); + + //- sixten: find the first and last affected nodes + s64 FirstAffectedNodeIndex = Range.Min/List->ChunkSize; + s64 LastAffectedNodeIndex = Range.Max/List->ChunkSize; + string_node *FirstAffectedNode = List->First; + for(s64 WalkIndex = 0; WalkIndex < FirstAffectedNodeIndex; WalkIndex += 1) + { + FirstAffectedNode = FirstAffectedNode->Next; + } + string_node *LastAffectedNode = FirstAffectedNode; + for(s64 WalkIndex = 0; WalkIndex < LastAffectedNodeIndex-FirstAffectedNodeIndex; WalkIndex += 1) + { + LastAffectedNode = LastAffectedNode->Next; + } + + if(CountDelta >= 0) + { + //- sixten: insertion - make room and the copy the data + s64 WriteOffset = Range.Min%List->ChunkSize; + for(string_node *Node = List->Last; Node != 0; Node = Node->Prev) + { + CopyReverse(Node->String.Data+CountDelta+WriteOffset, Node->String.Data+WriteOffset, List->ChunkSize-CountDelta-WriteOffset); + + if(Node == LastAffectedNode) + { + break; + } + else + { + Copy(Node->String.Data, Node->Prev->String.Data+List->ChunkSize-CountDelta, CountDelta); + } + } + s64 SourceOffset = 0; + for(string_node *Node = FirstAffectedNode; Node != 0; Node = Node->Next) + { + Copy(Node->String.Data+WriteOffset, Text.Data+SourceOffset, Min(List->ChunkSize-WriteOffset, Text.Count-SourceOffset)); + SourceOffset += List->ChunkSize; + if(Node == LastAffectedNode) + { + break; + } + } + } + else if(CountDelta < 0) + { + //- sixten: deletion + } + + List->TotalCount = NewTotalCount; } @@ -529,346 +529,346 @@ static void ReplaceRange(arena *Arena, string_chunk_list *List, string Text, ran read_only u8 UTF8Lengths[] = { - 1, 1, 1, 1, // 000xx - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 0, 0, 0, 0, // 100xx - 0, 0, 0, 0, - 2, 2, 2, 2, // 110xx - 3, 3, // 1110x - 4, // 11110 - 0, // 11111 + 1, 1, 1, 1, // 000xx + 1, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, + 0, 0, 0, 0, // 100xx + 0, 0, 0, 0, + 2, 2, 2, 2, // 110xx + 3, 3, // 1110x + 4, // 11110 + 0, // 11111 }; static string_decode DecodeUTF8Codepoint(u8 *Data, s64 Count) { - string_decode Result = {}; - u8 FirstByteMask[] = {0, 0x7F, 0x1F, 0x0F, 0x07}; - u8 FinalShift[] = {0, 18, 12, 6, 0}; - if(Count > 0) - { - Result.Codepoint = '#'; - Result.Size = 1; - - u8 Byte = Data[0]; - u8 Length = UTF8Lengths[Byte>>3]; - if(0 < Length && Length <= Count) - { - u32 Codepoint = (Byte&FirstByteMask[Length])<<18; - switch(Length) - { - case 4: {Codepoint |= ((Data[3] & 0x3F) << 0);} fallthrough; - case 3: {Codepoint |= ((Data[2] & 0x3F) << 6);} fallthrough; - case 2: {Codepoint |= ((Data[1] & 0x3F) << 12);} fallthrough; - default: break; - } - - Result.Codepoint = Codepoint >> FinalShift[Length]; - Result.Size = Length; - } - } - return(Result); + string_decode Result = {}; + u8 FirstByteMask[] = {0, 0x7F, 0x1F, 0x0F, 0x07}; + u8 FinalShift[] = {0, 18, 12, 6, 0}; + if(Count > 0) + { + Result.Codepoint = '#'; + Result.Size = 1; + + u8 Byte = Data[0]; + u8 Length = UTF8Lengths[Byte>>3]; + if(0 < Length && Length <= Count) + { + u32 Codepoint = (Byte&FirstByteMask[Length])<<18; + switch(Length) + { + case 4: {Codepoint |= ((Data[3] & 0x3F) << 0);} fallthrough; + case 3: {Codepoint |= ((Data[2] & 0x3F) << 6);} fallthrough; + case 2: {Codepoint |= ((Data[1] & 0x3F) << 12);} fallthrough; + default: break; + } + + Result.Codepoint = Codepoint >> FinalShift[Length]; + Result.Size = Length; + } + } + return(Result); } static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint) { - u32 Size = 0; - u8 DummyDest[4]; - Dest = Dest?Dest:DummyDest; - if(Codepoint < (1<<7)) - { - Dest[0] = Codepoint; - Size = 1; - } - else if (Codepoint < (1 << 11)) - { - Dest[0] = 0xC0|(Codepoint >> 6); - Dest[1] = 0x80|(Codepoint & 0x3F); - Size = 2; - } - else if (Codepoint < (1 << 16)) - { - Dest[0] = 0xE0|(Codepoint >> 12); - Dest[1] = 0x80|((Codepoint >> 6) & 0x3F); - Dest[2] = 0x80|(Codepoint & 0x3F); - Size = 3; - } - else if (Codepoint < (1 << 21)) - { - Dest[0] = 0xF0|(Codepoint >> 18); - Dest[1] = 0x80|((Codepoint >> 12) & 0x3F); - Dest[2] = 0x80|((Codepoint >> 6) & 0x3F); - Dest[3] = 0x80|(Codepoint & 0x3F); - Size = 4; - } - else - { - Dest[0] = '#'; - Size = 1; - } - return(Size); + u32 Size = 0; + u8 DummyDest[4]; + Dest = Dest?Dest:DummyDest; + if(Codepoint < (1<<7)) + { + Dest[0] = Codepoint; + Size = 1; + } + else if (Codepoint < (1 << 11)) + { + Dest[0] = 0xC0|(Codepoint >> 6); + Dest[1] = 0x80|(Codepoint & 0x3F); + Size = 2; + } + else if (Codepoint < (1 << 16)) + { + Dest[0] = 0xE0|(Codepoint >> 12); + Dest[1] = 0x80|((Codepoint >> 6) & 0x3F); + Dest[2] = 0x80|(Codepoint & 0x3F); + Size = 3; + } + else if (Codepoint < (1 << 21)) + { + Dest[0] = 0xF0|(Codepoint >> 18); + Dest[1] = 0x80|((Codepoint >> 12) & 0x3F); + Dest[2] = 0x80|((Codepoint >> 6) & 0x3F); + Dest[3] = 0x80|(Codepoint & 0x3F); + Size = 4; + } + else + { + Dest[0] = '#'; + Size = 1; + } + return(Size); } static string_decode DecodeUTF16Codepoint(u16 *Data, s64 Count) { - string_decode Result = {'#', 1}; - if(Data[0] < 0xD800 || 0xDFFF < Data[0]) - { - Result.Codepoint = Data[0]; - Result.Size = 1; - } - else if(Count >= 2) - { - if(0xD800 <= Data[0] && Data[0] < 0xDC00 && - 0xDC00 <= Data[1] && Data[1] < 0xE000) - { - Result.Codepoint = ((Data[0] - 0xD800)<<10)|(Data[1]-0xDC00); - Result.Size = 2; - } - } - return(Result); + string_decode Result = {'#', 1}; + if(Data[0] < 0xD800 || 0xDFFF < Data[0]) + { + Result.Codepoint = Data[0]; + Result.Size = 1; + } + else if(Count >= 2) + { + if(0xD800 <= Data[0] && Data[0] < 0xDC00 && + 0xDC00 <= Data[1] && Data[1] < 0xE000) + { + Result.Codepoint = ((Data[0] - 0xD800)<<10)|(Data[1]-0xDC00); + Result.Size = 2; + } + } + return(Result); } static u32 EncodeUTF16Codepoint(u16 *Dest, u32 Codepoint) { - u32 Size = 0; - u16 DummyDest[2]; - Dest = Dest?Dest:DummyDest; - if(Codepoint < 0x10000) - { - Dest[0] = Codepoint; - Size = 1; - } - else - { - Dest[0] = ((Codepoint - 0x10000) >> 10) + 0xD800; - Dest[1] = ((Codepoint - 0x10000) & 0x3FF) + 0xDC00; - Size = 2; - } - return(Size); + u32 Size = 0; + u16 DummyDest[2]; + Dest = Dest?Dest:DummyDest; + if(Codepoint < 0x10000) + { + Dest[0] = Codepoint; + Size = 1; + } + else + { + Dest[0] = ((Codepoint - 0x10000) >> 10) + 0xD800; + Dest[1] = ((Codepoint - 0x10000) & 0x3FF) + 0xDC00; + Size = 2; + } + return(Size); } static s64 UTF8IndexFromOffset(string String, s64 Offset) { - u8 *StringBegin = String.Data; - u8 *StringEnd = StringBegin+String.Count; - u8 *Byte = StringBegin; - for(;Byte < StringEnd && Offset > 0; Offset -= 1) - { - Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size; - } - s64 Result = Byte-StringBegin; - return(Result); + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + u8 *Byte = StringBegin; + for(;Byte < StringEnd && Offset > 0; Offset -= 1) + { + Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size; + } + s64 Result = Byte-StringBegin; + return(Result); } static s64 UTF8OffsetFromIndex(string String, s64 Index) { - s64 Offset = 0; - u8 *StringBegin = String.Data; - u8 *StringEnd = StringBegin+Min(Index, String.Count); - u8 *Byte = StringBegin; - for(;Byte < StringEnd;) - { - Offset += 1; - Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size; - } - return(Offset); + s64 Offset = 0; + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+Min(Index, String.Count); + u8 *Byte = StringBegin; + for(;Byte < StringEnd;) + { + Offset += 1; + Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size; + } + return(Offset); } static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint) { - s64 Length = 0; - if(Codepoint <= 0x7F) - { - Out[0] = (u8)Codepoint; - Length = 1; - } - else if(Codepoint <= 0x7FF) - { - Out[0] = (0x3 << 6) | ((Codepoint >> 6) & 0x1F); - Out[1] = 0x80 | ( Codepoint & 0x3F); - Length = 2; - } - else if(Codepoint <= 0xFFFF) - { - Out[0] = (0x7 << 5) | ((Codepoint >> 12) & 0x0F); - Out[1] = 0x80 | ((Codepoint >> 6) & 0x3F); - Out[2] = 0x80 | ( Codepoint & 0x3F); - Length = 3; - } - else if(Codepoint <= 0x10FFFF) - { - Out[0] = (0xF << 4) | ((Codepoint >> 12) & 0x07); - Out[1] = 0x80 | ((Codepoint >> 12) & 0x3F); - Out[2] = 0x80 | ((Codepoint >> 6) & 0x3F); - Out[3] = 0x80 | ( Codepoint & 0x3F); - Length = 4; - } - else - { - Out[0] = '?'; - Length = 1; - } - - return(Length); + s64 Length = 0; + if(Codepoint <= 0x7F) + { + Out[0] = (u8)Codepoint; + Length = 1; + } + else if(Codepoint <= 0x7FF) + { + Out[0] = (0x3 << 6) | ((Codepoint >> 6) & 0x1F); + Out[1] = 0x80 | ( Codepoint & 0x3F); + Length = 2; + } + else if(Codepoint <= 0xFFFF) + { + Out[0] = (0x7 << 5) | ((Codepoint >> 12) & 0x0F); + Out[1] = 0x80 | ((Codepoint >> 6) & 0x3F); + Out[2] = 0x80 | ( Codepoint & 0x3F); + Length = 3; + } + else if(Codepoint <= 0x10FFFF) + { + Out[0] = (0xF << 4) | ((Codepoint >> 12) & 0x07); + Out[1] = 0x80 | ((Codepoint >> 12) & 0x3F); + Out[2] = 0x80 | ((Codepoint >> 6) & 0x3F); + Out[3] = 0x80 | ( Codepoint & 0x3F); + Length = 4; + } + else + { + Out[0] = '?'; + Length = 1; + } + + return(Length); } static string String8FromString16(arena *Arena, string16 String) { - s64 AllocGuess = String.Count*3+1; - u8 *Memory = PushArray(Arena, u8, AllocGuess); - u16 *StringBegin = String.Data; - u16 *StringEnd = StringBegin+String.Count; - u16 *Source = StringBegin; - u8 *Dest = Memory; - for(;Source < StringEnd;) - { - string_decode Decode = DecodeUTF16Codepoint(Source, StringEnd-Source); - Dest += EncodeUTF8Codepoint(Dest, Decode.Codepoint); - Source += Decode.Size; - } - *Dest = 0; - s64 AllocUsed = Dest-Memory; - s64 AllocUnused = AllocGuess-AllocUsed; - Assert(AllocUnused >= 0); - ArenaPop(Arena, AllocUnused); - - string Result = MakeString(Memory, AllocUsed); - return(Result); + s64 AllocGuess = String.Count*3+1; + u8 *Memory = PushArray(Arena, u8, AllocGuess); + u16 *StringBegin = String.Data; + u16 *StringEnd = StringBegin+String.Count; + u16 *Source = StringBegin; + u8 *Dest = Memory; + for(;Source < StringEnd;) + { + string_decode Decode = DecodeUTF16Codepoint(Source, StringEnd-Source); + Dest += EncodeUTF8Codepoint(Dest, Decode.Codepoint); + Source += Decode.Size; + } + *Dest = 0; + s64 AllocUsed = Dest-Memory; + s64 AllocUnused = AllocGuess-AllocUsed; + Assert(AllocUnused >= 0); + ArenaPop(Arena, AllocUnused); + + string Result = MakeString(Memory, AllocUsed); + return(Result); } static string16 String16FromString8(arena *Arena, string String) { - s64 AllocGuess = String.Count*2+1; - u16 *Memory = PushArray(Arena, u16, AllocGuess); - u8 *StringBegin = String.Data; - u8 *StringEnd = StringBegin+String.Count; - u8 *Source = StringBegin; - u16 *Dest = Memory; - for(;Source < StringEnd;) - { - string_decode Decode = DecodeUTF8Codepoint(Source, StringEnd-Source); - Dest += EncodeUTF16Codepoint(Dest, Decode.Codepoint); - Source += Decode.Size; - } - *Dest = 0; - s64 AllocUsed = Dest-Memory; - s64 AllocUnused = AllocGuess-AllocUsed; - Assert(AllocUnused >= 0); - ArenaPop(Arena, AllocUnused); - - string16 Result = {AllocUsed, Memory}; - return(Result); + s64 AllocGuess = String.Count*2+1; + u16 *Memory = PushArray(Arena, u16, AllocGuess); + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + u8 *Source = StringBegin; + u16 *Dest = Memory; + for(;Source < StringEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Source, StringEnd-Source); + Dest += EncodeUTF16Codepoint(Dest, Decode.Codepoint); + Source += Decode.Size; + } + *Dest = 0; + s64 AllocUsed = Dest-Memory; + s64 AllocUnused = AllocGuess-AllocUsed; + Assert(AllocUnused >= 0); + ArenaPop(Arena, AllocUnused); + + string16 Result = {AllocUsed, Memory}; + return(Result); } //~ sixten: Text point static text_point TextPointFromOffset(string String, s64 Offset) { - text_point Point = {1, 1}; - for(s64 Index = 0; - Index < String.Count && Index < Offset; - ++Index) - { - if(String.Data[Index] == '\n') - { - ++Point.Line; - Point.Column = 1; - } - else - { - if(String.Data[Index] != '\r') - { - ++Point.Column; - } - } - } - - return(Point); + text_point Point = {1, 1}; + for(s64 Index = 0; + Index < String.Count && Index < Offset; + ++Index) + { + if(String.Data[Index] == '\n') + { + ++Point.Line; + Point.Column = 1; + } + else + { + if(String.Data[Index] != '\r') + { + ++Point.Column; + } + } + } + + return(Point); } static s64 OffsetFromTextPoint(string String, text_point Point) { - s64 Offset = 0; - - Point.Line -= 1; - Point.Column -= 1; - - u8 *StringBegin = String.Data; - u8 *StringEnd = StringBegin+String.Count; - u8 *Char = StringBegin; - //- sixten: find the start of the correct line - for(;Char < StringEnd && Point.Line > 0; Char += 1, Offset += 1) - { - if(*Char == '\n') - { - Point.Line -= 1; - } - } - - for(;Char < StringEnd && Point.Column > 0; Char += 1, Offset += 1) - { - //- sixten: if a newline has been reached, the initial column was out of bounds - if(*Char == '\n') - { - break; - } - //- sixten: tabs are two-spaces, so we must take that into account + s64 Offset = 0; + + Point.Line -= 1; + Point.Column -= 1; + + u8 *StringBegin = String.Data; + u8 *StringEnd = StringBegin+String.Count; + u8 *Char = StringBegin; + //- sixten: find the start of the correct line + for(;Char < StringEnd && Point.Line > 0; Char += 1, Offset += 1) + { + if(*Char == '\n') + { + Point.Line -= 1; + } + } + + for(;Char < StringEnd && Point.Column > 0; Char += 1, Offset += 1) + { + //- sixten: if a newline has been reached, the initial column was out of bounds + if(*Char == '\n') + { + break; + } + //- sixten: tabs are two-spaces, so we must take that into account #if 0 - if(*Char == '\t') - { - Point.Column -= 1; - } + if(*Char == '\t') + { + Point.Column -= 1; + } #endif - Point.Column -= 1; - } - - return(Offset); + Point.Column -= 1; + } + + return(Offset); } static text_range TextRange(text_point A, text_point B) { - text_range Result; - if(A.Line > B.Line || (A.Line == B.Line && A.Column > B.Column)) - { - Result = {B, A}; - } - else - { - Result = {A, B}; - } - return(Result); + text_range Result; + if(A.Line > B.Line || (A.Line == B.Line && A.Column > B.Column)) + { + Result = {B, A}; + } + else + { + Result = {A, B}; + } + return(Result); } //////////////////////////////// //~ sixten: 1D Interval List & Array Functions static void Range1S64ListPush(arena *Arena, range1_s64_list *List, range1_s64 Range) { - range1_s64_node *Node = PushStructNoClear(Arena, range1_s64_node); - Node->Range = Range; - QueuePush(List->First, List->Last, Node); - List->Count += 1; + range1_s64_node *Node = PushStructNoClear(Arena, range1_s64_node); + Node->Range = Range; + QueuePush(List->First, List->Last, Node); + List->Count += 1; } static range1_s64_array Range1S64ArrayFromList(arena *Arena, range1_s64_list *List) { - range1_s64_array Result = {}; - Result.Count = List->Count; - Result.Ranges = PushArray(Arena, range1_s64, List->Count); - s64 Index = 0; - for(range1_s64_node *Node = List->First; Node != 0; Node = Node->Next, Index += 1) - { - Result.Ranges[Index] = Node->Range; - } - return(Result); + range1_s64_array Result = {}; + Result.Count = List->Count; + Result.Ranges = PushArray(Arena, range1_s64, List->Count); + s64 Index = 0; + for(range1_s64_node *Node = List->First; Node != 0; Node = Node->Next, Index += 1) + { + Result.Ranges[Index] = Node->Range; + } + return(Result); } static s64 OffsetFromTextPoint(string String, range1_s64_array Lines, text_point Point) { - s64 LineIndex = Clamp(Point.Line, 1, Lines.Count) - 1; - range1_s64 Range = Lines.Ranges[LineIndex]; - s64 ColumnIndex = Clamp(Point.Column, 1, DimOfRange(Range)) - 1; - s64 Offset = Range.Min+ColumnIndex; - return(Offset); + s64 LineIndex = Clamp(Point.Line, 1, Lines.Count) - 1; + range1_s64 Range = Lines.Ranges[LineIndex]; + s64 ColumnIndex = Clamp(Point.Column, 1, DimOfRange(Range)) - 1; + s64 Offset = Range.Min+ColumnIndex; + return(Offset); } diff --git a/code/core/core_thread_context.cpp b/code/core/core_thread_context.cpp index 84ede7f..c589605 100644 --- a/code/core/core_thread_context.cpp +++ b/code/core/core_thread_context.cpp @@ -2,54 +2,54 @@ per_thread thread_context *ThreadLocal_ThreadContext = 0; static thread_context AllocateThreadContext(void) { - thread_context Context = {}; - for(s64 ArenaIndex = 0; ArenaIndex < ArrayCount(Context.Arenas); ArenaIndex += 1) - { - Context.Arenas[ArenaIndex] = ArenaAlloc(Megabytes(2), true); - } - return(Context); + thread_context Context = {}; + for(s64 ArenaIndex = 0; ArenaIndex < ArrayCount(Context.Arenas); ArenaIndex += 1) + { + Context.Arenas[ArenaIndex] = ArenaAlloc(Megabytes(2), true); + } + return(Context); } static void SetThreadContext(thread_context *Context) { - ThreadLocal_ThreadContext = Context; + ThreadLocal_ThreadContext = Context; } static thread_context *GetThreadContext(void) { - return(ThreadLocal_ThreadContext); + return(ThreadLocal_ThreadContext); } -static temporary_memory GetScratch(arena **Conflicts, u64 ConflictCount) +static temp GetScratch(arena **Conflicts, u64 ConflictCount) { - temporary_memory Scratch = {}; - thread_context *Context = GetThreadContext(); - - for(u64 ArenaIndex = 0; - ArenaIndex < ArrayCount(Context->Arenas); - ++ArenaIndex) - { - b32 FoundConflict = false; - for(u64 ConflictIndex = 0; - ConflictIndex < ConflictCount; - ++ConflictIndex) - { - arena *Conflict = Conflicts[ConflictIndex]; - if(Conflict == Context->Arenas[ArenaIndex]) - { - FoundConflict = true; - break; - } - } - - if(!FoundConflict) - { - Scratch = BeginTemporaryMemory(Context->Arenas[ArenaIndex]); - break; - } - } - - Assert(Scratch.Arena); - - return(Scratch); + temp Scratch = {}; + thread_context *Context = GetThreadContext(); + + for(u64 ArenaIndex = 0; + ArenaIndex < ArrayCount(Context->Arenas); + ++ArenaIndex) + { + b32 FoundConflict = false; + for(u64 ConflictIndex = 0; + ConflictIndex < ConflictCount; + ++ConflictIndex) + { + arena *Conflict = Conflicts[ConflictIndex]; + if(Conflict == Context->Arenas[ArenaIndex]) + { + FoundConflict = true; + break; + } + } + + if(!FoundConflict) + { + Scratch = BeginTemp(Context->Arenas[ArenaIndex]); + break; + } + } + + Assert(Scratch.Arena); + + return(Scratch); } diff --git a/code/core/core_thread_context.h b/code/core/core_thread_context.h index f16d15c..14fc88c 100644 --- a/code/core/core_thread_context.h +++ b/code/core/core_thread_context.h @@ -5,7 +5,7 @@ struct thread_context { - arena *Arenas[2]; + arena *Arenas[2]; }; //- sixten: Thread state management @@ -13,7 +13,7 @@ static void SetThreadContext(thread_context *Context); static thread_context *GetThreadContext(void); //- sixten: Scratch -static temporary_memory GetScratch(arena **Conflicts = 0, u64 ConflictCount = 0); -#define ReleaseScratch(Scratch) EndTemporaryMemory(Scratch) +static temp GetScratch(arena **Conflicts = 0, u64 ConflictCount = 0); +#define ReleaseScratch(Scratch) EndTemp(Scratch) #endif //CORE_THREAD_CONTEXT_H diff --git a/code/generated/vn_assets.meta.c b/code/generated/vn_assets.meta.c index 35f0e83..809b35b 100644 --- a/code/generated/vn_assets.meta.c +++ b/code/generated/vn_assets.meta.c @@ -1,14 +1,18 @@ -char * AssetPathLUT[5] = +char * AssetPathLUT[7] = { "", +"backgrounds/unknown.png", "backgrounds/test.jpg", +"backgrounds/ddlc.png", "characters/test_normal.png", "characters/test_happy.png", "characters/monika_leaning.png", }; -bool AssetIsPermanentLUT[5] = +bool AssetIsPermanentLUT[7] = { +true, +true, false, false, false, @@ -16,10 +20,12 @@ false, false, }; -char * AssetNameLUT[5] = +char * AssetNameLUT[7] = { "None", +"Error", "DemoBackground", +"DDLCBackground", "ArthurNormal", "ArthurHappy", "MonikaLeaning", diff --git a/code/generated/vn_assets.meta.h b/code/generated/vn_assets.meta.h index 26a6296..241079c 100644 --- a/code/generated/vn_assets.meta.h +++ b/code/generated/vn_assets.meta.h @@ -1,16 +1,19 @@ -enum asset_id +extern char * AssetPathLUT[7]; + +extern bool AssetIsPermanentLUT[7]; + +extern char * AssetNameLUT[7]; + +typedef s32 asset_id; +enum { AssetID_None, +AssetID_Error, AssetID_DemoBackground, +AssetID_DDLCBackground, AssetID_ArthurNormal, AssetID_ArthurHappy, AssetID_MonikaLeaning, AssetID_COUNT, }; -extern char * AssetPathLUT[5]; - -extern bool AssetIsPermanentLUT[5]; - -extern char * AssetNameLUT[5]; - diff --git a/code/opengl_render.cpp b/code/opengl_render.cpp index e619981..530ae44 100644 --- a/code/opengl_render.cpp +++ b/code/opengl_render.cpp @@ -7,128 +7,134 @@ static void OpenGL_DeallocateMemory(void *Memory); static void OpenGL_DebugMessageCallback(GLenum Source, GLenum Type, GLuint ID, GLenum Severity, GLsizei Length, const GLchar *Message, const void *UserParam) { - if(Severity == GL_DEBUG_SEVERITY_HIGH) - { - __debugbreak(); - } + if(Severity == GL_DEBUG_SEVERITY_HIGH) + { + __debugbreak(); + } } -inline render_handle OpenGL_GetHandleFromTexture(opengl_texture Texture) +inline render_handle OpenGL_HandleFromTexture(opengl_texture Texture) { - render_handle Result = {}; - Result.U32[0] = Texture.ID; - Result.U32[1] = Texture.Format; - Result.U32[2] = Texture.Dim.x; - Result.U32[3] = Texture.Dim.y; - return(Result); + render_handle Result = {}; + Result.U32[0] = Texture.ID; + Result.U32[1] = Texture.Format; + Result.U32[2] = Texture.Dim.x; + Result.U32[3] = Texture.Dim.y; + return(Result); } -inline opengl_texture OpenGL_GetTextureFromHandle(render_handle Handle) +inline opengl_texture OpenGL_TextureFromHandle(render_handle Handle) { - opengl_texture Result = {}; - Result.ID = Handle.U32[0]; - Result.Format = (render_texture_format)Handle.U32[1]; - Result.Dim.x = Handle.U32[2]; - Result.Dim.y = Handle.U32[3]; - return(Result); + opengl_texture Result = {}; + Result.ID = Handle.U32[0]; + Result.Format = (render_texture_format)Handle.U32[1]; + Result.Dim.x = Handle.U32[2]; + Result.Dim.y = Handle.U32[3]; + return(Result); } -inline u32 OpenGL_GetInternalFormatFromTextureFormat(render_texture_format Format) +inline u32 OpenGL_InternalFormatFromTextureFormat(render_texture_format Format) { - u32 InternalFormat = GL_INVALID_ENUM; - - if(Format == Render_TextureFormat_R8) - { - InternalFormat = GL_RED; - } - else if(Format == Render_TextureFormat_RGB8) - { - InternalFormat = GL_RGB; - } - else if(Format == Render_TextureFormat_RGBA8) - { - InternalFormat = GL_RGBA; - } - else - { - InvalidCodepath; - } - - return(InternalFormat); + u32 InternalFormat = GL_INVALID_ENUM; + + if(Format == Render_TextureFormat_R8) + { + InternalFormat = GL_RED; + } + else if(Format == Render_TextureFormat_RGB8) + { + InternalFormat = GL_RGB; + } + else if(Format == Render_TextureFormat_RGBA8) + { + InternalFormat = GL_RGBA; + } + else + { + InvalidCodepath; + } + + return(InternalFormat); } global GLint Global_OpenGL_SwizzleMaskR8[4] = {GL_ONE, GL_ONE, GL_ONE, GL_RED}; global GLint Global_OpenGL_SwizzleMaskRGB8[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}; global GLint Global_OpenGL_SwizzleMaskRGBA8[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; -inline GLint *OpenGL_GetSwizzleFromTextureFormat(render_texture_format Format) +inline GLint *OpenGL_SwizzleFromTextureFormat(render_texture_format Format) { - GLint *Swizzle = 0; - if(Format == Render_TextureFormat_R8) - { - Swizzle = Global_OpenGL_SwizzleMaskR8; - } - else if(Format == Render_TextureFormat_RGB8) - { - Swizzle = Global_OpenGL_SwizzleMaskRGB8; - } - else if(Format == Render_TextureFormat_RGBA8) - { - Swizzle = Global_OpenGL_SwizzleMaskRGBA8; - } - else - { - InvalidCodepath; - } - - return(Swizzle); + GLint *Swizzle = 0; + if(Format == Render_TextureFormat_R8) + { + Swizzle = Global_OpenGL_SwizzleMaskR8; + } + else if(Format == Render_TextureFormat_RGB8) + { + Swizzle = Global_OpenGL_SwizzleMaskRGB8; + } + else if(Format == Render_TextureFormat_RGBA8) + { + Swizzle = Global_OpenGL_SwizzleMaskRGBA8; + } + else + { + InvalidCodepath; + } + + return(Swizzle); } static RENDER_ALLOCATE_TEXTURE(OpenGL_AllocateTexture) { - opengl_texture Texture = {}; - Texture.Dim = Dim; - Texture.Format = Format; - - glGenTextures(1, &Texture.ID); - Assert(Texture.ID); - - u32 InternalFormat = OpenGL_GetInternalFormatFromTextureFormat(Format); - GLint *SwizzleMask = OpenGL_GetSwizzleFromTextureFormat(Format); - - glBindTexture(GL_TEXTURE_2D, Texture.ID); - glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Dim.x, Dim.y, 0, InternalFormat, GL_UNSIGNED_BYTE, Data); - - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, SwizzleMask); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if(GenerateMipmap) - { - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - render_handle Handle = OpenGL_GetHandleFromTexture(Texture); - return(Handle); + opengl_texture Texture = {}; + Texture.Dim = Dim; + Texture.Format = Format; + + glGenTextures(1, &Texture.ID); + Assert(Texture.ID); + + u32 InternalFormat = OpenGL_InternalFormatFromTextureFormat(Format); + GLint *SwizzleMask = OpenGL_SwizzleFromTextureFormat(Format); + + glBindTexture(GL_TEXTURE_2D, Texture.ID); + glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Dim.x, Dim.y, 0, InternalFormat, GL_UNSIGNED_BYTE, Data); + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, SwizzleMask); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if(GenerateMipmap) + { + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + render_handle Handle = OpenGL_HandleFromTexture(Texture); + return(Handle); +} + +RENDER_DEALLOCATE_TEXTURE(OpenGL_DeallocateTexture) +{ + opengl_texture Texture = OpenGL_TextureFromHandle(Handle); + glDeleteTextures(1, &Texture.ID); } static RENDER_FILL_REGION(OpenGL_FillRegion) { - opengl_texture Texture = OpenGL_GetTextureFromHandle(Handle); - - glBindTexture(GL_TEXTURE_2D, Texture.ID); - glTexSubImage2D(GL_TEXTURE_2D, 0, - DestP.x, DestP.y, DestDim.x, DestDim.y, - OpenGL_GetInternalFormatFromTextureFormat(Texture.Format), - GL_UNSIGNED_BYTE, Data); - glBindTexture(GL_TEXTURE_2D, 0); + opengl_texture Texture = OpenGL_TextureFromHandle(Handle); + + glBindTexture(GL_TEXTURE_2D, Texture.ID); + glTexSubImage2D(GL_TEXTURE_2D, 0, + DestP.x, DestP.y, DestDim.x, DestDim.y, + OpenGL_InternalFormatFromTextureFormat(Texture.Format), + GL_UNSIGNED_BYTE, Data); + glBindTexture(GL_TEXTURE_2D, 0); } global char *OpenGL_ShaderHeader = @@ -162,148 +168,148 @@ R"GLSL( static u32 OpenGL_CompileShaderProgram(char *VertexSource, char *FragmentSource) { - u32 Result = 0; - - u32 VertexShader = glCreateShader(GL_VERTEX_SHADER); - char *VertexSources[] = - { - OpenGL_ShaderHeader, - VertexSource - }; - glShaderSource(VertexShader, ArrayCount(VertexSources), VertexSources, 0); - glCompileShader(VertexShader); - - s32 VertexCompilationStatus; - glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &VertexCompilationStatus); - if(VertexCompilationStatus) - { - u32 FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - char *FragmentSources[] = - { - OpenGL_ShaderHeader, - FragmentSource - }; - glShaderSource(FragmentShader, ArrayCount(FragmentSources), FragmentSources, 0); - glCompileShader(FragmentShader); - - s32 FragmentCompilationStatus; - glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &FragmentCompilationStatus); - if(FragmentCompilationStatus) - { - u32 Program = glCreateProgram(); - - glAttachShader(Program, VertexShader); - glAttachShader(Program, FragmentShader); - glLinkProgram(Program); - - glDeleteShader(VertexShader); - glDeleteShader(FragmentShader); - - s32 ProgramLinkStatus; - glGetProgramiv(Program, GL_LINK_STATUS, &ProgramLinkStatus); - if(ProgramLinkStatus) - { - Result = Program; - } - else - { - char InfoLog[1024]; - glGetProgramInfoLog(Program, ArrayCount(InfoLog), 0, InfoLog); - - InvalidCodepath; - } - } - else - { - char InfoLog[1024]; - glGetShaderInfoLog(FragmentShader, ArrayCount(InfoLog), 0, InfoLog); - - InvalidCodepath; - } - } - else - { - char InfoLog[1024]; - glGetShaderInfoLog(VertexShader, ArrayCount(InfoLog), 0, InfoLog); - - InvalidCodepath; - } - - return(Result); + u32 Result = 0; + + u32 VertexShader = glCreateShader(GL_VERTEX_SHADER); + char *VertexSources[] = + { + OpenGL_ShaderHeader, + VertexSource + }; + glShaderSource(VertexShader, ArrayCount(VertexSources), VertexSources, 0); + glCompileShader(VertexShader); + + s32 VertexCompilationStatus; + glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &VertexCompilationStatus); + if(VertexCompilationStatus) + { + u32 FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + char *FragmentSources[] = + { + OpenGL_ShaderHeader, + FragmentSource + }; + glShaderSource(FragmentShader, ArrayCount(FragmentSources), FragmentSources, 0); + glCompileShader(FragmentShader); + + s32 FragmentCompilationStatus; + glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &FragmentCompilationStatus); + if(FragmentCompilationStatus) + { + u32 Program = glCreateProgram(); + + glAttachShader(Program, VertexShader); + glAttachShader(Program, FragmentShader); + glLinkProgram(Program); + + glDeleteShader(VertexShader); + glDeleteShader(FragmentShader); + + s32 ProgramLinkStatus; + glGetProgramiv(Program, GL_LINK_STATUS, &ProgramLinkStatus); + if(ProgramLinkStatus) + { + Result = Program; + } + else + { + char InfoLog[1024]; + glGetProgramInfoLog(Program, ArrayCount(InfoLog), 0, InfoLog); + + InvalidCodepath; + } + } + else + { + char InfoLog[1024]; + glGetShaderInfoLog(FragmentShader, ArrayCount(InfoLog), 0, InfoLog); + + InvalidCodepath; + } + } + else + { + char InfoLog[1024]; + glGetShaderInfoLog(VertexShader, ArrayCount(InfoLog), 0, InfoLog); + + InvalidCodepath; + } + + return(Result); } static quad_program OpenGL_CompileQuadProgram(void) { - char *VertexSource = - R"GLSL( + char *VertexSource = + R"GLSL( in v2 In_P; - in v2 In_SourceP; + in v2 In_SourceP; in s32 In_TextureIndex; - in u32 In_Color; + in u32 In_Color; in v2 In_ToCenter; in v2 In_HalfSize; - in r32 In_CornerRadius; - in r32 In_EdgeSoftness; - in r32 In_BorderThickness; + 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; - + "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" + R"GLSL( + uniform v2 Uniform_Resolution; + flat out s32 TextureIndex; - out v2 DestP; - out v2 DestHalfSize; - out v2 DestCenter; - out v2 SourceP; - out v4 Color; - out r32 CornerRadius; - out r32 EdgeSoftness; - out r32 BorderThickness; - - void main() - { - DestP = In_P; - DestCenter = In_P + In_ToCenter; - DestHalfSize = In_HalfSize; + out v2 DestP; + out v2 DestHalfSize; + out v2 DestCenter; + out v2 SourceP; + out v4 Color; + out r32 CornerRadius; + out r32 EdgeSoftness; + out r32 BorderThickness; + + void main() + { + DestP = In_P; + DestCenter = In_P + In_ToCenter; + DestHalfSize = In_HalfSize; - SourceP = In_SourceP; - - v2 ScreenP = DestP / Uniform_Resolution;; - ScreenP = ScreenP*2 - 1; - ScreenP.y = -ScreenP.y; - - gl_Position = V4(ScreenP, 0, 1); - Color.r = r32((In_Color >> 24) & 255u)/255.0; - Color.g = r32((In_Color >> 16) & 255u)/255.0; - Color.b = r32((In_Color >> 8) & 255u)/255.0; - Color.a = r32((In_Color >> 0) & 255u)/255.0; + SourceP = In_SourceP; + + v2 ScreenP = DestP / Uniform_Resolution;; + ScreenP = ScreenP*2 - 1; + ScreenP.y = -ScreenP.y; + + gl_Position = V4(ScreenP, 0, 1); + Color.r = r32((In_Color >> 24) & 255u)/255.0; + Color.g = r32((In_Color >> 16) & 255u)/255.0; + Color.b = r32((In_Color >> 8) & 255u)/255.0; + Color.a = r32((In_Color >> 0) & 255u)/255.0; TextureIndex = In_TextureIndex; - CornerRadius = In_CornerRadius; - EdgeSoftness = In_EdgeSoftness; - BorderThickness = In_BorderThickness; - })GLSL"; - - char *FragmentSource = - R"GLSL( + CornerRadius = In_CornerRadius; + EdgeSoftness = In_EdgeSoftness; + BorderThickness = In_BorderThickness; + })GLSL"; + + char *FragmentSource = + R"GLSL( - flat in s32 TextureIndex; - in v2 DestP; - in v2 DestHalfSize; - in v2 DestCenter; +flat in s32 TextureIndex; +in v2 DestP; +in v2 DestHalfSize; +in v2 DestCenter; in v2 SourceP; in v4 Color; - in r32 CornerRadius; - in r32 EdgeSoftness; - in r32 BorderThickness; +in r32 CornerRadius; +in r32 EdgeSoftness; +in r32 BorderThickness; )GLSL" - - "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" - - R"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; @@ -333,55 +339,55 @@ BorderFactor = smoothstep(0, 2*EdgeSoftness, InsideDist); v4 TextureFactor = texture(TextureSamplers[TextureIndex], SourceP); Out_Color = Color*TextureFactor*BorderFactor*SDFFactor; })GLSL"; - - quad_program Program = {}; - Program.ID = OpenGL_CompileShaderProgram(VertexSource, FragmentSource); - - Program.PID = glGetAttribLocation(Program.ID, "In_P"); - Program.SourcePID = glGetAttribLocation(Program.ID, "In_SourceP"); - Program.TextureIndexID = glGetAttribLocation(Program.ID, "In_TextureIndex"); - Program.ColorID = glGetAttribLocation(Program.ID, "In_Color"); - Program.ToCenterID = glGetAttribLocation(Program.ID, "In_ToCenter"); - Program.HalfSizeID = glGetAttribLocation(Program.ID, "In_HalfSize"); - 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); + + quad_program Program = {}; + Program.ID = OpenGL_CompileShaderProgram(VertexSource, FragmentSource); + + Program.PID = glGetAttribLocation(Program.ID, "In_P"); + Program.SourcePID = glGetAttribLocation(Program.ID, "In_SourceP"); + Program.TextureIndexID = glGetAttribLocation(Program.ID, "In_TextureIndex"); + Program.ColorID = glGetAttribLocation(Program.ID, "In_Color"); + Program.ToCenterID = glGetAttribLocation(Program.ID, "In_ToCenter"); + Program.HalfSizeID = glGetAttribLocation(Program.ID, "In_HalfSize"); + 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"); + + temp 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); } static instanced_quad_program OpenGL_CompileInstacedQuadProgram(void) { - char *VertexSource = - R"GLSL( + char *VertexSource = + R"GLSL( - in v4 In_Dest; + 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; + 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 sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" + R"GLSL( + uniform v2 Uniform_Resolution; out v2 DestP; @@ -406,40 +412,40 @@ SourceP = LinearBlend(In_Source.xy, In_Source.zw, Vertices[gl_VertexID]); v2 ScreenP = V2(DestP.x / Uniform_Resolution.x, DestP.y / Uniform_Resolution.y); ScreenP = ScreenP*2 - 1; - ScreenP.y = -ScreenP.y; +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; + 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; +EdgeSoftness = In_EdgeSoftness; +BorderThickness = In_BorderThickness; TextureIndex = In_TextureIndex; } )GLSL"; - - char *FragmentSource = - R"GLSL( + + char *FragmentSource = + R"GLSL( - in v2 DestP; - in v2 DestHalfSize; - in v2 DestCenter; +in v2 DestP; +in v2 DestHalfSize; +in v2 DestCenter; in v2 SourceP; in v4 Color; - in r32 CornerRadius; - in r32 EdgeSoftness; - in r32 BorderThickness; +in r32 CornerRadius; +in r32 EdgeSoftness; +in r32 BorderThickness; flat in s32 TextureIndex; )GLSL" - "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" - R"GLSL( - + "uniform sampler2D TextureSamplers[" Stringify(MAX_BOUND_TEXTURES) "];" + R"GLSL( + out v4 Out_Color; r32 RoundedRect(v2 P, v2 Center, v2 HalfSize, r32 r) @@ -474,38 +480,38 @@ 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); + + 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"); + + temp 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)\ @@ -538,267 +544,268 @@ glDisableVertexAttribArray(Index);\ static void OpenGL_BeginProgram(quad_program *Program) { - glUseProgram(Program->ID); - - OpenGL_EnableFloatVertexAttribute(Program->PID, 2, GL_FLOAT, quad_vertex, P); - OpenGL_EnableFloatVertexAttribute(Program->SourcePID, 2, GL_FLOAT, quad_vertex, SourceP); - OpenGL_EnableIntegerVertexAttribute(Program->TextureIndexID, 1, GL_UNSIGNED_INT, quad_vertex, TextureIndex); - OpenGL_EnableIntegerVertexAttribute(Program->ColorID, 1, GL_UNSIGNED_INT, quad_vertex, Color); - OpenGL_EnableFloatVertexAttribute(Program->ToCenterID, 2, GL_FLOAT, quad_vertex, ToCenter); - OpenGL_EnableFloatVertexAttribute(Program->HalfSizeID, 2, GL_FLOAT, quad_vertex, HalfSize); - OpenGL_EnableFloatVertexAttribute(Program->CornerRadiusID, 1, GL_FLOAT, quad_vertex, CornerRadius); - OpenGL_EnableFloatVertexAttribute(Program->EdgeSoftnessID, 1, GL_FLOAT, quad_vertex, EdgeSoftness); - OpenGL_EnableFloatVertexAttribute(Program->BorderThicknessID, 1, GL_FLOAT, quad_vertex, BorderThickness); + glUseProgram(Program->ID); + + OpenGL_EnableFloatVertexAttribute(Program->PID, 2, GL_FLOAT, quad_vertex, P); + OpenGL_EnableFloatVertexAttribute(Program->SourcePID, 2, GL_FLOAT, quad_vertex, SourceP); + OpenGL_EnableIntegerVertexAttribute(Program->TextureIndexID, 1, GL_UNSIGNED_INT, quad_vertex, TextureIndex); + OpenGL_EnableIntegerVertexAttribute(Program->ColorID, 1, GL_UNSIGNED_INT, quad_vertex, Color); + OpenGL_EnableFloatVertexAttribute(Program->ToCenterID, 2, GL_FLOAT, quad_vertex, ToCenter); + OpenGL_EnableFloatVertexAttribute(Program->HalfSizeID, 2, GL_FLOAT, quad_vertex, HalfSize); + OpenGL_EnableFloatVertexAttribute(Program->CornerRadiusID, 1, GL_FLOAT, quad_vertex, CornerRadius); + OpenGL_EnableFloatVertexAttribute(Program->EdgeSoftnessID, 1, GL_FLOAT, quad_vertex, EdgeSoftness); + OpenGL_EnableFloatVertexAttribute(Program->BorderThicknessID, 1, GL_FLOAT, quad_vertex, BorderThickness); } static void OpenGL_EndProgram(quad_program *Program) { - OpenGL_DisableVertexAttribute(Program->PID); - OpenGL_DisableVertexAttribute(Program->SourcePID); - OpenGL_DisableVertexAttribute(Program->TextureIndexID); - OpenGL_DisableVertexAttribute(Program->ColorID); - OpenGL_DisableVertexAttribute(Program->ToCenterID); - OpenGL_DisableVertexAttribute(Program->HalfSizeID); - OpenGL_DisableVertexAttribute(Program->CornerRadiusID); - OpenGL_DisableVertexAttribute(Program->EdgeSoftnessID); - OpenGL_DisableVertexAttribute(Program->BorderThicknessID); - - glUseProgram(0); + OpenGL_DisableVertexAttribute(Program->PID); + OpenGL_DisableVertexAttribute(Program->SourcePID); + OpenGL_DisableVertexAttribute(Program->TextureIndexID); + OpenGL_DisableVertexAttribute(Program->ColorID); + OpenGL_DisableVertexAttribute(Program->ToCenterID); + OpenGL_DisableVertexAttribute(Program->HalfSizeID); + OpenGL_DisableVertexAttribute(Program->CornerRadiusID); + OpenGL_DisableVertexAttribute(Program->EdgeSoftnessID); + OpenGL_DisableVertexAttribute(Program->BorderThicknessID); + + 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); + 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); + 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 = {}; - - RenderCommands->MaxPushBufferSize = MaxPushBufferSize; - RenderCommands->PushBufferBase = (u8 *)OpenGL_AllocateMemory(RenderCommands->MaxPushBufferSize); - + opengl_context Context = {}; + + 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)); + 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)); + 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)); #endif - + #if VN_USE_INSTANCING - Context.InstancedQuadProgram = OpenGL_CompileInstacedQuadProgram(); + Context.InstancedQuadProgram = OpenGL_CompileInstacedQuadProgram(); #else - Context.QuadProgram = OpenGL_CompileQuadProgram(); + Context.QuadProgram = OpenGL_CompileQuadProgram(); #endif - + #if VN_USE_INSTANCING - glGenBuffers(1, &Context.InstancedQuadBuffer); + glGenBuffers(1, &Context.InstancedQuadBuffer); #else - glGenBuffers(1, &Context.VertexBuffer); - glGenBuffers(1, &Context.IndexBuffer); + 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); + 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); - - 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); + glBindBuffer(GL_ARRAY_BUFFER, Context.VertexBuffer); + glBufferData(GL_ARRAY_BUFFER, RenderCommands->MaxQuadVertexCount*sizeof(quad_vertex), 0, GL_STREAM_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + 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, false, &WhiteData); - - RenderCommands->AllocateTexture = OpenGL_AllocateTexture; - RenderCommands->FillRegion = OpenGL_FillRegion; - + + u32 WhiteData = 0xFFFFFFFF; + RenderCommands->WhiteTexture = OpenGL_AllocateTexture(V2S32(1, 1), Render_TextureFormat_RGBA8, false, &WhiteData); + + RenderCommands->AllocateTexture = OpenGL_AllocateTexture; + RenderCommands->DeallocateTexture = OpenGL_DeallocateTexture; + RenderCommands->FillRegion = OpenGL_FillRegion; + #if VN_SLOW&&0 - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(OpenGL_DebugMessageCallback, 0); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(OpenGL_DebugMessageCallback, 0); #endif - - u32 DummyVertexArray; - glGenVertexArrays(1, &DummyVertexArray); - glBindVertexArray(DummyVertexArray); - - return(Context); + + u32 DummyVertexArray; + glGenVertexArrays(1, &DummyVertexArray); + glBindVertexArray(DummyVertexArray); + + return(Context); } static void OpenGL_BeginFrame(vn_render_commands *RenderCommands, v2 RenderDim) { - RenderCommands->PushBufferAt = RenderCommands->PushBufferBase; + RenderCommands->PushBufferAt = RenderCommands->PushBufferBase; #if VN_USE_INSTANCING - RenderCommands->InstancedQuadCount = 0; + RenderCommands->InstancedQuadCount = 0; #else - RenderCommands->QuadVertexCount = 0; - RenderCommands->QuadIndexCount = 0; + RenderCommands->QuadVertexCount = 0; + RenderCommands->QuadIndexCount = 0; #endif - - RenderCommands->RenderDim = RenderDim; + + RenderCommands->RenderDim = RenderDim; } static void OpenGL_EndFrame(opengl_context *Context, vn_render_commands *RenderCommands) { - glViewport(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); - glScissor(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnable(GL_SCISSOR_TEST); - + glViewport(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); + glScissor(0, 0, RenderCommands->RenderDim.x, RenderCommands->RenderDim.y); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_SCISSOR_TEST); + #if !VN_USE_INSTANCING - glBindBuffer(GL_ARRAY_BUFFER, Context->VertexBuffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context->IndexBuffer); - - void *VertexData = RenderCommands->QuadVertexBase; - umm VertexSize = RenderCommands->QuadVertexCount*sizeof(quad_vertex); - glBufferSubData(GL_ARRAY_BUFFER, 0, VertexSize, VertexData); - - void *IndexData = RenderCommands->QuadIndexBase; - umm IndexSize = RenderCommands->QuadIndexCount*sizeof(s32); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, IndexSize, IndexData); + glBindBuffer(GL_ARRAY_BUFFER, Context->VertexBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Context->IndexBuffer); + + void *VertexData = RenderCommands->QuadVertexBase; + umm VertexSize = RenderCommands->QuadVertexCount*sizeof(quad_vertex); + glBufferSubData(GL_ARRAY_BUFFER, 0, VertexSize, VertexData); + + void *IndexData = RenderCommands->QuadIndexBase; + umm IndexSize = RenderCommands->QuadIndexCount*sizeof(s32); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, IndexSize, IndexData); #endif - - for(u8 *PushBufferAt = RenderCommands->PushBufferBase; - PushBufferAt < RenderCommands->PushBufferAt;) - { - render_command_header *Header = (render_command_header *)PushBufferAt; - PushBufferAt += sizeof(*Header); - - switch(Header->Type) - { - case Render_Command_render_command_clear: - { - render_command_clear *Command = (render_command_clear *)PushBufferAt; - PushBufferAt += sizeof(*Command); - - glClearColor(Command->Color.r, Command->Color.g, Command->Color.b, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - } break; - + + for(u8 *PushBufferAt = RenderCommands->PushBufferBase; + PushBufferAt < RenderCommands->PushBufferAt;) + { + render_command_header *Header = (render_command_header *)PushBufferAt; + PushBufferAt += sizeof(*Header); + + switch(Header->Type) + { + case Render_Command_render_command_clear: + { + render_command_clear *Command = (render_command_clear *)PushBufferAt; + PushBufferAt += sizeof(*Command); + + glClearColor(Command->Color.r, Command->Color.g, Command->Color.b, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } break; + #if VN_USE_INSTANCING - case Render_Command_render_command_instanced_quads: - { - render_command_instanced_quads *Command = (render_command_instanced_quads *)PushBufferAt; - PushBufferAt += sizeof(*Command); - - 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); - } - - 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; - - case Render_Command_render_command_clip: - { - render_command_clip *Command = (render_command_clip *)PushBufferAt; - PushBufferAt += sizeof(*Command); - - 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; - + case Render_Command_render_command_instanced_quads: + { + render_command_instanced_quads *Command = (render_command_instanced_quads *)PushBufferAt; + PushBufferAt += sizeof(*Command); + + render_texture_mapping *Mapping = &Command->Mapping; + + for(s32 TextureIndex = 0; + TextureIndex < Mapping->TexturesUsed; + ++TextureIndex) + { + opengl_texture Texture = OpenGL_TextureFromHandle(Mapping->Textures[TextureIndex]); + glActiveTexture(GL_TEXTURE0 + TextureIndex); + glBindTexture(GL_TEXTURE_2D, Texture.ID); + } + + 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; + + case Render_Command_render_command_clip: + { + render_command_clip *Command = (render_command_clip *)PushBufferAt; + PushBufferAt += sizeof(*Command); + + 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; + 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_TextureFromHandle(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; - } - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); + + InvalidDefaultCase; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); } \ No newline at end of file diff --git a/code/vn.cpp b/code/vn.cpp index 1751eef..b7ce884 100644 --- a/code/vn.cpp +++ b/code/vn.cpp @@ -7,10 +7,10 @@ #if VN_INTERNAL struct debug_settings { - b32 RenderUIDebugRects; - b32 RenderFPSCounter; - b32 ListHotAndActive; - b32 ShowWelcomeMessage; + b32 RenderUIDebugRects; + b32 RenderFPSCounter; + b32 ListHotAndActive; + b32 ShowWelcomeMessage; }; global debug_settings *DEBUG_DebugSettings = 0; @@ -32,6 +32,9 @@ global debug_settings *DEBUG_DebugSettings = 0; #if VN_INTERNAL #include "vn_workspace.h" +#include "vn_debug_info.h" + +global vn_render_commands *GlobalRenderCommands = 0; #endif #include "generated/vn_character.meta.c" @@ -49,199 +52,179 @@ global debug_settings *DEBUG_DebugSettings = 0; #if VN_INTERNAL #include "vn_workspace.cpp" +#include "vn_debug_info.cpp" #endif struct vn_state { - arena *Arena; - arena *FrameArena; - - assets Assets; - - glyph_atlas *GlyphAtlas; - - config *Config; - - ui UI; - animation_curve_state AnimationCurveState; - b32 EditorMode; - - render_handle BackgroundTexture; - - scene_view SceneView; - + arena *Arena; + arena *FrameArena; + + assets Assets; + + glyph_atlas *GlyphAtlas; + + config *Config; + + ui UI; + animation_curve_state AnimationCurveState; + b32 EditorMode; + + render_handle BackgroundTexture; + + scene_view SceneView; + #if VN_INTERNAL - workspace Workspace; - debug_settings DebugSettings; + debug_info *DebugInfo; + workspace Workspace; + debug_settings DebugSettings; #endif }; -static render_handle CreateTextureFromPath(vn_render_commands *Commands, string Path) -{ - render_handle Result = {}; - - temporary_memory Scratch = GetScratch(); - platform_file_handle File = Platform.OpenFile(Path, PlatformAccess_Read); - if(File.IsValid) - { - u64 DataCount = Platform.GetFileSize(File); - u8 *Data = PushArray(Scratch.Arena, u8, DataCount); - Platform.ReadFile(File, Data, 0, DataCount); - - s32 Width, Height, BPP; - u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0); - - render_texture_format TextureFormat = Render_TextureFormat_Invalid; - switch(BPP) - { - InvalidDefaultCase; - case 1: { TextureFormat = Render_TextureFormat_R8; } break; - case 3: { TextureFormat = Render_TextureFormat_RGB8; } break; - case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break; - } - - Result = Commands->AllocateTexture(V2S32(Width, Height), TextureFormat, true, TextureData); - - stbi_image_free(TextureData); - - Platform.CloseFile(File); - } - ReleaseScratch(Scratch); - return(Result); -} - VN_UPDATE_AND_RENDER(VN_UpdateAndRender) { - SetThreadContext(ThreadContext); - Platform = Memory->PlatformAPI; - - vn_state *State = Memory->State; - - //- sixten: initialize application state - if(!Memory->State) - { - arena *Arena = ArenaAlloc(Kilobytes(24), true); - State = Memory->State = PushStruct(Arena, vn_state); - State->Arena = Arena; - State->FrameArena = ArenaAlloc(Megabytes(1), true); - - State->GlyphAtlas = CreateGlyphAtlas(RenderCommands); - State->Config = CreateConfig(); - - //- sixten: load assets - State->Assets.AllocateTexture = RenderCommands->AllocateTexture; - LoadPermanentAssets(&State->Assets); - - State->BackgroundTexture = TextureFromAssetID(AssetID_DemoBackground); - - //- sixten: setup config binds and load current config - { - Config_BindS32(State->Config, StrLit("Platform/RefreshRate"), &Input->RefreshRate, 60); + SetThreadContext(ThreadContext); + Platform = Memory->PlatformAPI; + #if VN_INTERNAL - DEBUG_DebugSettings = &State->DebugSettings; - Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &DEBUG_DebugSettings->RenderUIDebugRects, 0); - Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &DEBUG_DebugSettings->RenderFPSCounter, 0); - Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &DEBUG_DebugSettings->ListHotAndActive, 0); - Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1); + GlobalRenderCommands = RenderCommands; #endif - - Config_ReadFile(State->Config, StrLit("config.vn")); - } - - scene_view *SceneView = &State->SceneView; - SV_Init(SceneView, State->Arena); - - //- sixten: load startup scene - temporary_memory Scratch = GetScratch(); - string SceneInput = Platform_ReadEntireFile(Scratch.Arena, StrLit("data/scene.vns")); - compiled_scene Scene = S_ScriptFromText(Scratch.Arena, SceneInput); - SV_SetCurrentSource(&Scene); - ReleaseScratch(Scratch); - - SceneView->TestHappy = TextureFromAssetID(AssetID_ArthurHappy); - SceneView->TestNormal = TextureFromAssetID(AssetID_ArthurNormal); - SceneView->MonikaLeaning = TextureFromAssetID(AssetID_MonikaLeaning); - - SceneView->BackgroundTexture = State->BackgroundTexture; - - UI_Init(&State->UI); - AC_Init(&State->AnimationCurveState); - + + vn_state *State = Memory->State; + + //- sixten: initialize application state + if(!Memory->State) + { + arena *Arena = ArenaAlloc(Kilobytes(24), true); + State = Memory->State = PushStruct(Arena, vn_state); + State->Arena = Arena; + State->FrameArena = ArenaAlloc(Megabytes(1), true); + + State->GlyphAtlas = CreateGlyphAtlas(RenderCommands); + State->Config = CreateConfig(); + + //- sixten: load assets + State->Assets.AllocateTexture = RenderCommands->AllocateTexture; + LoadPermanentAssets(&State->Assets); + + State->BackgroundTexture = TextureFromAssetID(AssetID_DemoBackground); + + //- sixten: setup config binds and load current config + { + Config_BindS32(State->Config, StrLit("Platform/RefreshRate"), &Input->RefreshRate, 60); #if VN_INTERNAL - W_Init(&State->Workspace); + State->DebugInfo = DI_DebugInfoAlloc(); + + DEBUG_DebugSettings = &State->DebugSettings; + Config_BindB32(State->Config, StrLit("Dev/RenderUIDebugRects"), &DEBUG_DebugSettings->RenderUIDebugRects, 0); + Config_BindB32(State->Config, StrLit("Dev/RenderFPSCounter"), &DEBUG_DebugSettings->RenderFPSCounter, 0); + Config_BindB32(State->Config, StrLit("Dev/ListHotAndActive"), &DEBUG_DebugSettings->ListHotAndActive, 0); + Config_BindB32(State->Config, StrLit("Dev/ShowWelcomeMessage"), &DEBUG_DebugSettings->ShowWelcomeMessage, 1); #endif - return; - } - + + Config_ReadFile(State->Config, StrLit("config.vn")); + } + + scene_view *SceneView = &State->SceneView; + SV_Init(SceneView, State->Arena); + + //- sixten: load startup scene + temp Scratch = GetScratch(); + string SceneInput = Platform_ReadEntireFile(Scratch.Arena, StrLit("data/scene.vns")); + compiled_scene Scene = S_ScriptFromText(Scratch.Arena, SceneInput); + SV_SetCurrentSource(&Scene); + ReleaseScratch(Scratch); + + SceneView->TestHappy = TextureFromAssetID(AssetID_ArthurHappy); + SceneView->TestNormal = TextureFromAssetID(AssetID_ArthurNormal); + SceneView->MonikaLeaning = TextureFromAssetID(AssetID_MonikaLeaning); + + SceneView->BackgroundTexture = State->BackgroundTexture; + + UI_Init(&State->UI); + AC_Init(&State->AnimationCurveState); + #if VN_INTERNAL - DEBUG_DebugSettings = &State->DebugSettings; + W_Init(&State->Workspace); #endif - - //- sixten: begin new frame - ArenaClear(State->FrameArena); - SetAssets(&State->Assets); - AC_NewFrame(&State->AnimationCurveState, Input->dtForFrame); - UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, Input->dtForFrame, State->GlyphAtlas); - SV_NewFrame(&State->SceneView, Input->EventList, Input->dtForFrame); - - //- sixten: check for toggle between modes - if(Platform_KeyPress(Input->EventList, Key_Return, PlatformModifier_Ctrl)) - { - State->EditorMode = !State->EditorMode; - } - - //- sixten: build the ui - UI_BeginBuild(RenderCommands->RenderDim); - { + return; + } + #if VN_INTERNAL - if(State->EditorMode) - { - W_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas); - } - else - { + DI_BeginFrame(State->DebugInfo); + DEBUG_DebugSettings = &State->DebugSettings; #endif - SV_BuildSceneView(Input); + + //- sixten: begin new frame + ArenaClear(State->FrameArena); + SetAssets(&State->Assets); + AC_NewFrame(&State->AnimationCurveState, Input->dtForFrame); + UI_NewFrame(&State->UI, Input->EventList, Input->MouseP, Input->dtForFrame, State->GlyphAtlas); + SV_NewFrame(&State->SceneView, Input->EventList, Input->dtForFrame); + + //- sixten: check for toggle between modes + if(Platform_KeyPress(Input->EventList, Key_Return, PlatformModifier_Ctrl)) + { + State->EditorMode = !State->EditorMode; + } + + //- sixten: update the scene + SV_Update(State->FrameArena); + + //- sixten: build the ui + UI_BeginBuild(RenderCommands->RenderDim); + { #if VN_INTERNAL - } + DI_BuildInfo(); + if(State->EditorMode) + { + W_Update(&State->Workspace, RenderCommands, Input, State->GlyphAtlas); + } + else + { #endif - } - UI_EndBuild(); - - - //- sixten: update the scene - SV_Update(State->FrameArena); - - //- sixten: consume all remaining evetns - for(platform_event *Event = Input->EventList->First; - Event != 0; - Event = Event->Next) - { - if(Event->Type == PlatformEvent_WindowClose) - { - Config_WriteFile(State->Config, StrLit("config.vn")); - Input->ExitRequested = true; - } - - Platform_ConsumeEvent(Input->EventList, Event); - } - - //- sixten: render the frame - { - render_group Group = BeginRenderGroup(RenderCommands); - PushClear(&Group, V3(0.1, 0.1, 0.1)); - - UI_RenderFrame(&Group, State->GlyphAtlas); - + SV_BuildSceneView(Input); +#if VN_INTERNAL + } +#endif + } + UI_EndBuild(); + + + //- sixten: consume all remaining evetns + for(platform_event *Event = Input->EventList->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_WindowClose) + { + Config_WriteFile(State->Config, StrLit("config.vn")); + Input->ExitRequested = true; + } + + Platform_ConsumeEvent(Input->EventList, Event); + } + + //- sixten: render the frame + { + render_group Group = BeginRenderGroup(RenderCommands); + PushClear(&Group, V3(0.1, 0.1, 0.1)); + + UI_RenderFrame(&Group, State->GlyphAtlas); + #if VN_INTERNAL - if(DEBUG_DebugSettings->ListHotAndActive) - { - PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey, - PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_BoxStringFromKey(UI_HotKey()), UI_HotKey())); - PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey, - PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_BoxStringFromKey(UI_ActiveKey()), UI_ActiveKey())); - } + if(DEBUG_DebugSettings->ListHotAndActive) + { + PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey, + PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_BoxStringFromKey(UI_HotKey()), UI_HotKey())); + PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 40), 15, Color_Grey, + PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_BoxStringFromKey(UI_ActiveKey()), UI_ActiveKey())); + } #endif - - } + +#if VN_INTERNAL + DI_EndFrame(); +#endif + + } } \ No newline at end of file diff --git a/code/vn_animation_curve.cpp b/code/vn_animation_curve.cpp index aa79ede..c58c98b 100644 --- a/code/vn_animation_curve.cpp +++ b/code/vn_animation_curve.cpp @@ -5,154 +5,154 @@ global animation_curve_state *Global_AnimationCurveState = 0; inline animation_curve_state *AC_GetState(void) { - return(Global_AnimationCurveState); + return(Global_AnimationCurveState); } inline void AC_SetState(animation_curve_state *State) { - Global_AnimationCurveState = State; + Global_AnimationCurveState = State; } static void AC_Init(animation_curve_state *State) { - State->Arena = ArenaAlloc(Kilobytes(32), true); + State->Arena = ArenaAlloc(Kilobytes(32), true); } inline animation_curve_key AC_GenerateKeyFromString(string String) { - animation_curve_key Key; - Key.Value = HashString(String); - - return(Key); + animation_curve_key Key; + Key.Value = HashString(String); + + return(Key); } static animation_curve_entry *AC_GetEntryByKey(animation_curve_key Key, r32 Initial) { - animation_curve_state *State = AC_GetState(); - - u64 Hash = Key.Value; - u64 Slot = Hash % ArrayCount(State->Buckets); - - animation_curve_bucket *Bucket = State->Buckets + Slot; - - animation_curve_entry *Result = 0; - for(animation_curve_entry *Entry = Bucket->First; - Entry != 0; - Entry = Entry->Next) - { - if(AreEqual(Entry->Key, Key)) - { - Result = Entry; - break; - } - } - - if(!Result) - { - if(DLLIsEmpty(State->FirstFreeEntry)) - { - Result = PushStruct(State->Arena, animation_curve_entry); - } - else - { - Result = State->FirstFreeEntry; - DLLRemove(State->FirstFreeEntry, State->LastFreeEntry, Result); - } - - DLLInsertLast(Bucket->First, Bucket->Last, Result); - - Result->Value = Initial; - } - - Result->Key = Key; - Result->LastFrameTouched = State->CurrentFrame; - - return(Result); + animation_curve_state *State = AC_GetState(); + + u64 Hash = Key.Value; + u64 Slot = Hash % ArrayCount(State->Buckets); + + animation_curve_bucket *Bucket = State->Buckets + Slot; + + animation_curve_entry *Result = 0; + for(animation_curve_entry *Entry = Bucket->First; + Entry != 0; + Entry = Entry->Next) + { + if(AreEqual(Entry->Key, Key)) + { + Result = Entry; + break; + } + } + + if(!Result) + { + if(DLLIsEmpty(State->FirstFreeEntry)) + { + Result = PushStruct(State->Arena, animation_curve_entry); + } + else + { + Result = State->FirstFreeEntry; + DLLRemove(State->FirstFreeEntry, State->LastFreeEntry, Result); + } + + DLLInsertLast(Bucket->First, Bucket->Last, Result); + + Result->Value = Initial; + } + + Result->Key = Key; + Result->LastFrameTouched = State->CurrentFrame; + + return(Result); } inline r32 AC_GetValue(string Name, r32 Initial) { - animation_curve_key Key = AC_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); - - r32 Result = Entry->Value; - return(Result); + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); + + r32 Result = Entry->Value; + return(Result); } inline void AC_SetValue(string Name, r32 Value) { - animation_curve_key Key = AC_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AC_GetEntryByKey(Key, Value); - - Entry->Value = Value; + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Value); + + Entry->Value = Value; } inline r32 AC_AnimateValueDirect(r32 Target, r32 Duration, r32 *Value) { - animation_curve_state *State = AC_GetState(); - - r32 Result = *Value; - - r32 Rate = 1.0 - Pow(2, -(10.0 / Duration * State->dtForFrame)); - *Value += (Target - *Value) * Rate; - - return(Result); + animation_curve_state *State = AC_GetState(); + + r32 Result = *Value; + + r32 Rate = 1.0 - Pow(2, -(10.0 / Duration * State->dtForFrame)); + *Value += (Target - *Value) * Rate; + + return(Result); } inline r32 AC_AnimateValue(r32 Target, r32 Initial, r32 Duration, string Name) { - animation_curve_key Key = AC_GenerateKeyFromString(Name); - animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); - - r32 Result = AC_AnimateValueDirect(Target, Duration, &Entry->Value); - return(Result); + animation_curve_key Key = AC_GenerateKeyFromString(Name); + animation_curve_entry *Entry = AC_GetEntryByKey(Key, Initial); + + r32 Result = AC_AnimateValueDirect(Target, Duration, &Entry->Value); + return(Result); } inline r32 AC_AnimateValueF(r32 Target, r32 Initial, r32 Duration, char *Format, ...) { - temporary_memory Scratch = GetScratch(0, 0); - - va_list Arguments; - va_start(Arguments, Format); - string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); - va_end(Arguments); - - r32 Result = AC_AnimateValue(Target, Initial, Duration, String); - - ReleaseScratch(Scratch); - - return(Result); + temp Scratch = GetScratch(0, 0); + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); + va_end(Arguments); + + r32 Result = AC_AnimateValue(Target, Initial, Duration, String); + + ReleaseScratch(Scratch); + + return(Result); } static void AC_NewFrame(animation_curve_state *State, r32 dtForFrame) { - AC_SetState(State); - State->dtForFrame = dtForFrame; - - // sixten: Prune untouched entries. - for(s32 BucketIndex = 0; - BucketIndex < ArrayCount(State->Buckets); - ++BucketIndex) - { - animation_curve_bucket *Bucket = State->Buckets + BucketIndex; - - animation_curve_entry *Entry = Bucket->First; - while(Entry != 0) - { - if(Entry->LastFrameTouched != State->CurrentFrame) - { - animation_curve_entry *ToRemove = Entry; - Entry = Entry->Next; - - DLLRemove(Bucket->First, Bucket->Last, ToRemove); - DLLInsertLast(State->FirstFreeEntry, State->LastFreeEntry, ToRemove); - } - else - { - Entry = Entry->Next; - } - } - } - - ++State->CurrentFrame; + AC_SetState(State); + State->dtForFrame = dtForFrame; + + // sixten: Prune untouched entries. + for(s32 BucketIndex = 0; + BucketIndex < ArrayCount(State->Buckets); + ++BucketIndex) + { + animation_curve_bucket *Bucket = State->Buckets + BucketIndex; + + animation_curve_entry *Entry = Bucket->First; + while(Entry != 0) + { + if(Entry->LastFrameTouched != State->CurrentFrame) + { + animation_curve_entry *ToRemove = Entry; + Entry = Entry->Next; + + DLLRemove(Bucket->First, Bucket->Last, ToRemove); + DLLInsertLast(State->FirstFreeEntry, State->LastFreeEntry, ToRemove); + } + else + { + Entry = Entry->Next; + } + } + } + + ++State->CurrentFrame; } \ No newline at end of file diff --git a/code/vn_asset.cpp b/code/vn_asset.cpp index ddebaab..05a489b 100644 --- a/code/vn_asset.cpp +++ b/code/vn_asset.cpp @@ -2,77 +2,77 @@ global assets *Global_Assets; static assets *GetAssets() { - return(Global_Assets); + return(Global_Assets); } static void SetAssets(assets *Assets) { - Global_Assets = Assets; + Global_Assets = Assets; } static void LoadAsset(asset_id ID) { - assets *Assets = GetAssets(); - Assert(ID >= AssetID_None && ID < AssetID_COUNT); - - asset *Asset = Assets->Assets + ID; - - if(!Asset->IsLoaded) - { - temporary_memory Scratch = GetScratch(); - - string Filepath = PushFormat(Scratch.Arena, "data/%s", AssetPathLUT[ID]); - platform_file_handle File = Platform.OpenFile(Filepath, PlatformAccess_Read); - if(File.IsValid) - { - u64 DataCount = Platform.GetFileSize(File); - u8 *Data = PushArray(Scratch.Arena, u8, DataCount); - Platform.ReadFile(File, Data, 0, DataCount); - - s32 Width, Height, BPP; - u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0); - - render_texture_format TextureFormat = Render_TextureFormat_Invalid; - switch(BPP) - { - InvalidDefaultCase; - case 1: { TextureFormat = Render_TextureFormat_R8; } break; - case 3: { TextureFormat = Render_TextureFormat_RGB8; } break; - case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break; - } - - Asset->Handle = Assets->AllocateTexture(V2S32(Width, Height), TextureFormat, true, TextureData); - - stbi_image_free(TextureData); - - Platform.CloseFile(File); - } - - ReleaseScratch(Scratch); - } + assets *Assets = GetAssets(); + Assert(ID >= AssetID_None && ID < AssetID_COUNT); + + asset *Asset = Assets->Assets + ID; + + if(!Asset->IsLoaded) + { + temp Scratch = GetScratch(); + + string Filepath = PushFormat(Scratch.Arena, "data/%s", AssetPathLUT[ID]); + platform_file_handle File = Platform.OpenFile(Filepath, PlatformAccess_Read); + if(File.IsValid) + { + u64 DataCount = Platform.GetFileSize(File); + u8 *Data = PushArray(Scratch.Arena, u8, DataCount); + Platform.ReadFile(File, Data, 0, DataCount); + + s32 Width, Height, BPP; + u8 *TextureData = stbi_load_from_memory(Data, DataCount, &Width, &Height, &BPP, 0); + + render_texture_format TextureFormat = Render_TextureFormat_Invalid; + switch(BPP) + { + InvalidDefaultCase; + case 1: { TextureFormat = Render_TextureFormat_R8; } break; + case 3: { TextureFormat = Render_TextureFormat_RGB8; } break; + case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break; + } + + Asset->Handle = Assets->AllocateTexture(V2S32(Width, Height), TextureFormat, true, TextureData); + + stbi_image_free(TextureData); + + Platform.CloseFile(File); + } + + ReleaseScratch(Scratch); + } } static void LoadPermanentAssets(assets *Assets) { - SetAssets(Assets); - - //- sixten: assign ID to all assets & load all permanent ones - for(int Index = 0; Index < AssetID_COUNT; ++Index) - { - asset_id ID = (asset_id)Index; - asset *Asset = Assets->Assets + Index; - Asset->ID = ID; - - if(ID != AssetID_None) - { - LoadAsset(ID); - } - } + SetAssets(Assets); + + //- sixten: assign ID to all assets & load all permanent ones + for(int Index = 0; Index < AssetID_COUNT; ++Index) + { + asset_id ID = (asset_id)Index; + asset *Asset = Assets->Assets + Index; + Asset->ID = ID; + + if(ID != AssetID_None) + { + LoadAsset(ID); + } + } } static render_handle TextureFromAssetID(asset_id ID) { - Assert(ID >= AssetID_None && ID < AssetID_COUNT); - - asset *Asset = GetAssets()->Assets + ID; - return(Asset->Handle); + Assert(ID >= AssetID_None && ID < AssetID_COUNT); + + asset *Asset = GetAssets()->Assets + ID; + return(Asset->Handle); } \ No newline at end of file diff --git a/code/vn_assets.md b/code/vn_assets.md index 5ac4a1e..60f7fc0 100644 --- a/code/vn_assets.md +++ b/code/vn_assets.md @@ -1,44 +1,50 @@ @table(Name, Path, IsPermanent) assets_desc: { - { None, "", false } - - //////////////////////////////// - //~ sixten: backgrounds - { DemoBackground, "backgrounds/test.jpg", false } - - //////////////////////////////// - //~ sixten: characters - - //- sixten: arthur - { ArthurNormal, "characters/test_normal.png", false } - { ArthurHappy, "characters/test_happy.png", false } - - //- sixten: monika - { MonikaLeaning, "characters/monika_leaning.png", false } - + { None, "", true } + { Error, "backgrounds/unknown.png", true } + + //////////////////////////////// + //~ sixten: backgrounds + { DemoBackground, "backgrounds/test.jpg", false } + { DDLCBackground, "backgrounds/ddlc.png", false } + + //////////////////////////////// + //~ sixten: characters + + //- sixten: arthur + { ArthurNormal, "characters/test_normal.png", false } + { ArthurHappy, "characters/test_happy.png", false } + + //- sixten: monika + { MonikaLeaning, "characters/monika_leaning.png", false } + } -@table_gen_enum asset_id: +@table_gen { - @expand(assets_desc s) `AssetID_$(s.Name),`; - `AssetID_COUNT,`; + `typedef s32 asset_id;` + `enum` + `{` + @expand(assets_desc s) `AssetID_$(s.Name),`; + `AssetID_COUNT,`; + `};` } @table_gen_data(`char *`) AssetPathLUT: { - @expand(assets_desc s) - `"$(s.Path)",`; + @expand(assets_desc s) + `"$(s.Path)",`; } @table_gen_data(`bool`) AssetIsPermanentLUT: { - @expand(assets_desc s) - `$(s.IsPermanent),`; + @expand(assets_desc s) + `$(s.IsPermanent),`; } @table_gen_data(`char *`) AssetNameLUT: { - @expand(assets_desc s) - `"$(s.Name)",`; + @expand(assets_desc s) + `"$(s.Name)",`; } \ No newline at end of file diff --git a/code/vn_config.cpp b/code/vn_config.cpp index 2fe553e..051b5c1 100644 --- a/code/vn_config.cpp +++ b/code/vn_config.cpp @@ -1,327 +1,327 @@ static config *CreateConfig(void) { - arena *Arena = ArenaAlloc(Kilobytes(4), true); - config *Config = PushStruct(Arena, config); - Config->Arena = Arena; - return(Config); + arena *Arena = ArenaAlloc(Kilobytes(4), true); + config *Config = PushStruct(Arena, config); + Config->Arena = Arena; + return(Config); } static config_entry *Config_FindEntryByName(config *Config, string Name) { - config_entry *Result = 0; - - u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets); - config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot; - - for(config_entry *Entry = Bucket->First; - Entry != 0; - Entry = Entry->Next) - { - if(AreEqual(Entry->Name, Name)) - { - Result = Entry; - break; - } - } - - return(Result); + config_entry *Result = 0; + + u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets); + config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot; + + for(config_entry *Entry = Bucket->First; + Entry != 0; + Entry = Entry->Next) + { + if(AreEqual(Entry->Name, Name)) + { + Result = Entry; + break; + } + } + + return(Result); } static void Config_BindEntry(config *Config, string Name, config_entry_type Type, void *Target) { - config_entry *Entry = Config_FindEntryByName(Config, Name); - if(!Entry) - { - Entry = PushStruct(Config->Arena, config_entry); - Entry->Name = PushString(Config->Arena, Name); - Entry->Type = Type; - - u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets); - config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot; - - DLLInsertLast(Bucket->First, Bucket->Last, Entry); - - if(Config->LastInternal) - { - Config->LastInternal->NextInternal = Entry; - Config->LastInternal = Entry; - } - else - { - Config->FirstInternal = Config->LastInternal = Entry; - } - } - - Assert(Entry->Type == Type); - Entry->Target = Target; + config_entry *Entry = Config_FindEntryByName(Config, Name); + if(!Entry) + { + Entry = PushStruct(Config->Arena, config_entry); + Entry->Name = PushString(Config->Arena, Name); + Entry->Type = Type; + + u32 BucketSlot = HashString(Name) % ArrayCount(Config->EntryBuckets); + config_entry_bucket *Bucket = Config->EntryBuckets + BucketSlot; + + DLLInsertLast(Bucket->First, Bucket->Last, Entry); + + if(Config->LastInternal) + { + Config->LastInternal->NextInternal = Entry; + Config->LastInternal = Entry; + } + else + { + Config->FirstInternal = Config->LastInternal = Entry; + } + } + + Assert(Entry->Type == Type); + Entry->Target = Target; } inline void Config_BindS32(config *Config, string Name, s32 *Target, s32 Default) { - *Target = Default; - Config_BindEntry(Config, Name, Config_Entry_S32, Target); + *Target = Default; + Config_BindEntry(Config, Name, Config_Entry_S32, Target); } inline void Config_BindS64(config *Config, string Name, s64 *Target, s64 Default) { - *Target = Default; - Config_BindEntry(Config, Name, Config_Entry_S64, Target); + *Target = Default; + Config_BindEntry(Config, Name, Config_Entry_S64, Target); } inline void Config_BindB32(config *Config, string Name, b32 *Target, b32 Default) { - *Target = Default; - Config_BindEntry(Config, Name, Config_Entry_B32, Target); + *Target = Default; + Config_BindEntry(Config, Name, Config_Entry_B32, Target); } static void Config_ParseError(string Message, string FileText, s64 Offset, arena *Arena) { - text_point Point = TextPointFromOffset(FileText, Offset); - string String = PushFormat(Arena, "Config: At %i:%i - %S", Point.Line, Point.Column, Message); - Platform.ShowMessage(String, Platform_Message_Warning); + text_point Point = TextPointFromOffset(FileText, Offset); + string String = PushFormat(Arena, "Config: At %i:%i - %S", Point.Line, Point.Column, Message); + Platform.ShowMessage(String, Platform_Message_Warning); } static void Config_ReadFile(config *Config, string Path) { - temporary_memory Scratch = GetScratch(); - - //- sixten: read & tokenize input file - string Text = Platform_ReadEntireFile(Scratch.Arena, Path); - tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Text, T_IsIrregular); - token_array Tokens = TokenizeResult.Tokens; - - // sixten: parse context - config_parse_list FullPath = {}; - config_parse_mode ParseMode = ConfigParseMode_Main; - - //- sixten: parse tokens - token *TokensStart = Tokens.Tokens; - token *TokensEnd = Tokens.Tokens + Tokens.Count; - token *Token = TokensStart; - for(;Token < TokensEnd;) - { - string TokenString = Substring(Text, Token->Range); - - //- sixten: get next name - if(ParseMode == ConfigParseMode_Main && Token->Kind & TokenKind_Identifier) - { - Config_ParseListPush(Scratch.Arena, &FullPath, TokenString); - ParseMode = ConfigParseMode_ScanForCurlyOpenOrEquals; - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for curly close - if(ParseMode == ConfigParseMode_Main && Token->Kind == TokenKind_CurlyClose) - { - Config_ParseListPop(&FullPath); - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for curly open - if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind == TokenKind_CurlyOpen) - { - ParseMode = ConfigParseMode_Main; - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for equals - if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind & TokenKind_Equal) - { - ParseMode = ConfigParseMode_ScanForValue; - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for semicolon - if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Kind == TokenKind_Semicolon) - { - ParseMode = ConfigParseMode_Main; - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for boolean value - if(ParseMode == ConfigParseMode_ScanForValue && (Token->Kind == TokenKind_True || Token->Kind == TokenKind_False)) - { - string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); - config_entry *Entry = Config_FindEntryByName(Config, FullName); - if(Entry) - { - b32 Value = AreEqual(TokenString, StrLit("true")); - Assert(Entry->Type == Config_Entry_B32); - *(b32 *)Entry->Target = Value; - } - Config_ParseListPop(&FullPath); - ParseMode = ConfigParseMode_ScanForSemicolon; - Token += 1; - goto TokenConsumed; - } - - //- sixten: scan for integer value - if(ParseMode == ConfigParseMode_ScanForValue && Token->Kind & TokenKind_Numeric) - { - string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); - config_entry *Entry = Config_FindEntryByName(Config, FullName); - if(Entry) - { - s64 Value = ConvertStringToS64(TokenString); - if(Entry->Type == Config_Entry_S32) - { - *(s32 *)Entry->Target = Value; - } - else if(Entry->Type == Config_Entry_S64) - { - *(s64 *)Entry->Target = Value; - } - else - { - InvalidCodepath; - } - } - Config_ParseListPop(&FullPath); - ParseMode = ConfigParseMode_ScanForSemicolon; - Token += 1; - goto TokenConsumed; - } - - //- sixten: if the token has not been consumed, something's gone wrong - { - string ErrorMessage = StrLit("Unknown parse error"); - //- sixten: determine error message - switch(ParseMode) - { - case ConfigParseMode_Main: { ErrorMessage = StrLit("Expected identifier or '}'"); } break; - case ConfigParseMode_ScanForCurlyOpenOrEquals: { ErrorMessage = StrLit("Expected '{' or '='") ; } break; - case ConfigParseMode_ScanForValue: { ErrorMessage = StrLit("Expected value"); } break; - case ConfigParseMode_ScanForSemicolon: { ErrorMessage = StrLit("Expected ';'"); } break; - } - - Config_ParseError(ErrorMessage, Text, Token->Range.Min, Scratch.Arena); - Token += 1; - } - - TokenConsumed:; - } - - ReleaseScratch(Scratch); + temp Scratch = GetScratch(); + + //- sixten: read & tokenize input file + string Text = Platform_ReadEntireFile(Scratch.Arena, Path); + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Text, T_IsIrregular); + token_array Tokens = TokenizeResult.Tokens; + + // sixten: parse context + config_parse_list FullPath = {}; + config_parse_mode ParseMode = ConfigParseMode_Main; + + //- sixten: parse tokens + token *TokensStart = Tokens.Tokens; + token *TokensEnd = Tokens.Tokens + Tokens.Count; + token *Token = TokensStart; + for(;Token < TokensEnd;) + { + string TokenString = Substring(Text, Token->Range); + + //- sixten: get next name + if(ParseMode == ConfigParseMode_Main && Token->Kind & TokenKind_Identifier) + { + Config_ParseListPush(Scratch.Arena, &FullPath, TokenString); + ParseMode = ConfigParseMode_ScanForCurlyOpenOrEquals; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for curly close + if(ParseMode == ConfigParseMode_Main && Token->Kind == TokenKind_CurlyClose) + { + Config_ParseListPop(&FullPath); + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for curly open + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind == TokenKind_CurlyOpen) + { + ParseMode = ConfigParseMode_Main; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for equals + if(ParseMode == ConfigParseMode_ScanForCurlyOpenOrEquals && Token->Kind & TokenKind_Equal) + { + ParseMode = ConfigParseMode_ScanForValue; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for semicolon + if(ParseMode == ConfigParseMode_ScanForSemicolon && Token->Kind == TokenKind_Semicolon) + { + ParseMode = ConfigParseMode_Main; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for boolean value + if(ParseMode == ConfigParseMode_ScanForValue && (Token->Kind == TokenKind_True || Token->Kind == TokenKind_False)) + { + string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); + config_entry *Entry = Config_FindEntryByName(Config, FullName); + if(Entry) + { + b32 Value = AreEqual(TokenString, StrLit("true")); + Assert(Entry->Type == Config_Entry_B32); + *(b32 *)Entry->Target = Value; + } + Config_ParseListPop(&FullPath); + ParseMode = ConfigParseMode_ScanForSemicolon; + Token += 1; + goto TokenConsumed; + } + + //- sixten: scan for integer value + if(ParseMode == ConfigParseMode_ScanForValue && Token->Kind & TokenKind_Numeric) + { + string FullName = Config_ParseListJoin(Scratch.Arena, &FullPath); + config_entry *Entry = Config_FindEntryByName(Config, FullName); + if(Entry) + { + s64 Value = ConvertStringToS64(TokenString); + if(Entry->Type == Config_Entry_S32) + { + *(s32 *)Entry->Target = Value; + } + else if(Entry->Type == Config_Entry_S64) + { + *(s64 *)Entry->Target = Value; + } + else + { + InvalidCodepath; + } + } + Config_ParseListPop(&FullPath); + ParseMode = ConfigParseMode_ScanForSemicolon; + Token += 1; + goto TokenConsumed; + } + + //- sixten: if the token has not been consumed, something's gone wrong + { + string ErrorMessage = StrLit("Unknown parse error"); + //- sixten: determine error message + switch(ParseMode) + { + case ConfigParseMode_Main: { ErrorMessage = StrLit("Expected identifier or '}'"); } break; + case ConfigParseMode_ScanForCurlyOpenOrEquals: { ErrorMessage = StrLit("Expected '{' or '='") ; } break; + case ConfigParseMode_ScanForValue: { ErrorMessage = StrLit("Expected value"); } break; + case ConfigParseMode_ScanForSemicolon: { ErrorMessage = StrLit("Expected ';'"); } break; + } + + Config_ParseError(ErrorMessage, Text, Token->Range.Min, Scratch.Arena); + Token += 1; + } + + TokenConsumed:; + } + + ReleaseScratch(Scratch); } //////////////////////////////// //~ sixten: Config Parse Type Functions static void Config_ParseListPush(arena *Arena, config_parse_list *List, string Name) { - config_parse_node *Node = PushStruct(Arena, config_parse_node); - Node->Name = Name; - List->TotalCountPlusOne += Name.Count + 1; - DLLInsertLast(List->First, List->Last, Node); + config_parse_node *Node = PushStruct(Arena, config_parse_node); + Node->Name = Name; + List->TotalCountPlusOne += Name.Count + 1; + DLLInsertLast(List->First, List->Last, Node); } static void Config_ParseListPop(config_parse_list *List) { - config_parse_node *Node = List->Last; - if(Node) - { - List->TotalCountPlusOne -= Node->Name.Count + 1; - DLLRemove(List->First, List->Last, Node); - } + config_parse_node *Node = List->Last; + if(Node) + { + List->TotalCountPlusOne -= Node->Name.Count + 1; + DLLRemove(List->First, List->Last, Node); + } } static string Config_ParseListJoin(arena *Arena, config_parse_list *List) { - s64 TotalCount = List->TotalCountPlusOne - 1; - string Result = MakeString(PushArray(Arena, u8, List->TotalCountPlusOne), TotalCount); - s64 Index = 0; - for(config_parse_node *Node = List->First; Node != 0; Node = Node->Next) - { - Copy(Result.Data + Index, Node->Name.Data, Node->Name.Count); - Index += Node->Name.Count; - if(Node->Next) - { - Result.Data[Index] = '/'; - Index += 1; - } - } - return(Result); + s64 TotalCount = List->TotalCountPlusOne - 1; + string Result = MakeString(PushArray(Arena, u8, List->TotalCountPlusOne), TotalCount); + s64 Index = 0; + for(config_parse_node *Node = List->First; Node != 0; Node = Node->Next) + { + Copy(Result.Data + Index, Node->Name.Data, Node->Name.Count); + Index += Node->Name.Count; + if(Node->Next) + { + Result.Data[Index] = '/'; + Index += 1; + } + } + return(Result); } static void Config_WriteFile(config *Config, string Path) { - string_list Out = {}; - temporary_memory Scratch = GetScratch(); - - string LastDir = MakeString(0, 0LL); - for(config_entry *Entry = Config->FirstInternal; - Entry != 0; - Entry = Entry->NextInternal) - { - s64 LastSlash = LastIndexOf(Entry->Name, '/'); - Assert(LastSlash != -1); - - string Dir = Prefix(Entry->Name, LastSlash); - string Name = Suffix(Entry->Name, Entry->Name.Count - LastSlash - 1); - - if(!AreEqual(Dir, LastDir)) - { - if(!AreEqual(LastDir, MakeString(0, 0LL))) - { - AppendString(&Out, StrLit("}\n\n"), Scratch.Arena); - } - - AppendString(&Out, Dir, Scratch.Arena); - AppendString(&Out, StrLit("\n{\n"), Scratch.Arena); - - LastDir = Dir; - } - - AppendString(&Out, StrLit("\t"), Scratch.Arena); - AppendString(&Out, Name, Scratch.Arena); - AppendString(&Out, StrLit(" = "), Scratch.Arena); - - // sixten: Output the value of the entry - if(Entry->Type == Config_Entry_S32 || Entry->Type == Config_Entry_S64) - { - s64 IntegerValue; - if(Entry->Type == Config_Entry_S32) - { - IntegerValue = *(s32 *)Entry->Target; - } - else - { - IntegerValue = *(s64 *)Entry->Target; - } - - string Value = ConvertS64ToString(Scratch.Arena, IntegerValue); - AppendString(&Out, Value, Scratch.Arena); - } - else if(Entry->Type == Config_Entry_B32) - { - string Value = (*(b32 *)Entry->Target)?StrLit("true"):StrLit("false"); - AppendString(&Out, Value, Scratch.Arena); - } - else - { - UnimplementedCodepath; - } - - AppendString(&Out, StrLit(";\n"), Scratch.Arena); - } - - if(!AreEqual(LastDir, MakeString(0, 0LL))) - { - AppendString(&Out, StrLit("}"), Scratch.Arena); - } - - string FinalOut = JoinStringList(&Out, Scratch.Arena); - - platform_file_handle Handle = Platform.OpenFile(Path, PlatformAccess_Write); - if(Handle.IsValid) - { - Platform.WriteFile(Handle, FinalOut.Data, 0, FinalOut.Count); - Platform.CloseFile(Handle); - } - - ReleaseScratch(Scratch); + string_list Out = {}; + temp Scratch = GetScratch(); + + string LastDir = MakeString(0, 0LL); + for(config_entry *Entry = Config->FirstInternal; + Entry != 0; + Entry = Entry->NextInternal) + { + s64 LastSlash = LastIndexOf(Entry->Name, '/'); + Assert(LastSlash != -1); + + string Dir = Prefix(Entry->Name, LastSlash); + string Name = Suffix(Entry->Name, Entry->Name.Count - LastSlash - 1); + + if(!AreEqual(Dir, LastDir)) + { + if(!AreEqual(LastDir, MakeString(0, 0LL))) + { + AppendString(&Out, StrLit("}\n\n"), Scratch.Arena); + } + + AppendString(&Out, Dir, Scratch.Arena); + AppendString(&Out, StrLit("\n{\n"), Scratch.Arena); + + LastDir = Dir; + } + + AppendString(&Out, StrLit("\t"), Scratch.Arena); + AppendString(&Out, Name, Scratch.Arena); + AppendString(&Out, StrLit(" = "), Scratch.Arena); + + // sixten: Output the value of the entry + if(Entry->Type == Config_Entry_S32 || Entry->Type == Config_Entry_S64) + { + s64 IntegerValue; + if(Entry->Type == Config_Entry_S32) + { + IntegerValue = *(s32 *)Entry->Target; + } + else + { + IntegerValue = *(s64 *)Entry->Target; + } + + string Value = ConvertS64ToString(Scratch.Arena, IntegerValue); + AppendString(&Out, Value, Scratch.Arena); + } + else if(Entry->Type == Config_Entry_B32) + { + string Value = (*(b32 *)Entry->Target)?StrLit("true"):StrLit("false"); + AppendString(&Out, Value, Scratch.Arena); + } + else + { + UnimplementedCodepath; + } + + AppendString(&Out, StrLit(";\n"), Scratch.Arena); + } + + if(!AreEqual(LastDir, MakeString(0, 0LL))) + { + AppendString(&Out, StrLit("}"), Scratch.Arena); + } + + string FinalOut = JoinStringList(&Out, Scratch.Arena); + + platform_file_handle Handle = Platform.OpenFile(Path, PlatformAccess_Write); + if(Handle.IsValid) + { + Platform.WriteFile(Handle, FinalOut.Data, 0, FinalOut.Count); + Platform.CloseFile(Handle); + } + + ReleaseScratch(Scratch); } \ No newline at end of file diff --git a/code/vn_debug_info.cpp b/code/vn_debug_info.cpp new file mode 100644 index 0000000..ca0973e --- /dev/null +++ b/code/vn_debug_info.cpp @@ -0,0 +1,108 @@ +//////////////////////////////// +//~ sixten: Debug Info Functions + +per_thread debug_info *ThreadLocal_DebugInfo = 0; + +//- sixten: manage state +static debug_info *DI_DebugInfoAlloc(void) +{ + arena *Arena = ArenaAlloc(Kilobytes(1), true); + debug_info *Result = PushStruct(Arena, debug_info); + Result->Arena = Arena; + return(Result); +} + +static void DI_BeginFrame(debug_info *DebugInfo) +{ + ThreadLocal_DebugInfo = DebugInfo; + DebugInfo->FrameTemp = BeginTemp(DebugInfo->Arena); +} + +static void DI_EndFrame() +{ + +} + +//- sixten: user interface +static void DI_Info(string Message) +{ + debug_info *DebugInfo = ThreadLocal_DebugInfo; + debug_info_node *Node = PushStruct(DebugInfo->Arena, debug_info_node); + Node->Message = PushString(DebugInfo->Arena, Message); + DLLInsertLast(DebugInfo->First, DebugInfo->Last, Node); +} + +static void DI_InfoF(char *Format, ...) +{ + debug_info *DebugInfo = ThreadLocal_DebugInfo; + va_list Arguments; + va_start(Arguments, Format); + temp Scratch = GetScratch(); + string Message = PushFormatVariadic(DebugInfo->Arena, Format, Arguments); + DI_Info(Message); + ReleaseScratch(Scratch); + va_end(Arguments); +} + +static void DI_BuildInfo(void) +{ + debug_info *DebugInfo = ThreadLocal_DebugInfo; + AC_AnimateValueDirect(DebugInfo->Open, 0.3f, &DebugInfo->OpenT); + if(!DLLIsEmpty(DebugInfo->First)) + { + UI_Tooltip + { + UI_SetNextBackgroundColor(SetAlpha(Theme_BackgroundColor, 0.5f)); + UI_SetNextSize(UI_Em(15, 1), UI_ChildrenSum(1, 1)); + UI_SetNextCornerRadius(4.0f); + UI_SetNextHoverCursor(PlatformCursor_ArrowAll); + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder|UI_BoxFlag_Clickable| + UI_BoxFlag_HotAnimation|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_Clip, + StrLit("DI Container")); + UI_Parent(Box) + { + UI_Size(UI_Percent(1, 1), UI_ChildrenSum(1, 1)) UI_Row() UI_Width(UI_TextContent(15, 1)) UI_Height(UI_TextContent(15, 1)) + { + UI_Font(Font_Bold) UI_LabelF("Debug Info"); + UI_Spacer(UI_Percent(1, 0)); + UI_Font(Font_Icons) + { + ui_box *ExpandBox = UI_MakeBoxF(UI_BoxFlag_DrawText|UI_BoxFlag_Clickable|UI_BoxFlag_HotAnimation, "%U", DebugInfo->Open?FontIcon_DownDir:FontIcon_RightDir); + ui_signal ExpandBoxSignal = UI_SignalFromBox(ExpandBox); + if(ExpandBoxSignal.Pressed) + { + DebugInfo->Open = !DebugInfo->Open; + } + } + } + + UI_Height(UI_ChildrenSum(DebugInfo->OpenT, 1)) UI_Column() + { + UI_Width(UI_TextContent(15, 1)) UI_Height(UI_TextContent(15, 1)) + for(debug_info_node *Node = DebugInfo->First; Node != 0; Node = Node->Next) + { + ui_box *MessageBox = UI_MakeBoxF(UI_BoxFlag_DrawText, "%p", Node); + UI_EquipBoxText(MessageBox, Node->Message); + } + } + } + + ui_signal Signal = UI_SignalFromBox(Box); + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragV2(DebugInfo->RelativeP); + } + + v2 StartP = UI_GetDragV2(); + v2 EndP = StartP + Signal.DragDelta; + DebugInfo->RelativeP = EndP; + } + Box->FixedP = DebugInfo->RelativeP; + } + } + // sixten: after building we assume that the data should be released + EndTemp(DebugInfo->FrameTemp); + DebugInfo->First = DebugInfo->Last = 0; +} \ No newline at end of file diff --git a/code/vn_debug_info.h b/code/vn_debug_info.h new file mode 100644 index 0000000..8a48964 --- /dev/null +++ b/code/vn_debug_info.h @@ -0,0 +1,41 @@ +/* date = December 9th 2023 0:37 pm */ + +#ifndef VN_DEBUG_INFO_H +#define VN_DEBUG_INFO_H + +//////////////////////////////// +//~ sixten: Debug Info Types + +struct debug_info_node +{ + debug_info_node *Next; + debug_info_node *Prev; + string Message; +}; + +struct debug_info +{ + arena *Arena; + temp FrameTemp; + debug_info_node *First; + debug_info_node *Last; + v2_r32 RelativeP; + b32 Open; + r32 OpenT; +}; + + +//////////////////////////////// +//~ sixten: Debug Info Functions + +//- sixten: manage state +static debug_info *DI_DebugInfoAlloc(void); +static void DI_BeginFrame(debug_info *DebugInfo); +static void DI_EndFrame(void); + +//- sixten: user interface +static void DI_Info(string Message); +static void DI_InfoF(char *Format, ...); +static void DI_BuildInfo(void); + +#endif //VN_DEBUG_INFO_H diff --git a/code/vn_font.cpp b/code/vn_font.cpp index b73ffab..7e95406 100644 --- a/code/vn_font.cpp +++ b/code/vn_font.cpp @@ -4,230 +4,230 @@ global read_only s32 Font_Oversample = 2; inline s32 GetSubpixelSegmentAtP(r32 Value) { - s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS; - return(Result); + s32 Result = (s32)(Value - Floor(Value))*GLYPH_SUBPIXEL_SEGMENTS; + return(Result); } static void RasterizeGlyph(glyph_atlas *Atlas, font_id Font, glyph *Glyph, u32 Codepoint, r32 Size, s32 Subpixel) { - Glyph->Font = Font; - Glyph->Codepoint = Codepoint; - Glyph->Size = Size; - Glyph->Subpixel = Subpixel; - - Assert(Subpixel < GLYPH_SUBPIXEL_SEGMENTS); - - loaded_font *LoadedFont = Atlas->Fonts + Font; - stbtt_fontinfo *Info = &LoadedFont->Info; - - r32 Scale = stbtt_ScaleForMappingEmToPixels(Info, Size); - - s32 InternalIndex = (s32)(Glyph - Atlas->Glyphs); - s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize; - - v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize, - (InternalIndex / GlyphsPerRow)*Atlas->GlyphSize); - - int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint); - - stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale, - (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0, - &Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y); - - Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize); - stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer, - Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize, - Scale, Scale, - (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0, - GlyphIndex); - - s32 Advance, LeftSideBearing; - stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing); - Glyph->Advance = Advance*Scale; - Glyph->Offset.x = LeftSideBearing*Scale; - - Glyph->Offset.y = Glyph->P0.y + (LoadedFont->Ascent + LoadedFont->LineGap)*Scale; - - v2_s32 Dim = Glyph->P1 - Glyph->P0; - - Glyph->P0 = BaseTextureOffset; - Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2); - - Atlas->RenderCommands->FillRegion(Atlas->Texture, - BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize), - Atlas->BitmapBuffer); + Glyph->Font = Font; + Glyph->Codepoint = Codepoint; + Glyph->Size = Size; + Glyph->Subpixel = Subpixel; + + Assert(Subpixel < GLYPH_SUBPIXEL_SEGMENTS); + + loaded_font *LoadedFont = Atlas->Fonts + Font; + stbtt_fontinfo *Info = &LoadedFont->Info; + + r32 Scale = stbtt_ScaleForMappingEmToPixels(Info, Size); + + s32 InternalIndex = (s32)(Glyph - Atlas->Glyphs); + s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize; + + v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize, + (InternalIndex / GlyphsPerRow)*Atlas->GlyphSize); + + int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint); + + stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale, + (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0, + &Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y); + + Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize); + stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer, + Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize, + Scale, Scale, + (r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0, + GlyphIndex); + + s32 Advance, LeftSideBearing; + stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing); + Glyph->Advance = Advance*Scale; + Glyph->Offset.x = LeftSideBearing*Scale; + + Glyph->Offset.y = Glyph->P0.y + (LoadedFont->Ascent + LoadedFont->LineGap)*Scale; + + v2_s32 Dim = Glyph->P1 - Glyph->P0; + + Glyph->P0 = BaseTextureOffset; + Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2); + + Atlas->RenderCommands->FillRegion(Atlas->Texture, + BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize), + Atlas->BitmapBuffer); } static glyph *GetGlyph(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size, s32 Subpixel) { - glyph *Glyph = 0; - - for(s32 GlyphIndex = 0; - GlyphIndex < Atlas->GlyphsUsed; - ++GlyphIndex) - { - glyph *At = Atlas->Glyphs + GlyphIndex; - if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel)) - { - Glyph = At; - break; - } - } - - if(Glyph) - { - DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); - } - else - { - if(Atlas->GlyphsUsed < Atlas->MaxGlyphCount) - { - Glyph = Atlas->Glyphs + Atlas->GlyphsUsed++; - } - else - { - Glyph = Atlas->LRUFirst; - Assert(Glyph); - - DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); - } - - RasterizeGlyph(Atlas, Font, Glyph, Codepoint, Size, Subpixel); - } - - DLLInsertLast_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); - - return(Glyph); + glyph *Glyph = 0; + + for(s32 GlyphIndex = 0; + GlyphIndex < Atlas->GlyphsUsed; + ++GlyphIndex) + { + glyph *At = Atlas->Glyphs + GlyphIndex; + if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel)) + { + Glyph = At; + break; + } + } + + if(Glyph) + { + DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); + } + else + { + if(Atlas->GlyphsUsed < Atlas->MaxGlyphCount) + { + Glyph = Atlas->Glyphs + Atlas->GlyphsUsed++; + } + else + { + Glyph = Atlas->LRUFirst; + Assert(Glyph); + + DLLRemove_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); + } + + RasterizeGlyph(Atlas, Font, Glyph, Codepoint, Size, Subpixel); + } + + DLLInsertLast_NP(Atlas->LRUFirst, Atlas->LRULast, Glyph, LRUNext, LRUPrev); + + return(Glyph); } static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands, s32 BitmapSize = DEFAULT_GLYPH_ATLAS_DIM, s32 GlyphSize = MAX_GLYPH_SIZE) { - arena *Arena = ArenaAlloc(Megabytes(1), true); - glyph_atlas *Atlas = PushStruct(Arena, glyph_atlas); - Atlas->Arena = Arena; - - Atlas->BitmapSize = BitmapSize; - Atlas->GlyphSize = GlyphSize; - - Atlas->MaxGlyphCount = (DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE)*(DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE); - Atlas->Glyphs = PushArray(Atlas->Arena, glyph, Atlas->MaxGlyphCount); - - Atlas->RenderCommands = RenderCommands; - Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, false, 0); - - Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Roboto-Regular.ttf")); - Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Roboto-Bold.ttf")); - Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono.ttf")); - Atlas->Fonts[Font_MonospaceOblique].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono-Oblique.ttf")); - Atlas->Fonts[Font_Fancy].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Merriweather-Regular.ttf")); - Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/icons.ttf")); - - for(s32 FontIndex = 0; - FontIndex < Font_Count; - ++FontIndex) - { - loaded_font *Font = Atlas->Fonts + FontIndex; - stbtt_InitFont(&Font->Info, - Font->Data.Data, - stbtt_GetFontOffsetForIndex(Font->Data.Data, 0)); - - stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap); - } - - Atlas->BitmapBuffer = PushArray(Atlas->Arena, u8, GlyphSize*GlyphSize); - - return(Atlas); + arena *Arena = ArenaAlloc(Megabytes(1), true); + glyph_atlas *Atlas = PushStruct(Arena, glyph_atlas); + Atlas->Arena = Arena; + + Atlas->BitmapSize = BitmapSize; + Atlas->GlyphSize = GlyphSize; + + Atlas->MaxGlyphCount = (DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE)*(DEFAULT_GLYPH_ATLAS_DIM / MAX_GLYPH_SIZE); + Atlas->Glyphs = PushArray(Atlas->Arena, glyph, Atlas->MaxGlyphCount); + + Atlas->RenderCommands = RenderCommands; + Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, false, 0); + + Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Roboto-Regular.ttf")); + Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Roboto-Bold.ttf")); + Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono.ttf")); + Atlas->Fonts[Font_MonospaceOblique].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/DejaVuSansMono-Oblique.ttf")); + Atlas->Fonts[Font_Fancy].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/Merriweather-Regular.ttf")); + Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/icons.ttf")); + + for(s32 FontIndex = 0; + FontIndex < Font_Count; + ++FontIndex) + { + loaded_font *Font = Atlas->Fonts + FontIndex; + stbtt_InitFont(&Font->Info, + Font->Data.Data, + stbtt_GetFontOffsetForIndex(Font->Data.Data, 0)); + + stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap); + } + + Atlas->BitmapBuffer = PushArray(Atlas->Arena, u8, GlyphSize*GlyphSize); + + return(Atlas); } static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font, v2 P, r32 Size, v4 Color, string Text) { - r32 OffsetX = 0; - u8 *TextBegin = Text.Data; - u8 *TextEnd = TextBegin+Text.Count; - for(u8 *Byte = TextBegin; Byte < TextEnd;) - { - string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); - Byte += Decode.Size; - u32 Codepoint = Decode.Codepoint; - - glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample)); - Assert(Glyph); - - v2 GlyphP = P + Glyph->Offset*(1.0 / Font_Oversample) + V2(OffsetX, 1); - - v2 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0); - v2 Dim = RenderDim*(1.0 / Font_Oversample); - - PushTexturedQuad(Group, - Range2R32(GlyphP, GlyphP+Dim), - Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)), - Color, Color, Color, Color, 0, 0, 0, Atlas->Texture); - - OffsetX += Glyph->Advance/Font_Oversample; - } - return(OffsetX); + r32 OffsetX = 0; + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + for(u8 *Byte = TextBegin; Byte < TextEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); + Byte += Decode.Size; + u32 Codepoint = Decode.Codepoint; + + glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample)); + Assert(Glyph); + + v2 GlyphP = P + Glyph->Offset*(1.0 / Font_Oversample) + V2(OffsetX, 1); + + v2 RenderDim = ConvertV2ToR32(Glyph->P1 - Glyph->P0); + v2 Dim = RenderDim*(1.0 / Font_Oversample); + + PushTexturedQuad(Group, + Range2R32(GlyphP, GlyphP+Dim), + Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)), + Color, Color, Color, Color, 0, 0, 0, Atlas->Texture); + + OffsetX += Glyph->Advance/Font_Oversample; + } + return(OffsetX); } static void PushTextF(render_group *Group, glyph_atlas *Atlas, font_id Font, v2 P, r32 Size, v4 Color, char *Format, ...) { - temporary_memory Scratch = GetScratch(0, 0); - - va_list Arguments; - va_start(Arguments, Format); - string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); - va_end(Arguments); - - PushText(Group, Atlas, Font, P, Size, Color, String); - - ReleaseScratch(Scratch); + temp Scratch = GetScratch(0, 0); + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); + va_end(Arguments); + + PushText(Group, Atlas, Font, P, Size, Color, String); + + ReleaseScratch(Scratch); } inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Size, string Text) { - r32 X = 0; - - u8 *TextBegin = Text.Data; - u8 *TextEnd = TextBegin+Text.Count; - for(u8 *Byte = TextBegin; Byte < TextEnd;) - { - string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); - Byte += Decode.Size; - u32 Codepoint = Decode.Codepoint; - - glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample)); - Assert(Glyph); - - X += Glyph->Advance/Font_Oversample; - } - - return(X); + r32 X = 0; + + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + for(u8 *Byte = TextBegin; Byte < TextEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); + Byte += Decode.Size; + u32 Codepoint = Decode.Codepoint; + + glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample)); + Assert(Glyph); + + X += Glyph->Advance/Font_Oversample; + } + + return(X); } inline r32 CalculateRasterizedTextHeight(glyph_atlas *Atlas, font_id Font, r32 Size, string Text) { - r32 Scale = stbtt_ScaleForMappingEmToPixels(&Atlas->Fonts[Font].Info, Size)/ - stbtt_ScaleForPixelHeight(&Atlas->Fonts[Font].Info, Size); - - r32 Y = Size*Scale; - - u8 *TextBegin = Text.Data; - u8 *TextEnd = TextBegin+Text.Count; - for(u8 *Byte = TextBegin; Byte < TextEnd;) - { - string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); - Byte += Decode.Size; - u32 Codepoint = Decode.Codepoint; - - if(Codepoint == '\n') - { - Y += Size*Scale; - } - } - return(Y); + r32 Scale = stbtt_ScaleForMappingEmToPixels(&Atlas->Fonts[Font].Info, Size)/ + stbtt_ScaleForPixelHeight(&Atlas->Fonts[Font].Info, Size); + + r32 Y = Size*Scale; + + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin+Text.Count; + for(u8 *Byte = TextBegin; Byte < TextEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); + Byte += Decode.Size; + u32 Codepoint = Decode.Codepoint; + + if(Codepoint == '\n') + { + Y += Size*Scale; + } + } + return(Y); } \ No newline at end of file diff --git a/code/vn_font.h b/code/vn_font.h index 6a61b9b..98efcb0 100644 --- a/code/vn_font.h +++ b/code/vn_font.h @@ -5,14 +5,14 @@ enum font_id { - Font_Regular, - Font_Bold, - Font_Monospace, - Font_MonospaceOblique, - Font_Fancy, - Font_Icons, - - Font_Count, + Font_Regular, + Font_Bold, + Font_Monospace, + Font_MonospaceOblique, + Font_Fancy, + Font_Icons, + + Font_Count, }; #define FontIcon_None 0x0000 @@ -62,7 +62,7 @@ enum font_id #define FontIcon_DocumentFileCode 0xf1c9 #define FontIcon_UserPlus 0xf234 #define FontIcon_UserTimes 0xf235 -#define FontIcon_History 0xf235 +#define FontIcon_History 0xf1da #define FontIcon_Trash 0xf1f8 #define FontIcon_Debug 0xf188 #define FontIcon_Gamepad 0xf11b @@ -76,18 +76,18 @@ enum font_id struct glyph { - glyph *LRUNext; - glyph *LRUPrev; - - font_id Font; - u32 Codepoint; - r32 Size; - s32 Subpixel; - - v2_s32 P0; - v2_s32 P1; - v2 Offset; - r32 Advance; + glyph *LRUNext; + glyph *LRUPrev; + + font_id Font; + u32 Codepoint; + r32 Size; + s32 Subpixel; + + v2_s32 P0; + v2_s32 P1; + v2 Offset; + r32 Advance; }; #define DEFAULT_GLYPH_ATLAS_DIM 1024*4 @@ -98,33 +98,33 @@ struct glyph struct loaded_font { - stbtt_fontinfo Info; - string Data; - - s32 Ascent; - s32 Descent; - s32 LineGap; + stbtt_fontinfo Info; + string Data; + + s32 Ascent; + s32 Descent; + s32 LineGap; }; struct glyph_atlas { - arena *Arena; - - s32 MaxGlyphCount; - s32 GlyphsUsed; - glyph *Glyphs; - - glyph *LRUFirst; - glyph *LRULast; - - vn_render_commands *RenderCommands; - render_handle Texture; - - u8 *BitmapBuffer; - s32 BitmapSize; - s32 GlyphSize; - - loaded_font Fonts[Font_Count]; + arena *Arena; + + s32 MaxGlyphCount; + s32 GlyphsUsed; + glyph *Glyphs; + + glyph *LRUFirst; + glyph *LRULast; + + vn_render_commands *RenderCommands; + render_handle Texture; + + u8 *BitmapBuffer; + s32 BitmapSize; + s32 GlyphSize; + + loaded_font Fonts[Font_Count]; }; #endif //VN_FONT_H diff --git a/code/vn_platform.h b/code/vn_platform.h index b32fad7..8e01c42 100644 --- a/code/vn_platform.h +++ b/code/vn_platform.h @@ -13,119 +13,119 @@ enum { - PlatformAccess_Read = 0x1, - PlatformAccess_Write = 0x2, + PlatformAccess_Read = 0x1, + PlatformAccess_Write = 0x2, }; typedef u32 platform_access_flags; struct platform_file_handle { - u64 Platform; - b32 IsValid; + u64 Platform; + b32 IsValid; }; struct platform_file_info { - string Name; - b32 IsDirectory; + string Name; + b32 IsDirectory; }; struct platform_file_iter { - u64 U64[1024]; + u64 U64[1024]; }; enum platform_cursor { - PlatformCursor_Arrow, - PlatformCursor_Cross, - PlatformCursor_Hand, - PlatformCursor_Help, - PlatformCursor_IBeam, - PlatformCursor_SlashedCircle, - PlatformCursor_ArrowAll, - PlatformCursor_ArrowNESW, - PlatformCursor_ArrowVertical, - PlatformCursor_ArrowNWSE, - PlatformCursor_ArrowHorizontal, - PlatformCursor_Wait, - - PlatformCursor_Count + PlatformCursor_Arrow, + PlatformCursor_Cross, + PlatformCursor_Hand, + PlatformCursor_Help, + PlatformCursor_IBeam, + PlatformCursor_SlashedCircle, + PlatformCursor_ArrowAll, + PlatformCursor_ArrowNESW, + PlatformCursor_ArrowVertical, + PlatformCursor_ArrowNWSE, + PlatformCursor_ArrowHorizontal, + PlatformCursor_Wait, + + PlatformCursor_Count }; enum platform_message_type { - Platform_Message_Info, - Platform_Message_Warning, - Platform_Message_Error, - Platform_Message_Fatal, + Platform_Message_Info, + Platform_Message_Warning, + Platform_Message_Error, + Platform_Message_Fatal, }; enum platform_event_type { - PlatformEvent_Press, - PlatformEvent_Release, - PlatformEvent_Text, - PlatformEvent_MouseScroll, - PlatformEvent_WindowClose, + PlatformEvent_Press, + PlatformEvent_Release, + PlatformEvent_Text, + PlatformEvent_MouseScroll, + PlatformEvent_WindowClose, }; enum platform_key { - Key_Invalid, - - Key_A, Key_B, Key_C, Key_D, - Key_E, Key_F, Key_G, Key_H, - Key_I, Key_J, Key_K, Key_L, - Key_M, Key_N, Key_O, Key_P, - Key_Q, Key_R, Key_S, Key_T, - Key_U, Key_V, Key_W, Key_X, - Key_Y, Key_Z, - - Key_F1, Key_F2, Key_F3, Key_F4, - Key_F5, Key_F6, Key_F7, Key_F8, - Key_F9, Key_F10, Key_F11, Key_F12, - - Key_Left, Key_Right, Key_Up, Key_Down, - - Key_Space, Key_Return, - - Key_PageUp, Key_PageDown, - Key_Home, Key_End, - - Key_Backspace, Key_Delete, - - Key_Escape, - - Key_MouseLeft, Key_MouseMiddle, Key_MouseRight, + Key_Invalid, + + Key_A, Key_B, Key_C, Key_D, + Key_E, Key_F, Key_G, Key_H, + Key_I, Key_J, Key_K, Key_L, + Key_M, Key_N, Key_O, Key_P, + Key_Q, Key_R, Key_S, Key_T, + Key_U, Key_V, Key_W, Key_X, + Key_Y, Key_Z, + + Key_F1, Key_F2, Key_F3, Key_F4, + Key_F5, Key_F6, Key_F7, Key_F8, + Key_F9, Key_F10, Key_F11, Key_F12, + + Key_Left, Key_Right, Key_Up, Key_Down, + + Key_Space, Key_Return, + + Key_PageUp, Key_PageDown, + Key_Home, Key_End, + + Key_Backspace, Key_Delete, + + Key_Escape, + + Key_MouseLeft, Key_MouseMiddle, Key_MouseRight, }; typedef u32 platform_modifiers; enum { - PlatformModifier_Ctrl = (1 << 0), - PlatformModifier_Shift = (1 << 1), - PlatformModifier_Alt = (1 << 2), - - PlatformModifier_DoesNotMatter = -1 + PlatformModifier_Ctrl = (1 << 0), + PlatformModifier_Shift = (1 << 1), + PlatformModifier_Alt = (1 << 2), + + PlatformModifier_DoesNotMatter = -1 }; struct platform_event { - platform_event *Next; - platform_event *Prev; - platform_event_type Type; - platform_modifiers Modifiers; - platform_key Key; - u32 Codepoint; - v2 P; - v2 Scroll; + platform_event *Next; + platform_event *Prev; + platform_event_type Type; + platform_modifiers Modifiers; + platform_key Key; + u32 Codepoint; + v2 P; + v2 Scroll; }; struct platform_event_list { - platform_event *First; - platform_event *Last; + platform_event *First; + platform_event *Last; }; #include "generated/vn_platform.meta.h" @@ -139,58 +139,62 @@ static platform_api Platform; #define RENDER_ALLOCATE_TEXTURE(name) render_handle name(v2_s32 Dim, render_texture_format Format, b32 GenerateMipmap, void *Data) typedef RENDER_ALLOCATE_TEXTURE(render_allocate_texture); +#define RENDER_DEALLOCATE_TEXTURE(name) void name(render_handle Handle) +typedef RENDER_DEALLOCATE_TEXTURE(render_deallocate_texture); + #define RENDER_FILL_REGION(name) void name(render_handle Handle, v2_s32 DestP, v2_s32 DestDim, void *Data) typedef RENDER_FILL_REGION(render_fill_region); struct vn_render_commands { - render_allocate_texture *AllocateTexture; - render_fill_region *FillRegion; - - render_handle WhiteTexture; - - u64 MaxPushBufferSize; - u8 *PushBufferBase; - u8 *PushBufferAt; - + render_allocate_texture *AllocateTexture; + render_deallocate_texture *DeallocateTexture; + render_fill_region *FillRegion; + + render_handle WhiteTexture; + + u64 MaxPushBufferSize; + u8 *PushBufferBase; + u8 *PushBufferAt; + #if VN_USE_INSTANCING - s32 MaxInstancedQuadCount; - instanced_quad *InstancedQuadBase; - s32 InstancedQuadCount; + s32 MaxInstancedQuadCount; + instanced_quad *InstancedQuadBase; + s32 InstancedQuadCount; #else - s32 MaxQuadVertexCount; - quad_vertex *QuadVertexBase; - s32 QuadVertexCount; - - s32 MaxQuadIndexCount; - s32 *QuadIndexBase; - s32 QuadIndexCount; + s32 MaxQuadVertexCount; + quad_vertex *QuadVertexBase; + s32 QuadVertexCount; + + s32 MaxQuadIndexCount; + s32 *QuadIndexBase; + s32 QuadIndexCount; #endif - - v2 RenderDim; + + v2 RenderDim; }; // sixten: Services the application provides to the platform struct vn_input { - // sixten: Platform to application - platform_event_list *EventList; - - v2 MouseP; - v2 dMouseP; - - r32 dtForFrame; - - // sixten: Application to platform - s32 RefreshRate; - b32 ExitRequested; + // sixten: Platform to application + platform_event_list *EventList; + + v2 MouseP; + v2 dMouseP; + + r32 dtForFrame; + + // sixten: Application to platform + s32 RefreshRate; + b32 ExitRequested; }; struct vn_memory { - platform_api PlatformAPI; - struct vn_state *State; + platform_api PlatformAPI; + struct vn_state *State; }; #define VN_UPDATE_AND_RENDER(name) void name(thread_context *ThreadContext, vn_memory *Memory, vn_input *Input, vn_render_commands *RenderCommands) diff --git a/code/vn_scene.cpp b/code/vn_scene.cpp index 7e2ed82..4dc866c 100644 --- a/code/vn_scene.cpp +++ b/code/vn_scene.cpp @@ -6,1194 +6,1273 @@ static void S_ParseError(scene_compiler *Compiler, char *Message, s64 TokenOffset) { - if(!Compiler->InPanicMode) - { - 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; - } + if(!Compiler->InPanicMode) + { + 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_emission_target *Target = &Compiler->TargetStack[Compiler->TargetStackIndex]; - scene_bytecode_bucket *Bucket = Target->Bucket; - scene_bytecode_chunk *Chunk = Bucket->Last; - if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || (Target->Type == S_EmissionTarget_Named && !AreEqual(Chunk->Name, Target->Name))) - { - Chunk = PushStruct(Target->Arena, scene_bytecode_chunk); - Chunk->Name = Target->Name; - - QueuePush(Bucket->First, Bucket->Last, Chunk); - Bucket->Count += 1; - } - Chunk->Data[Chunk->Count] = Byte; - Chunk->Count += 1; + scene_emission_target *Target = &Compiler->TargetStack[Compiler->TargetStackIndex]; + scene_bytecode_bucket *Bucket = Target->Bucket; + scene_bytecode_chunk *Chunk = Bucket->Last; + if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Data) || (Target->Type == S_EmissionTarget_Named && !AreEqual(Chunk->Name, Target->Name))) + { + Chunk = PushStruct(Target->Arena, scene_bytecode_chunk); + Chunk->Name = Target->Name; + + QueuePush(Bucket->First, Bucket->Last, Chunk); + Bucket->Count += 1; + } + Chunk->Data[Chunk->Count] = Byte; + Chunk->Count += 1; } static void S_EmitU32(scene_compiler *Compiler, u32 Value) { - S_EmitByte(Compiler, Value >> 0); - S_EmitByte(Compiler, Value >> 8); - S_EmitByte(Compiler, Value >> 16); - S_EmitByte(Compiler, Value >> 24); + S_EmitByte(Compiler, Value >> 0); + S_EmitByte(Compiler, Value >> 8); + S_EmitByte(Compiler, Value >> 16); + S_EmitByte(Compiler, Value >> 24); } static void S_EmitBucket(scene_compiler *Compiler, scene_bytecode_bucket *Bucket) { - for(scene_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]); - } - } + for(scene_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; - if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Values)) - { - Chunk = PushStruct(Compiler->Arena, scene_value_chunk); - QueuePush(Compiler->FirstValueChunk, Compiler->LastValueChunk, Chunk); - } - u64 Result = Compiler->ValueCount; - Chunk->Values[Chunk->Count] = Value; - Chunk->Count += 1; - Compiler->ValueCount += 1; - return(Result); + scene_value_chunk *Chunk = Compiler->LastValueChunk; + if(!Chunk || Chunk->Count >= ArrayCount(Chunk->Values)) + { + Chunk = PushStruct(Compiler->Arena, scene_value_chunk); + QueuePush(Compiler->FirstValueChunk, Compiler->LastValueChunk, Chunk); + } + u64 Result = Compiler->ValueCount; + Chunk->Values[Chunk->Count] = Value; + Chunk->Count += 1; + Compiler->ValueCount += 1; + return(Result); } static void S_EmitConstant(scene_compiler *Compiler, scene_value Value) { - S_EmitByte(Compiler, S_Op_Constant); - S_EmitU32(Compiler, S_MakeConstant(Compiler, Value)); + S_EmitByte(Compiler, S_Op_Constant); + S_EmitU32(Compiler, S_MakeConstant(Compiler, Value)); } static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target) { - Compiler->TargetStack[Compiler->TargetStackIndex] = 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; + Compiler->TargetStackIndex += 1; + Compiler->TargetStack[Compiler->TargetStackIndex] = Target; } static void S_PopEmissionTarget(scene_compiler *Compiler) { - Compiler->TargetStackIndex -= 1; + Compiler->TargetStackIndex -= 1; } static scene_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name) { - scene_bytecode_chunk *Result = 0; - u64 Hash = HashString(Name); - scene_bytecode_bucket *Bucket = &Compiler->ProcBuckets[Hash%ArrayCount(Compiler->ProcBuckets)]; - for(scene_bytecode_chunk *Chunk = Bucket->First; Chunk != 0; Chunk = Chunk->Next) - { - if(AreEqual(Chunk->Name, Name)) - { - Result = Chunk; - break; - } - } - return(Result); + scene_bytecode_chunk *Result = 0; + u64 Hash = HashString(Name); + scene_bytecode_bucket *Bucket = &Compiler->ProcBuckets[Hash%ArrayCount(Compiler->ProcBuckets)]; + for(scene_bytecode_chunk *Chunk = Bucket->First; Chunk != 0; Chunk = Chunk->Next) + { + if(AreEqual(Chunk->Name, Name)) + { + Result = Chunk; + break; + } + } + return(Result); } static void S_AdvanceCompiler(scene_compiler *Compiler) { - Compiler->At += 1; + Compiler->At += 1; } static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token) { - scene_parse_rule Result = {}; - switch(Token.Kind) - { - case TokenKind_ParenthesisOpen: { Result = { S_ParseGrouping, 0, S_Precedence_None }; } break; - case TokenKind_Bang: { Result = { S_ParseUnary, 0, S_Precedence_None }; } break; - case TokenKind_Minus: { Result = { S_ParseUnary, S_ParseBinary, S_Precedence_Term }; } break; - case TokenKind_Plus: { Result = { 0, S_ParseBinary, S_Precedence_Term }; } break; - case TokenKind_Star: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; - case TokenKind_Slash: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; - case TokenKind_EqualEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; - case TokenKind_BangEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; - case TokenKind_Greater: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; - case TokenKind_GreaterEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; - case TokenKind_Less: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; - case TokenKind_LessEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; - case TokenKind_False: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; - case TokenKind_True: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; - case TokenKind_Numeric: { Result = { S_ParseNumber, 0, S_Precedence_None }; } break; - case TokenKind_StringLiteral: { Result = { S_ParseString, 0, S_Precedence_None }; } break; - case TokenKind_Identifier: { Result = { S_ParseVariable, 0, S_Precedence_None }; } break; - default: - { - //InvalidCodepath; - } break; - } - - return(Result); + scene_parse_rule Result = {}; + switch(Token.Kind) + { + case TokenKind_ParenthesisOpen: { Result = { S_ParseGrouping, 0, S_Precedence_None }; } break; + case TokenKind_Bang: { Result = { S_ParseUnary, 0, S_Precedence_None }; } break; + case TokenKind_Minus: { Result = { S_ParseUnary, S_ParseBinary, S_Precedence_Term }; } break; + case TokenKind_Plus: { Result = { 0, S_ParseBinary, S_Precedence_Term }; } break; + case TokenKind_Star: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; + case TokenKind_Slash: { Result = { 0, S_ParseBinary, S_Precedence_Factor }; } break; + case TokenKind_EqualEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; + case TokenKind_BangEqual: { Result = { 0, S_ParseBinary, S_Precedence_Equality }; } break; + case TokenKind_Greater: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_GreaterEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_Less: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_LessEqual: { Result = { 0, S_ParseBinary, S_Precedence_Comparison }; } break; + case TokenKind_False: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; + case TokenKind_True: { Result = { S_ParseLiteral, 0, S_Precedence_None }; } break; + case TokenKind_Numeric: { Result = { S_ParseNumber, 0, S_Precedence_None }; } break; + case TokenKind_StringLiteral: { Result = { S_ParseString, 0, S_Precedence_None }; } break; + case TokenKind_Identifier: { Result = { S_ParseVariable, 0, S_Precedence_None }; } break; + default: + { + //InvalidCodepath; + } break; + } + + return(Result); } static b32 S_MatchToken(scene_compiler *Compiler, token Token, token_kind Kind) { - b32 Result = false; - string String = T_StringFromToken(Compiler->Text, Token); - if(Token.Kind == Kind) - { - Result = true; - } - return(Result); + b32 Result = false; + string String = T_StringFromToken(Compiler->Text, Token); + if(Token.Kind == Kind) + { + Result = true; + } + return(Result); } static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message) { - token Token = Compiler->At[0]; - string String = T_StringFromToken(Compiler->Text, Token); - if(Token.Kind != Kind) - { - S_ParseError(Compiler, Message); - } - - Compiler->At += 1; - return(Token); + token Token = Compiler->At[0]; + string String = T_StringFromToken(Compiler->Text, Token); + if(Token.Kind != Kind) + { + S_ParseError(Compiler, Message); + } + + Compiler->At += 1; + return(Token); } static void S_ParseTopLevelDeclaration(scene_compiler *Compiler) { - if(Compiler->At[0].Kind == TokenKind_Proc) - { - Compiler->At += 1; - S_ParseProcedure(Compiler); - } - else if(Compiler->At[0].Kind == TokenKind_Var) - { - Compiler->At += 1; - S_ParseVariableDeclaration(Compiler); - } - else - { - S_ParseError(Compiler, "Expected top-level declaration (proc or var).", 0); - Compiler->At += 1; - } + if(Compiler->At[0].Kind == TokenKind_Proc) + { + Compiler->At += 1; + S_ParseProcedure(Compiler); + } + else if(Compiler->At[0].Kind == TokenKind_Var) + { + Compiler->At += 1; + S_ParseVariableDeclaration(Compiler); + } + else if(Compiler->At[0].Kind == TokenKind_Nav) + { + Compiler->At += 1; + S_ParseNavFilePath(Compiler); + } + else if(Compiler->At[0].Kind == TokenKind_Background) + { + Compiler->At += 1; + S_ParseBackgroundAsset(Compiler); + } + else + { + S_ParseError(Compiler, "Expected top-level declaration (proc, var, background or nav).", 0); + Compiler->At += 1; + } +} + +static void S_ParseNavFilePath(scene_compiler *Compiler) +{ + token NameToken = Compiler->At[0]; + Compiler->At += 1; + + if(NameToken.Kind == TokenKind_StringLiteral) + { + Compiler->NavFileName = Substring(Compiler->Text, Pad(NameToken.Range, -1)); + if(Compiler->At[0].Kind == TokenKind_Semicolon) + { + // sixten: all good + Compiler->At += 1; + } + else + { + S_ParseError(Compiler, "Expected ';'.", 0); + } + } + else + { + S_ParseError(Compiler, "Expected nav file name."); + } +} + +static void S_ParseBackgroundAsset(scene_compiler *Compiler) +{ + token AssetNameToken = Compiler->At[0]; + Compiler->At += 1; + if(AssetNameToken.Kind == TokenKind_Identifier) + { + //- sixten: find asset id by name + string AssetName = Substring(Compiler->Text, AssetNameToken.Range); + Compiler->BackgroundTexture = AssetID_None; + for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1) + { + if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName)) + { + Compiler->BackgroundTexture = AssetIndex; + goto ValidAssetFound; + } + } + S_ParseError(Compiler, "Invalid asset name."); + + ValidAssetFound:; + if(Compiler->At[0].Kind == TokenKind_Semicolon) + { + // sixten: all good + Compiler->At += 1; + } + else + { + S_ParseError(Compiler, "Expected ';'.", 0); + } + } } 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."); - - if(!Compiler->InPanicMode) - { - string Name = T_StringFromToken(Compiler->Text, NameToken); - scene_bytecode_bucket *Bucket = &Compiler->ProcBuckets[HashString(Name)%ArrayCount(Compiler->ProcBuckets)]; - S_SetEmissionTarget(Compiler, S_NamedEmissionTarget(Compiler->Arena, Bucket, Name)); - - for(;Compiler->At < Compiler->TokensEnd;) - { - if(Compiler->At[0].Kind == TokenKind_CurlyClose) - { - Compiler->At += 1; - break; - } - else - { - S_ParseDeclaration(Compiler); - } - } - } + token NameToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected procedure name after 'proc'"); + S_ConsumeToken(Compiler, TokenKind_CurlyOpen, "Expected '{' after procedure name."); + + if(!Compiler->InPanicMode) + { + string Name = T_StringFromToken(Compiler->Text, NameToken); + scene_bytecode_bucket *Bucket = &Compiler->ProcBuckets[HashString(Name)%ArrayCount(Compiler->ProcBuckets)]; + S_SetEmissionTarget(Compiler, S_NamedEmissionTarget(Compiler->Arena, Bucket, Name)); + + for(;Compiler->At < Compiler->TokensEnd;) + { + 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) - { - case TokenKind_Var: - { - Compiler->At += 1; - S_ParseVariableDeclaration(Compiler); - } break; - case TokenKind_Jump: - { - Compiler->At += 1; - S_ParseJumpStatement(Compiler); - } break; - case TokenKind_Branch: - { - Compiler->At += 1; - S_ParseBranchStatement(Compiler); - } break; - case TokenKind_At: - { - Compiler->At += 1; - - token IdentifierToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected identifier after '@'"); - S_EmitConstant(Compiler, S_MakeSourceRef(IdentifierToken)); - - if(Compiler->At->Kind == TokenKind_ParenthesisOpen) - { - Compiler->At += 1; - token StateToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected character state."); - S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after character state."); - - S_EmitConstant(Compiler, S_MakeSourceRef(StateToken)); - } - else - { - S_EmitConstant(Compiler, S_MakeNil()); - } - - S_EmitByte(Compiler, S_Op_ShowCharacter); - - if(Compiler->At->Kind == TokenKind_Semicolon) - { - Compiler->At += 1; - } - } break; - case TokenKind_StringLiteral: - { - S_ParseExpression(Compiler); - - //- sixten: parse tags - { - b32 EmitAwait = true; - b32 EmitClear = true; - for(;Compiler->At[0].Kind == TokenKind_PoundSign;) - { - Compiler->At += 1; - token TagToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected tag name after '#'."); - string TagString = T_StringFromToken(Compiler->Text, TagToken); - if(AreEqual(TagString, StrLit("noclear"))) - { - EmitClear = false; - } - else if(AreEqual(TagString, StrLit("noawait"))) - { - EmitAwait = false; - } - else - { - S_ParseError(Compiler, "Unknown tag."); - } - } - - if(EmitClear) - { - S_EmitByte(Compiler, S_Op_ClearDialog); - } - - S_EmitByte(Compiler, S_Op_LineEntry); - - if(EmitAwait) - { - S_EmitByte(Compiler, S_Op_AwaitInput); - } - - S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement."); - } - } break; - default: - { - S_ParseStatement(Compiler); - S_EmitByte(Compiler, S_Op_Pop); - } break; - } - - if(Compiler->InPanicMode) - { - for(;Compiler->At < Compiler->TokensEnd;) - { - if(Compiler->At[-1].Kind == TokenKind_Semicolon) - { - goto End; - } - - switch(Compiler->At[0].Kind) - { - case TokenKind_Var: goto End; - case TokenKind_StringLiteral: goto End; - case TokenKind_Jump: goto End; - case TokenKind_Branch: goto End; - default: break; - } - - Compiler->At += 1; - } - - End: - Compiler->InPanicMode = false; - } + switch(Compiler->At[0].Kind) + { + case TokenKind_Var: + { + Compiler->At += 1; + S_ParseVariableDeclaration(Compiler); + } break; + case TokenKind_Jump: + { + Compiler->At += 1; + S_ParseJumpStatement(Compiler); + } break; + case TokenKind_Branch: + { + Compiler->At += 1; + S_ParseBranchStatement(Compiler); + } break; + case TokenKind_At: + { + Compiler->At += 1; + + token IdentifierToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected identifier after '@'"); + S_EmitConstant(Compiler, S_MakeSourceRef(IdentifierToken)); + + if(Compiler->At->Kind == TokenKind_ParenthesisOpen) + { + Compiler->At += 1; + token StateToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected character state."); + S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after character state."); + + S_EmitConstant(Compiler, S_MakeSourceRef(StateToken)); + } + else + { + S_EmitConstant(Compiler, S_MakeNil()); + } + + S_EmitByte(Compiler, S_Op_ShowCharacter); + + if(Compiler->At->Kind == TokenKind_Semicolon) + { + Compiler->At += 1; + } + } break; + case TokenKind_StringLiteral: + { + S_ParseExpression(Compiler); + + //- sixten: parse tags + { + b32 EmitAwait = true; + b32 EmitClear = true; + for(;Compiler->At[0].Kind == TokenKind_PoundSign;) + { + Compiler->At += 1; + token TagToken = S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected tag name after '#'."); + string TagString = T_StringFromToken(Compiler->Text, TagToken); + if(AreEqual(TagString, StrLit("noclear"))) + { + EmitClear = false; + } + else if(AreEqual(TagString, StrLit("noawait"))) + { + EmitAwait = false; + } + else + { + S_ParseError(Compiler, "Unknown tag."); + } + } + + if(EmitClear) + { + S_EmitByte(Compiler, S_Op_ClearDialog); + } + + S_EmitByte(Compiler, S_Op_LineEntry); + + if(EmitAwait) + { + S_EmitByte(Compiler, S_Op_AwaitInput); + } + + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement."); + } + } break; + default: + { + S_ParseStatement(Compiler); + S_EmitByte(Compiler, S_Op_Pop); + } break; + } + + if(Compiler->InPanicMode) + { + for(;Compiler->At < Compiler->TokensEnd;) + { + if(Compiler->At[-1].Kind == TokenKind_Semicolon) + { + goto End; + } + + switch(Compiler->At[0].Kind) + { + case TokenKind_Var: goto End; + case TokenKind_StringLiteral: goto End; + case TokenKind_Jump: goto End; + case TokenKind_Branch: goto End; + default: break; + } + + 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_MakeSourceRef(Compiler->At[-1])); - - if(Compiler->At[0].Kind == TokenKind_Equal) - { - Compiler->At += 1; - S_ParseExpression(Compiler); - } - else - { - S_EmitByte(Compiler, S_Op_Nil); - } - - S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after variable declaration."); - - S_EmitByte(Compiler, S_Op_DefineGlobal); - - Assert(NameConstant < U32_Max); - S_EmitU32(Compiler, NameConstant); + S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected variable name."); + u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(Compiler->At[-1])); + + if(Compiler->At[0].Kind == TokenKind_Equal) + { + Compiler->At += 1; + S_ParseExpression(Compiler); + } + else + { + S_EmitByte(Compiler, S_Op_Nil); + } + + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after variable declaration."); + + S_EmitByte(Compiler, S_Op_DefineGlobal); + + Assert(NameConstant < U32_Max); + S_EmitU32(Compiler, NameConstant); } 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) { - u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(Token)); - if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) - { - Compiler->At += 1; - S_ParseExpression(Compiler); - S_EmitByte(Compiler, S_Op_SetGlobal); - } - else - { - S_EmitByte(Compiler, S_Op_GetGlobal); - } - S_EmitU32(Compiler, NameConstant); + u64 NameConstant = S_MakeConstant(Compiler, S_MakeSourceRef(Token)); + if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) + { + Compiler->At += 1; + S_ParseExpression(Compiler); + S_EmitByte(Compiler, S_Op_SetGlobal); + } + else + { + S_EmitByte(Compiler, S_Op_GetGlobal); + } + S_EmitU32(Compiler, NameConstant); } 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_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(DestToken))); - - S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after jump statement."); + S_ConsumeToken(Compiler, TokenKind_Identifier, "Expected jump destination."); + token DestToken = Compiler->At[-1]; + + S_EmitByte(Compiler, S_Op_Jump); + S_EmitU32(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_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(Branch->Name))); - S_EmitU32(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; - S_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeOffset(BaseOffset))); - BaseOffset += 4; - - 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) - { - // sixten(NOTE): This little line here feels rather sketchy, it may one day fail on us. - Branch->EndOffsetValue->Offset = BaseOffset-Branch->EndOffsetValue->Offset-4; - } - - ReleaseScratch(Scratch); + temp 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_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeSourceRef(Branch->Name))); + S_EmitU32(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; + S_EmitU32(Compiler, S_MakeConstant(Compiler, S_MakeOffset(BaseOffset))); + BaseOffset += 4; + + 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) + { + // sixten(NOTE): This little line here feels rather sketchy, it may one day fail on us. + Branch->EndOffsetValue->Offset = BaseOffset-Branch->EndOffsetValue->Offset-4; + } + + ReleaseScratch(Scratch); } static scene_branch_case *S_ParseBranchCase(scene_compiler *Compiler, 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); + 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); - S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement."); + S_ParseExpression(Compiler); + S_ConsumeToken(Compiler, TokenKind_Semicolon, "Expected ';' after statement."); } static void S_ParseExpression(scene_compiler *Compiler) { - S_ParsePrecedence(Compiler, S_Precedence_Assignment); + S_ParsePrecedence(Compiler, S_Precedence_Assignment); } static void S_ParseLiteral(scene_compiler *Compiler, b32 CanAssign) { - string Value = T_StringFromToken(Compiler->Text, Compiler->At[-1]); - switch(Compiler->At[-1].Kind) - { - case TokenKind_False: { S_EmitByte(Compiler, S_Op_False); } break; - case TokenKind_True: { S_EmitByte(Compiler, S_Op_True); } break; - InvalidDefaultCase; - } + string Value = T_StringFromToken(Compiler->Text, Compiler->At[-1]); + switch(Compiler->At[-1].Kind) + { + case TokenKind_False: { S_EmitByte(Compiler, S_Op_False); } break; + case TokenKind_True: { S_EmitByte(Compiler, S_Op_True); } break; + InvalidDefaultCase; + } } static void S_ParseNumber(scene_compiler *Compiler, b32 CanAssign) { - r64 Value = DoubleFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); - - S_EmitConstant(Compiler, S_MakeNumber(Value)); + r64 Value = DoubleFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); + + S_EmitConstant(Compiler, S_MakeNumber(Value)); } static void S_ParseString(scene_compiler *Compiler, b32 CanAssign) { - S_EmitConstant(Compiler, S_MakeSourceRef(Pad(Compiler->At[-1].Range, -1))); + S_EmitConstant(Compiler, S_MakeSourceRef(Pad(Compiler->At[-1].Range, -1))); } static void S_ParseGrouping(scene_compiler *Compiler, b32 CanAssign) { - S_ParseExpression(Compiler); - S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after expression."); + S_ParseExpression(Compiler); + S_ConsumeToken(Compiler, TokenKind_ParenthesisClose, "Expected ')' after expression."); } static void S_ParseUnary(scene_compiler *Compiler, b32 CanAssign) { - scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); - S_ParsePrecedence(Compiler, S_Precedence_Unary); - - switch(Operator) - { - case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Negate); } break; - case S_Operator_Not: { S_EmitByte(Compiler, S_Op_Not); } break; - InvalidDefaultCase; - } + scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Compiler->At[-1])); + S_ParsePrecedence(Compiler, S_Precedence_Unary); + + switch(Operator) + { + case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Negate); } break; + case S_Operator_Not: { S_EmitByte(Compiler, S_Op_Not); } break; + InvalidDefaultCase; + } } static void S_ParseBinary(scene_compiler *Compiler, b32 CanAssign) { - token Token = Compiler->At[-1]; - scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Token)); - scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Token); - S_ParsePrecedence(Compiler, (scene_precedence)(Rule.Precedence + 1)); - - switch(Operator) - { - case S_Operator_Add: { S_EmitByte(Compiler, S_Op_Add); } break; - case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Subtract); } break; - case S_Operator_Multiply: { S_EmitByte(Compiler, S_Op_Multiply); } break; - case S_Operator_Divide: { S_EmitByte(Compiler, S_Op_Divide); } break; - case S_Operator_Equals: { S_EmitByte(Compiler, S_Op_Equal); } break; - case S_Operator_NotEquals: { S_EmitByte(Compiler, S_Op_Equal); S_EmitByte(Compiler, S_Op_Not); } break; - case S_Operator_Greater: { S_EmitByte(Compiler, S_Op_Greater); } break; - case S_Operator_GreaterThanOrEquals: { S_EmitByte(Compiler, S_Op_Less); S_EmitByte(Compiler, S_Op_Not); } break; - case S_Operator_Less: { S_EmitByte(Compiler, S_Op_Less); } break; - case S_Operator_LessThanOrEquals: { S_EmitByte(Compiler, S_Op_Greater); S_EmitByte(Compiler, S_Op_Not); } break; - InvalidDefaultCase; - } + token Token = Compiler->At[-1]; + scene_operator Operator = S_OperatorFromString(T_StringFromToken(Compiler->Text, Token)); + scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Token); + S_ParsePrecedence(Compiler, (scene_precedence)(Rule.Precedence + 1)); + + switch(Operator) + { + case S_Operator_Add: { S_EmitByte(Compiler, S_Op_Add); } break; + case S_Operator_Minus: { S_EmitByte(Compiler, S_Op_Subtract); } break; + case S_Operator_Multiply: { S_EmitByte(Compiler, S_Op_Multiply); } break; + case S_Operator_Divide: { S_EmitByte(Compiler, S_Op_Divide); } break; + case S_Operator_Equals: { S_EmitByte(Compiler, S_Op_Equal); } break; + case S_Operator_NotEquals: { S_EmitByte(Compiler, S_Op_Equal); S_EmitByte(Compiler, S_Op_Not); } break; + case S_Operator_Greater: { S_EmitByte(Compiler, S_Op_Greater); } break; + case S_Operator_GreaterThanOrEquals: { S_EmitByte(Compiler, S_Op_Less); S_EmitByte(Compiler, S_Op_Not); } break; + case S_Operator_Less: { S_EmitByte(Compiler, S_Op_Less); } break; + case S_Operator_LessThanOrEquals: { S_EmitByte(Compiler, S_Op_Greater); S_EmitByte(Compiler, S_Op_Not); } break; + InvalidDefaultCase; + } } static void S_ParsePrecedence(scene_compiler *Compiler, scene_precedence Precedence) { - b32 CanAssign = (Precedence <= S_Precedence_Assignment); - - S_AdvanceCompiler(Compiler); - scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Compiler->At[-1]); - if(Rule.PrefixRule) - { - Rule.PrefixRule(Compiler, CanAssign); - } - else - { - S_ParseError(Compiler, "Expected expression."); - } - - - while(Precedence <= (Rule = S_ParseRuleFromToken(Compiler, Compiler->At[0])).Precedence) - { - S_AdvanceCompiler(Compiler); - Rule.InfixRule(Compiler, CanAssign); - } - - if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) - { - S_ParseError(Compiler, "Invalid assignment target."); - } + b32 CanAssign = (Precedence <= S_Precedence_Assignment); + + S_AdvanceCompiler(Compiler); + scene_parse_rule Rule = S_ParseRuleFromToken(Compiler, Compiler->At[-1]); + if(Rule.PrefixRule) + { + Rule.PrefixRule(Compiler, CanAssign); + } + else + { + S_ParseError(Compiler, "Expected expression."); + } + + + while(Precedence <= (Rule = S_ParseRuleFromToken(Compiler, Compiler->At[0])).Precedence) + { + S_AdvanceCompiler(Compiler); + Rule.InfixRule(Compiler, CanAssign); + } + + if(CanAssign && Compiler->At[0].Kind == TokenKind_Equal) + { + S_ParseError(Compiler, "Invalid assignment target."); + } } struct proc_from_chunks_result { - scene_proc *Proc; - scene_bytecode_chunk *NextChunk; + scene_proc *Proc; + scene_bytecode_chunk *NextChunk; }; static proc_from_chunks_result S_ProcFromChunks(arena *Arena, scene_bytecode_chunk *First) { - Assert(First != 0); - string ChunkName = First->Name; - - //- sixten: find required bytes - s64 RequiredBytes = 0; - scene_bytecode_chunk *NextChunk = 0; - { - scene_bytecode_chunk *Chunk = First; - for(;Chunk != 0 && AreEqual(Chunk->Name, ChunkName); Chunk = Chunk->Next) - { - RequiredBytes += Chunk->Count; - } - NextChunk = Chunk; - } - - scene_proc *Proc = PushStruct(Arena, scene_proc); - Proc->Name = ChunkName; - Proc->Data = PushArray(Arena, u8, RequiredBytes); - Proc->Count = RequiredBytes; - - //- sixten: copy over data from chunks - u8 *Dest = Proc->Data; - for(scene_bytecode_chunk *Chunk = First; Chunk != NextChunk; Chunk = Chunk->Next) - { - Copy(Dest, Chunk->Data, Chunk->Count); - Dest += Chunk->Count; - } - - //- sixten: fill & return - proc_from_chunks_result Result; - { - Result.Proc = Proc; - Result.NextChunk = NextChunk; - } - return(Result); + Assert(First != 0); + string ChunkName = First->Name; + + //- sixten: find required bytes + s64 RequiredBytes = 0; + scene_bytecode_chunk *NextChunk = 0; + { + scene_bytecode_chunk *Chunk = First; + for(;Chunk != 0 && AreEqual(Chunk->Name, ChunkName); Chunk = Chunk->Next) + { + RequiredBytes += Chunk->Count; + } + NextChunk = Chunk; + } + + scene_proc *Proc = PushStruct(Arena, scene_proc); + Proc->Name = ChunkName; + Proc->Data = PushArray(Arena, u8, RequiredBytes); + Proc->Count = RequiredBytes; + + //- sixten: copy over data from chunks + u8 *Dest = Proc->Data; + for(scene_bytecode_chunk *Chunk = First; Chunk != NextChunk; Chunk = Chunk->Next) + { + Copy(Dest, Chunk->Data, Chunk->Count); + Dest += Chunk->Count; + } + + //- sixten: fill & return + proc_from_chunks_result Result; + { + Result.Proc = Proc; + Result.NextChunk = NextChunk; + } + return(Result); } static compiled_scene S_ScriptFromText(arena *Arena, string Text) { - compiled_scene Result = {}; - - temporary_memory Scratch = GetScratch(&Arena, 1); - tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text, T_IsIrregular); - - scene_compiler Compiler = {}; - { - Compiler.Arena = Scratch.Arena; - Compiler.Text = Text; - Compiler.TokensBegin = TokenizeResult.Tokens.Tokens; - Compiler.TokensEnd = Compiler.TokensBegin+TokenizeResult.Tokens.Count; - Compiler.At = Compiler.TokensBegin; - }; - S_SetEmissionTarget(&Compiler, S_RawEmissionTarget(Compiler.Arena, &Compiler.GlobalScope)); - - //- sixten: append tokenization errors - b32 FoundTokenizationError = false; - for(tokenizer_message *Message = TokenizeResult.Messages.First; Message != 0; Message = Message->Next) - { - 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 global scope - if(Compiler.GlobalScope.First) - { - proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Compiler.GlobalScope.First); - Result.GlobalScope = ProcResult.Proc; - } - - //- sixten: bake compiled chunks - for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiler.ProcBuckets); BucketIndex += 1) - { - scene_bytecode_bucket *Bucket = &Compiler.ProcBuckets[BucketIndex]; - for(scene_bytecode_chunk *Chunk = Bucket->First; Chunk != 0;) - { - proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Chunk); - s64 Hash = HashString(Chunk->Name); - scene_proc_bucket *DestBucket = &Result.Buckets[Hash%ArrayCount(Result.Buckets)]; - QueuePush(DestBucket->First, DestBucket->Last, ProcResult.Proc); - Chunk = ProcResult.NextChunk; - } - } - - //- sixten: bake value chunks - { - Result.Values = PushArray(Arena, scene_value, Compiler.ValueCount); - Result.ValueCount = Compiler.ValueCount; - scene_value *Dest = Result.Values; - for(scene_value_chunk *Chunk = Compiler.FirstValueChunk; Chunk != 0; Chunk = Chunk->Next) - { - 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); + compiled_scene Result = {}; + + temp Scratch = GetScratch(&Arena, 1); + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text, T_IsIrregular); + + scene_compiler Compiler = {}; + { + Compiler.Arena = Scratch.Arena; + Compiler.Text = Text; + Compiler.TokensBegin = TokenizeResult.Tokens.Tokens; + Compiler.TokensEnd = Compiler.TokensBegin+TokenizeResult.Tokens.Count; + Compiler.At = Compiler.TokensBegin; + }; + S_SetEmissionTarget(&Compiler, S_RawEmissionTarget(Compiler.Arena, &Compiler.GlobalScope)); + + //- sixten: append tokenization errors + b32 FoundTokenizationError = false; + for(tokenizer_message *Message = TokenizeResult.Messages.First; Message != 0; Message = Message->Next) + { + 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 global scope + if(Compiler.GlobalScope.First) + { + proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Compiler.GlobalScope.First); + Result.GlobalScope = ProcResult.Proc; + } + + //- sixten: bake compiled chunks + for(s64 BucketIndex = 0; BucketIndex < ArrayCount(Compiler.ProcBuckets); BucketIndex += 1) + { + scene_bytecode_bucket *Bucket = &Compiler.ProcBuckets[BucketIndex]; + for(scene_bytecode_chunk *Chunk = Bucket->First; Chunk != 0;) + { + proc_from_chunks_result ProcResult = S_ProcFromChunks(Arena, Chunk); + s64 Hash = HashString(Chunk->Name); + scene_proc_bucket *DestBucket = &Result.Buckets[Hash%ArrayCount(Result.Buckets)]; + QueuePush(DestBucket->First, DestBucket->Last, ProcResult.Proc); + Chunk = ProcResult.NextChunk; + } + } + + //- sixten: bake value chunks + { + Result.Values = PushArray(Arena, scene_value, Compiler.ValueCount); + Result.ValueCount = Compiler.ValueCount; + scene_value *Dest = Result.Values; + for(scene_value_chunk *Chunk = Compiler.FirstValueChunk; Chunk != 0; Chunk = Chunk->Next) + { + 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: copy nav file name + Result.NavFileName = Compiler.NavFileName; + + //- sixten: copy background texture handle + Result.BackgroundTexture = Compiler.BackgroundTexture; + + // 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(arena *Arena, compiled_scene *Compiled) { - compiled_scene Result = {}; - - Assert(Compiled->Errors.Count == 0); - - //- sixten: copy the global scope - if(Compiled->GlobalScope) - { - Result.GlobalScope = PushStruct(Arena, scene_proc); - Result.GlobalScope->Name = PushString(Arena, Compiled->GlobalScope->Name); - Result.GlobalScope->Data = PushArray(Arena, u8, Compiled->GlobalScope->Count); - Copy(Result.GlobalScope->Data, Compiled->GlobalScope->Data, Compiled->GlobalScope->Count); - Result.GlobalScope->Count = Compiled->GlobalScope->Count; - } - - //- 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; sixten(TODO): I don't know why this is commented out. - - return(Result); + compiled_scene Result = {}; + + //- sixten(TODO): copy over the errors + //Assert(Compiled->Errors.Count == 0); + + //- sixten: copy the global scope + if(Compiled->GlobalScope) + { + Result.GlobalScope = PushStruct(Arena, scene_proc); + Result.GlobalScope->Name = PushString(Arena, Compiled->GlobalScope->Name); + Result.GlobalScope->Data = PushArray(Arena, u8, Compiled->GlobalScope->Count); + Copy(Result.GlobalScope->Data, Compiled->GlobalScope->Data, Compiled->GlobalScope->Count); + Result.GlobalScope->Count = Compiled->GlobalScope->Count; + } + + //- 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); + + //- sixten: copy nav file + Result.NavFileName = PushString(Arena, Compiled->NavFileName); + + //- sixten: copy background texture handle + Result.BackgroundTexture = Compiled->BackgroundTexture; + + Result.IsValid = true;//Compiled->IsValid; // sixten(TODO): I don't know why this is commented out. + + 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->Errors.First, Runtime->Errors.Last, Error); - Runtime->Errors.Count += 1; + scene_runtime_error *Error = PushStruct(Runtime->ErrorArena, scene_runtime_error); + Error->Message = PushString(Runtime->ErrorArena, Message); + DLLInsertLast(Runtime->Errors.First, Runtime->Errors.Last, Error); + Runtime->Errors.Count += 1; } 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->Errors.First, Runtime->Errors.Last, Error); - Runtime->Errors.Count += 1; - - va_end(Arguments); + 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->Errors.First, Runtime->Errors.Last, Error); + Runtime->Errors.Count += 1; + + va_end(Arguments); } static scene_named_value *S_FindGlobalVariableByName(scene_runtime *Runtime, string Name, b32 AlwaysCreate) { - scene_named_value *Result = 0; - - u64 Hash = HashString(Name); - scene_named_value_bucket *Bucket = &Runtime->GlobalVariableBuckets[Hash%ArrayCount(Runtime->GlobalVariableBuckets)]; - - //- sixten: search for the value in the bucket - for(scene_named_value_node *Node = Bucket->First; Node != 0; Node = Node->Next) - { - scene_named_value *Value = &Node->NamedValue; - if(AreEqual(Name, Value->Name)) - { - Result = Value; - break; - } - } - - //- sixten: create it if it doesn't exist - if(Result == 0) - { - //- sixten: check the free list first - scene_named_value_bucket *FreeList = &Runtime->GlobalVariableFreeList; - scene_named_value_node *Node = FreeList->First; - if(Node) - { - DLLRemove(FreeList->First, FreeList->Last, Node); - } - else - { - Node = PushStruct(Runtime->RuntimeArena, scene_named_value_node); - } - DLLInsertLast(Bucket->First, Bucket->Last, Node); - - Result = &Node->NamedValue; - Result->Name = Name; - } - - return(Result); + scene_named_value *Result = 0; + + u64 Hash = HashString(Name); + scene_named_value_bucket *Bucket = &Runtime->GlobalVariableBuckets[Hash%ArrayCount(Runtime->GlobalVariableBuckets)]; + + //- sixten: search for the value in the bucket + for(scene_named_value_node *Node = Bucket->First; Node != 0; Node = Node->Next) + { + scene_named_value *Value = &Node->NamedValue; + if(AreEqual(Name, Value->Name)) + { + Result = Value; + break; + } + } + + //- sixten: create it if it doesn't exist + if(Result == 0) + { + //- sixten: check the free list first + scene_named_value_bucket *FreeList = &Runtime->GlobalVariableFreeList; + scene_named_value_node *Node = FreeList->First; + if(Node) + { + DLLRemove(FreeList->First, FreeList->Last, Node); + } + else + { + Node = PushStruct(Runtime->RuntimeArena, scene_named_value_node); + } + DLLInsertLast(Bucket->First, Bucket->Last, Node); + + Result = &Node->NamedValue; + Result->Name = Name; + } + + return(Result); } 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); + 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 void S_PushStack(scene_runtime *Runtime, scene_value Value) { - scene_runtime_stack *Stack = &Runtime->Stack; - if(Stack->Count < ArrayCount(Stack->Stack)) - { - Stack->Stack[Stack->Count] = Value; - Stack->Count += 1; - } - else - { - S_RuntimeError(Runtime, StrLit("Stack overflow")); - } + scene_runtime_stack *Stack = &Runtime->Stack; + if(Stack->Count < ArrayCount(Stack->Stack)) + { + Stack->Stack[Stack->Count] = Value; + Stack->Count += 1; + } + else + { + S_RuntimeError(Runtime, StrLit("Stack overflow")); + } } static scene_value S_PopStack(scene_runtime *Runtime) { - scene_value Result = {}; - scene_runtime_stack *Stack = &Runtime->Stack; - if(Stack->Count > 0) - { - Stack->Count -= 1; - Result = Stack->Stack[Stack->Count]; - } - else - { - S_RuntimeError(Runtime, StrLit("Stack underflow")); - } - return(Result); + scene_value Result = {}; + scene_runtime_stack *Stack = &Runtime->Stack; + if(Stack->Count > 0) + { + Stack->Count -= 1; + Result = Stack->Stack[Stack->Count]; + } + else + { + S_RuntimeError(Runtime, StrLit("Stack underflow")); + } + return(Result); } static scene_value S_PopStackAndImplicitConvert(scene_runtime *Runtime) { - scene_value Result = S_PopStack(Runtime); - if(Result.Kind == S_ValueKind_SourceRef) - { - Result.Kind = S_ValueKind_String; - Result.String = Substring(Runtime->Compiled.Source, Result.SourceRef); - } - return(Result); + scene_value Result = S_PopStack(Runtime); + if(Result.Kind == S_ValueKind_SourceRef) + { + Result.Kind = S_ValueKind_String; + Result.String = Substring(Runtime->Compiled.Source, Result.SourceRef); + } + return(Result); } static void S_SetCurrentProc(scene_runtime *Runtime, scene_proc *Proc) { - Runtime->CurrentProc = Proc; - Runtime->IP = 0; - Runtime->BranchCount = 0; + Runtime->CurrentProc = Proc; + Runtime->IP = 0; + Runtime->BranchCount = 0; } static void S_ResetRuntime(scene_runtime *Runtime) { - S_SetCurrentProc(Runtime, 0); - Fill(Runtime->GlobalVariableBuckets, 0, sizeof(Runtime->GlobalVariableBuckets)); - ZeroStruct(&Runtime->GlobalVariableFreeList); - - ZeroStruct(&Runtime->Errors); - - if(Runtime->RuntimeArena) - { - ArenaClear(Runtime->RuntimeArena); - } - else - { - Runtime->RuntimeArena = ArenaAlloc(Kilobytes(16)); - } + S_SetCurrentProc(Runtime, 0); + Fill(Runtime->GlobalVariableBuckets, 0, sizeof(Runtime->GlobalVariableBuckets)); + ZeroStruct(&Runtime->GlobalVariableFreeList); + + ZeroStruct(&Runtime->Errors); + + if(Runtime->RuntimeArena) + { + ArenaClear(Runtime->RuntimeArena); + } + else + { + Runtime->RuntimeArena = ArenaAlloc(Kilobytes(16)); + } } static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32 AdvanceOnAwait) { - scene_runtime_result Result = {}; - compiled_scene *Compiled = &Runtime->Compiled; - - Assert(Runtime != 0); - Assert(Compiled->IsValid); - - // sixten(NOTE): This will only work on little endian architectures. + scene_runtime_result Result = {}; + compiled_scene *Compiled = &Runtime->Compiled; + + Assert(Runtime != 0); + Assert(Compiled->IsValid); + + // sixten(NOTE): This will only work on little endian architectures. #define S_ReadU32() (Runtime->IP += 4, *(u32 *)(Data+Runtime->IP-4)) #define S_ReadValue() Compiled->Values[S_ReadU32()] - - //- sixten: run the bytecode interpreter - if(Runtime->CurrentProc) - { - if(Runtime->IP < Runtime->CurrentProc->Count) - { - u8 *Data = Runtime->CurrentProc->Data; - - switch(Data[Runtime->IP]) - { - case S_Op_Constant: - { - Runtime->IP += 1; - S_PushStack(Runtime, S_ReadValue()); - } break; - - case S_Op_Pop: - { - Runtime->IP += 1; - S_PopStack(Runtime); - } break; - - case S_Op_Nil: - { - Runtime->IP += 1; - S_PushStack(Runtime, S_MakeNil()); - } break; - - case S_Op_True: - { - Runtime->IP += 1; - S_PushStack(Runtime, S_MakeBoolean(true)); - } break; - - case S_Op_False: - { - Runtime->IP += 1; - S_PushStack(Runtime, S_MakeBoolean(false)); - } break; - - case S_Op_Add: - { - Runtime->IP += 1; - scene_value Value1 = S_PopStackAndImplicitConvert(Runtime); - scene_value Value2 = S_PopStackAndImplicitConvert(Runtime); - if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number) - { - S_PushStack(Runtime, S_MakeNumber(Value1.Number + Value2.Number)); - } - else if(Value1.Kind == S_ValueKind_String && Value2.Kind == S_ValueKind_String) - { - S_RuntimeError(Runtime, StrLit("String concatination is not yet supported.")); - } - else - { - S_RuntimeError(Runtime, StrLit("This operation is not supported.")); - } - } break; - - case S_Op_Subtract: - { - Runtime->IP += 1; - scene_value Value1 = S_PopStack(Runtime); - scene_value Value2 = S_PopStack(Runtime); - if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number) - { - S_PushStack(Runtime, S_MakeNumber(Value1.Number - Value2.Number)); - } - else - { - S_RuntimeError(Runtime, StrLit("This operation is not supported.")); - } - } break; - - case S_Op_Multiply: - case S_Op_Divide: - { - S_RuntimeError(Runtime, StrLit("This operation is not supported.")); - } break; - - case S_Op_DefineGlobal: - { - Runtime->IP += 1; - scene_value Value = Runtime->Compiled.Values[S_ReadU32()]; - if(Value.Kind == S_ValueKind_SourceRef) - { - string Name = Substring(Runtime->Compiled.Source, Value.SourceRef); - scene_named_value *NamedValue = S_FindGlobalVariableByName(Runtime, Name); - if(!NamedValue->Initialized) - { - NamedValue->Initialized = true; - scene_value TargetValue = S_PopStackAndImplicitConvert(Runtime); - NamedValue->Value = TargetValue; - } - else - { - S_RuntimeErrorF(Runtime, "Global %S is already defined.", Name); - } - } - else - { - S_RuntimeError(Runtime, StrLit("Incorrect value kind when retrieving global name.")); - } - - } break; - - case S_Op_Jump: - { - Runtime->IP += 1; - scene_value Value = S_ReadValue(); - if(Value.Kind == S_ValueKind_SourceRef) - { - string JumpDest = Substring(Compiled->Source, Value.SourceRef); - scene_proc *Dest = S_FindProcByName(Compiled, JumpDest); - if(Dest) - { - S_SetCurrentProc(Runtime, Dest); - } - 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_value OffsetValue = S_ReadValue(); - 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_value BranchName = S_ReadValue(); - if(BranchName.Kind == S_ValueKind_SourceRef) - { - scene_value Offset = S_ReadValue(); - 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; - - case S_Op_ClearDialog: - { - scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action); - Action->Kind = S_TextboxActionKind_Set; - Action->String = StrLit(""); - QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action); - - Runtime->IP += 1; - } break; - - case S_Op_ShowCharacter: - { - Runtime->IP += 1; - - scene_value State = S_PopStackAndImplicitConvert(Runtime); - scene_value CharacterValue = S_PopStackAndImplicitConvert(Runtime); - - string Character = StrLit(""); - if(CharacterValue.Kind == S_ValueKind_String) - { - Character = CharacterValue.String; - } - else - { - S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving character name."); - } - - // sixten: create & apply character action - scene_character_action *Action = PushStruct(FrameArena, scene_character_action); - Action->Target = Character; - - if(State.Kind == S_ValueKind_Nil) - { - Action->State = CR_State_Invalid; - } - else - { - Action->State = CR_CharacterStateFromString(State.String); - } - DLLInsertLast(Runtime->FirstCharacterAction, Runtime->LastCharacterAction, Action); - } break; - - case S_Op_LineEntry: - { - scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action); - Action->Kind = S_TextboxActionKind_Append; - - Runtime->IP += 1; - - scene_value Value = S_PopStack(Runtime); - if(Value.Kind == S_ValueKind_SourceRef) - { - Action->String = Substring(Compiled->Source, Value.SourceRef); - QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action); - } - else - { - S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving line entry."); - } - - } break; - - default: - { - S_RuntimeErrorF(Runtime, "Unknown bytecode op: 0x%02x", Data[Runtime->IP]); - } break; - } - } - else - { - Result.ReachedAwait = true; - Runtime->CurrentProc = 0; - } - } - else - { - S_RuntimeErrorF(Runtime, "No main entry was found"); - } - - Result.HadError = !DLLIsEmpty(Runtime->Errors.First); - - Runtime->LastResult = Result; - return(Result); + + //- sixten: run the bytecode interpreter + if(Runtime->CurrentProc) + { + if(Runtime->IP < Runtime->CurrentProc->Count) + { + u8 *Data = Runtime->CurrentProc->Data; + + switch(Data[Runtime->IP]) + { + case S_Op_Constant: + { + Runtime->IP += 1; + S_PushStack(Runtime, S_ReadValue()); + } break; + + case S_Op_Pop: + { + Runtime->IP += 1; + S_PopStack(Runtime); + } break; + + case S_Op_Nil: + { + Runtime->IP += 1; + S_PushStack(Runtime, S_MakeNil()); + } break; + + case S_Op_True: + { + Runtime->IP += 1; + S_PushStack(Runtime, S_MakeBoolean(true)); + } break; + + case S_Op_False: + { + Runtime->IP += 1; + S_PushStack(Runtime, S_MakeBoolean(false)); + } break; + + case S_Op_Add: + { + Runtime->IP += 1; + scene_value Value1 = S_PopStackAndImplicitConvert(Runtime); + scene_value Value2 = S_PopStackAndImplicitConvert(Runtime); + if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number) + { + S_PushStack(Runtime, S_MakeNumber(Value1.Number + Value2.Number)); + } + else if(Value1.Kind == S_ValueKind_String && Value2.Kind == S_ValueKind_String) + { + S_RuntimeError(Runtime, StrLit("String concatination is not yet supported.")); + } + else + { + S_RuntimeError(Runtime, StrLit("This operation is not supported.")); + } + } break; + + case S_Op_Subtract: + { + Runtime->IP += 1; + scene_value Value1 = S_PopStack(Runtime); + scene_value Value2 = S_PopStack(Runtime); + if(Value1.Kind == S_ValueKind_Number && Value2.Kind == S_ValueKind_Number) + { + S_PushStack(Runtime, S_MakeNumber(Value1.Number - Value2.Number)); + } + else + { + S_RuntimeError(Runtime, StrLit("This operation is not supported.")); + } + } break; + + case S_Op_Multiply: + case S_Op_Divide: + { + S_RuntimeError(Runtime, StrLit("This operation is not supported.")); + } break; + + case S_Op_DefineGlobal: + { + Runtime->IP += 1; + scene_value Value = Runtime->Compiled.Values[S_ReadU32()]; + if(Value.Kind == S_ValueKind_SourceRef) + { + string Name = Substring(Runtime->Compiled.Source, Value.SourceRef); + scene_named_value *NamedValue = S_FindGlobalVariableByName(Runtime, Name); + if(!NamedValue->Initialized) + { + NamedValue->Initialized = true; + scene_value TargetValue = S_PopStackAndImplicitConvert(Runtime); + NamedValue->Value = TargetValue; + } + else + { + S_RuntimeErrorF(Runtime, "Global %S is already defined.", Name); + } + } + else + { + S_RuntimeError(Runtime, StrLit("Incorrect value kind when retrieving global name.")); + } + + } break; + + case S_Op_Jump: + { + Runtime->IP += 1; + scene_value Value = S_ReadValue(); + if(Value.Kind == S_ValueKind_SourceRef) + { + string JumpDest = Substring(Compiled->Source, Value.SourceRef); + scene_proc *Dest = S_FindProcByName(Compiled, JumpDest); + if(Dest) + { + S_SetCurrentProc(Runtime, Dest); + } + 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_value OffsetValue = S_ReadValue(); + 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_value BranchName = S_ReadValue(); + if(BranchName.Kind == S_ValueKind_SourceRef) + { + scene_value Offset = S_ReadValue(); + 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; + + case S_Op_ClearDialog: + { + scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action); + Action->Kind = S_TextboxActionKind_Set; + Action->String = StrLit(""); + QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action); + + Runtime->IP += 1; + } break; + + case S_Op_ShowCharacter: + { + Runtime->IP += 1; + + scene_value State = S_PopStackAndImplicitConvert(Runtime); + scene_value CharacterValue = S_PopStackAndImplicitConvert(Runtime); + + string Character = StrLit(""); + if(CharacterValue.Kind == S_ValueKind_String) + { + Character = CharacterValue.String; + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving character name."); + } + + // sixten: create & apply character action + scene_character_action *Action = PushStruct(FrameArena, scene_character_action); + Action->Target = Character; + + if(State.Kind == S_ValueKind_Nil) + { + Action->State = CR_State_Invalid; + } + else + { + Action->State = CR_CharacterStateFromString(State.String); + } + DLLInsertLast(Runtime->FirstCharacterAction, Runtime->LastCharacterAction, Action); + } break; + + case S_Op_LineEntry: + { + scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action); + Action->Kind = S_TextboxActionKind_Append; + + Runtime->IP += 1; + + scene_value Value = S_PopStack(Runtime); + if(Value.Kind == S_ValueKind_SourceRef) + { + Action->String = Substring(Compiled->Source, Value.SourceRef); + QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action); + } + else + { + S_RuntimeErrorF(Runtime, "Incorrect value kind when retrieving line entry."); + } + + } break; + + default: + { + S_RuntimeErrorF(Runtime, "Unknown bytecode op: 0x%02x", Data[Runtime->IP]); + } break; + } + } + else + { + Result.ReachedAwait = true; + Runtime->CurrentProc = 0; + } + } + else + { + S_RuntimeErrorF(Runtime, "No main entry was found"); + } + + Result.HadError = !DLLIsEmpty(Runtime->Errors.First); + + Runtime->LastResult = Result; + return(Result); } \ No newline at end of file diff --git a/code/vn_scene.h b/code/vn_scene.h index 7466eca..f266e7c 100644 --- a/code/vn_scene.h +++ b/code/vn_scene.h @@ -14,307 +14,312 @@ //~ sixten: Scene Compilation Types struct scene_compile_error { - scene_compile_error *Next; - string Message; - token Token; + scene_compile_error *Next; + string Message; + token Token; }; struct scene_compile_error_list { - scene_compile_error *First; - scene_compile_error *Last; - s64 Count; + scene_compile_error *First; + scene_compile_error *Last; + s64 Count; }; enum scene_opcode { - S_Op_Invalid = 0, - - S_Op_Constant, - S_Op_Pop, - - S_Op_Nil, - S_Op_True, - S_Op_False, - - S_Op_Add, - S_Op_Subtract, - S_Op_Multiply, - S_Op_Divide, - - S_Op_Equal, - S_Op_Greater, - S_Op_Less, - - S_Op_Negate, - S_Op_Not, - - S_Op_DefineGlobal, - S_Op_GetGlobal, - S_Op_SetGlobal, - - S_Op_Jump, - S_Op_JumpClose, - S_Op_AddBranch, - S_Op_Halt, - - S_Op_AwaitInput, - S_Op_ClearDialog, - S_Op_LineEntry, - S_Op_ShowCharacter, + S_Op_Invalid = 0, + + S_Op_Constant, + S_Op_Pop, + + S_Op_Nil, + S_Op_True, + S_Op_False, + + S_Op_Add, + S_Op_Subtract, + S_Op_Multiply, + S_Op_Divide, + + S_Op_Equal, + S_Op_Greater, + S_Op_Less, + + S_Op_Negate, + S_Op_Not, + + S_Op_DefineGlobal, + S_Op_GetGlobal, + S_Op_SetGlobal, + + S_Op_Jump, + S_Op_JumpClose, + S_Op_AddBranch, + S_Op_Halt, + + S_Op_AwaitInput, + S_Op_ClearDialog, + S_Op_LineEntry, + S_Op_ShowCharacter, }; struct scene_bytecode_chunk { - scene_bytecode_chunk *Next; - string Name; - s64 Count; - u8 Data[4096]; + scene_bytecode_chunk *Next; + string Name; + s64 Count; + u8 Data[4096]; }; struct scene_bytecode_bucket { - scene_bytecode_chunk *First; - scene_bytecode_chunk *Last; - s64 Count; + scene_bytecode_chunk *First; + scene_bytecode_chunk *Last; + s64 Count; }; enum scene_value_kind { - S_ValueKind_Nil = 0, - S_ValueKind_Number, - S_ValueKind_Boolean, - S_ValueKind_Pointer, - S_ValueKind_SourceRef, - S_ValueKind_String, - S_ValueKind_Offset, + S_ValueKind_Nil = 0, + S_ValueKind_Number, + S_ValueKind_Boolean, + S_ValueKind_Pointer, + S_ValueKind_SourceRef, + S_ValueKind_String, + S_ValueKind_Offset, }; struct scene_value { - scene_value_kind Kind; - union - { - r64 Number; - b32 Boolean; - u64 Pointer; - range1_s64 SourceRef; - string String; - s64 Offset; - }; + scene_value_kind Kind; + union + { + r64 Number; + b32 Boolean; + u64 Pointer; + range1_s64 SourceRef; + string String; + s64 Offset; + }; }; struct scene_value_chunk { - scene_value_chunk *Next; - s64 Count; - scene_value Values[512]; + scene_value_chunk *Next; + s64 Count; + scene_value Values[512]; }; enum scene_precedence { - S_Precedence_None, - S_Precedence_Assignment, - S_Precedence_Or, - S_Precedence_And, - S_Precedence_Equality, - S_Precedence_Comparison, - S_Precedence_Term, - S_Precedence_Factor, - S_Precedence_Unary, - S_Precedence_Call, - S_Precedence_Primary, + S_Precedence_None, + S_Precedence_Assignment, + S_Precedence_Or, + S_Precedence_And, + S_Precedence_Equality, + S_Precedence_Comparison, + S_Precedence_Term, + S_Precedence_Factor, + S_Precedence_Unary, + S_Precedence_Call, + S_Precedence_Primary, }; typedef void scene_parse_function(struct scene_compiler *Compiler, b32 CanAssign); struct scene_parse_rule { - scene_parse_function *PrefixRule; - scene_parse_function *InfixRule; - scene_precedence Precedence; + scene_parse_function *PrefixRule; + scene_parse_function *InfixRule; + scene_precedence Precedence; }; enum scene_emission_target_type { - S_EmissionTarget_Raw, - S_EmissionTarget_Named, + S_EmissionTarget_Raw, + S_EmissionTarget_Named, }; struct scene_emission_target { - arena *Arena; - scene_bytecode_bucket *Bucket; - scene_emission_target_type Type; - string Name; + arena *Arena; + scene_bytecode_bucket *Bucket; + scene_emission_target_type Type; + string Name; }; struct scene_branch_case { - scene_branch_case *Next; - token Name; - scene_bytecode_bucket Bucket; - scene_value *OffsetValue; - scene_value *EndOffsetValue; + scene_branch_case *Next; + token Name; + scene_bytecode_bucket Bucket; + scene_value *OffsetValue; + scene_value *EndOffsetValue; }; struct scene_character_action { - scene_character_action *Next; - scene_character_action *Prev; - - string Target; - character_state State; + scene_character_action *Next; + scene_character_action *Prev; + + string Target; + character_state State; }; struct scene_compiler { - arena *Arena; - - b32 InPanicMode; - b32 EncounteredError; - scene_compile_error_list Errors; - - string Text; - token *TokensBegin; - token *TokensEnd; - token *At; - - scene_bytecode_bucket GlobalScope; - scene_bytecode_bucket ProcBuckets[32]; - - scene_emission_target TargetStack[16]; - s32 TargetStackIndex; - - scene_value_chunk *FirstValueChunk; - scene_value_chunk *LastValueChunk; - s64 ValueCount; + arena *Arena; + + b32 InPanicMode; + b32 EncounteredError; + scene_compile_error_list Errors; + + string Text; + token *TokensBegin; + token *TokensEnd; + token *At; + + scene_bytecode_bucket GlobalScope; + scene_bytecode_bucket ProcBuckets[32]; + + scene_emission_target TargetStack[16]; + s32 TargetStackIndex; + + scene_value_chunk *FirstValueChunk; + scene_value_chunk *LastValueChunk; + s64 ValueCount; + + string NavFileName; + asset_id BackgroundTexture; }; //////////////////////////////// //~ sixten: Compiled Scene Types struct scene_proc { - // sixten: scene data - string Name; - u8 *Data; - s64 Count; - - // sixten: hash link - scene_proc *Next; + // sixten: scene data + string Name; + u8 *Data; + s64 Count; + + // sixten: hash link + scene_proc *Next; }; struct scene_proc_bucket { - scene_proc *First; - scene_proc *Last; + scene_proc *First; + scene_proc *Last; }; struct compiled_scene { - scene_proc *GlobalScope; - scene_proc_bucket Buckets[16]; - scene_value *Values; - s64 ValueCount; - string Source; - scene_compile_error_list Errors; - b32 IsValid; + scene_proc *GlobalScope; + scene_proc_bucket Buckets[16]; + scene_value *Values; + s64 ValueCount; + string Source; + string NavFileName; + asset_id BackgroundTexture; + 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; + scene_runtime_error *Next; + scene_runtime_error *Prev; + string Message; }; struct scene_runtime_error_list { - scene_runtime_error *First; - scene_runtime_error *Last; - s64 Count; + scene_runtime_error *First; + scene_runtime_error *Last; + s64 Count; }; struct scene_named_value { - b32 Initialized; - string Name; - scene_value Value; + b32 Initialized; + string Name; + scene_value Value; }; struct scene_named_value_node { - scene_named_value_node *Next; - scene_named_value_node *Prev; - scene_named_value NamedValue; + scene_named_value_node *Next; + scene_named_value_node *Prev; + scene_named_value NamedValue; }; struct scene_named_value_bucket { - s64 Count; - scene_named_value_node *First; - scene_named_value_node *Last; + s64 Count; + scene_named_value_node *First; + scene_named_value_node *Last; }; struct scene_runtime_result { - b32 HadError; - b32 ReachedAwait; + b32 HadError; + b32 ReachedAwait; }; enum scene_textbox_action_kind { - S_TextboxActionKind_Set, - S_TextboxActionKind_Append, + S_TextboxActionKind_Set, + S_TextboxActionKind_Append, }; struct scene_textbox_action { - scene_textbox_action *Next; - scene_textbox_action_kind Kind; - string String; + scene_textbox_action *Next; + scene_textbox_action_kind Kind; + string String; }; struct branch_case { - string Name; - s64 Offset; + string Name; + s64 Offset; }; struct scene_runtime_stack { - scene_value Stack[128]; - s32 Count; + scene_value Stack[128]; + s32 Count; }; struct scene_runtime { - compiled_scene Compiled; - - // sixten: runtime state - arena *RuntimeArena; - scene_proc *CurrentProc; - s64 IP; - scene_runtime_stack Stack; - scene_named_value_bucket GlobalVariableBuckets[16]; - scene_named_value_bucket GlobalVariableFreeList; - - // sixten: errors - arena *ErrorArena; - scene_runtime_error_list Errors; - - // sixten: branches - branch_case Branches[16]; - s64 BranchCount; - - // sixten: result - scene_runtime_result LastResult; - scene_textbox_action *FirstTextboxAction; - scene_textbox_action *LastTextboxAction; - scene_character_action *FirstCharacterAction; - scene_character_action *LastCharacterAction; + compiled_scene Compiled; + + // sixten: runtime state + arena *RuntimeArena; + scene_proc *CurrentProc; + s64 IP; + scene_runtime_stack Stack; + scene_named_value_bucket GlobalVariableBuckets[16]; + scene_named_value_bucket GlobalVariableFreeList; + + // sixten: errors + arena *ErrorArena; + scene_runtime_error_list Errors; + + // sixten: branches + branch_case Branches[16]; + s64 BranchCount; + + // sixten: result + scene_runtime_result LastResult; + scene_textbox_action *FirstTextboxAction; + scene_textbox_action *LastTextboxAction; + scene_character_action *FirstCharacterAction; + scene_character_action *LastCharacterAction; }; //////////////////////////////// @@ -323,57 +328,57 @@ struct scene_runtime //- sixten: value helpers inline scene_value S_MakeNil(void) { - scene_value Result; - Result.Kind = S_ValueKind_Nil; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_Nil; + return(Result); } inline scene_value S_MakeNumber(r64 Value) { - scene_value Result; - Result.Kind = S_ValueKind_Number; - Result.Number = Value; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_Number; + Result.Number = Value; + return(Result); } inline scene_value S_MakeBoolean(b32 Value) { - scene_value Result; - Result.Kind = S_ValueKind_Boolean; - Result.Boolean = Value; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_Boolean; + Result.Boolean = Value; + return(Result); } inline scene_value S_MakePointer(void *Value) { - scene_value Result; - Result.Kind = S_ValueKind_Pointer; - Result.Pointer = PointerToU64(Value); - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_Pointer; + Result.Pointer = PointerToU64(Value); + return(Result); } inline scene_value S_MakeSourceRef(token Token) { - scene_value Result; - Result.Kind = S_ValueKind_SourceRef; - Result.SourceRef = Token.Range; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_SourceRef; + Result.SourceRef = Token.Range; + return(Result); } inline scene_value S_MakeSourceRef(range1_s64 Range) { - scene_value Result; - Result.Kind = S_ValueKind_SourceRef; - Result.SourceRef = Range; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_SourceRef; + Result.SourceRef = Range; + return(Result); } inline scene_value S_MakeOffset(s64 Offset) { - scene_value Result; - Result.Kind = S_ValueKind_Offset; - Result.Offset = Offset; - return(Result); + scene_value Result; + Result.Kind = S_ValueKind_Offset; + Result.Offset = Offset; + return(Result); } //- sixten: error messaging @@ -392,21 +397,21 @@ static scene_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, inline scene_emission_target S_RawEmissionTarget(arena *Arena, scene_bytecode_bucket *Bucket) { - scene_emission_target Target = {}; - Target.Arena = Arena; - Target.Bucket = Bucket; - Target.Type = S_EmissionTarget_Raw; - return(Target); + scene_emission_target Target = {}; + Target.Arena = Arena; + Target.Bucket = Bucket; + Target.Type = S_EmissionTarget_Raw; + return(Target); } inline scene_emission_target S_NamedEmissionTarget(arena *Arena, scene_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); + scene_emission_target Target = {}; + Target.Arena = Arena; + Target.Bucket = Bucket; + Target.Type = S_EmissionTarget_Named; + Target.Name = Name; + return(Target); } //- sixten: parsing helpers @@ -422,6 +427,8 @@ 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_ParseNavFilePath(scene_compiler *Compiler); +static void S_ParseBackgroundAsset(scene_compiler *Compiler); static void S_ParseLineEntry(scene_compiler *Compiler); static void S_ParseJumpStatement(scene_compiler *Compiler); static void S_ParseBranchStatement(scene_compiler *Compiler); diff --git a/code/vn_scene_view.cpp b/code/vn_scene_view.cpp index 1453b5b..19fc7a1 100644 --- a/code/vn_scene_view.cpp +++ b/code/vn_scene_view.cpp @@ -2,803 +2,927 @@ per_thread scene_view *ThreadLocal_SceneView = 0; static void SV_SetState(scene_view *View) { - ThreadLocal_SceneView = View; + ThreadLocal_SceneView = View; } static scene_view *SV_GetState() { - return(ThreadLocal_SceneView); + return(ThreadLocal_SceneView); } static void SV_NewFrame(scene_view *View, platform_event_list *EventList, r32 dtForFrame) { - SV_SetState(View); - View->EventList = EventList; - View->dtForFrame = dtForFrame; + SV_SetState(View); + View->EventList = EventList; + View->dtForFrame = dtForFrame; } static void SV_Reset(void) { - scene_view *SceneView = SV_GetState(); - - ArenaClear(SceneView->Runtime.ErrorArena); - S_ResetRuntime(&SceneView->Runtime); - - // sixten: reset textbox - SceneView->Textbox.String.Count = 0; - - // sixten: reset last talking character - SceneView->LastTalkingCharacter.Count = 0; - SceneView->CharacterIsTalking = false; - - SceneView->OnscreenCharacterCount = 0; + scene_view *SceneView = SV_GetState(); + + ArenaClear(SceneView->Runtime.ErrorArena); + S_ResetRuntime(&SceneView->Runtime); + + // sixten: reset textbox + SceneView->Textbox.String.Count = 0; + + // sixten: reset onscreen characters + SceneView->CharacterCount = 0; +} + +static void SV_LoadNavItems(void) +{ + scene_view *SceneView = SV_GetState(); + temp Scratch = GetScratch(); + string NavData = Platform_ReadEntireFile(Scratch.Arena, SceneView->Runtime.Compiled.NavFileName); + + if(NavData.Count != 0) + { + u8 *Byte = NavData.Data; + SceneView->NavItemCount = *(u16 *)Byte; + Byte += 2; + + SceneView->NavItems = PushArrayNoClear(SceneView->SceneArena, scene_nav_item, SceneView->NavItemCount); + + //- sixten: parse items + for(u64 ItemIndex = 0; ItemIndex < SceneView->NavItemCount; ItemIndex += 1) + { + scene_nav_item *Item = &SceneView->NavItems[ItemIndex]; + for(;;) + { + switch(*Byte++) + { + case S_NavItemOp_TextureID: + { + string AssetName; + AssetName.Count = *(u16 *)Byte; + Byte += sizeof(u16); + AssetName.Data = Byte; + Byte += AssetName.Count; + + Item->TextureID = AssetID_Error; + for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1) + { + if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName)) + { + Item->TextureID = AssetIndex; + break; + } + } + } goto Next; + case S_NavItemOp_Scale: + { + Item->Scale = *(r32 *)Byte; + Byte += sizeof(r32); + } goto Next; + case S_NavItemOp_Origin: + { + Item->Origin = *(v2_r32 *)Byte; + Byte += sizeof(v2_r32); + } goto Next; + case S_NavItemOp_P: + { + Item->P = *(v2_r32 *)Byte; + Byte += sizeof(v2_r32); + } goto Next; + case S_NavItemOp_HoverText: + { + Item->HoverText.Count = *(u16 *)Byte; + Byte += 2; + + Item->HoverText.Data = PushArrayNoClear(SceneView->SceneArena, u8, Item->HoverText.Count); + Copy(Item->HoverText.Data, Byte, Item->HoverText.Count); + Byte += Item->HoverText.Count; + } goto Next; + case S_NavItemOp_Action: + { + Item->Action.Kind = (scene_nav_action_kind)*Byte; + Byte += 1; + + Item->Action.Content.Count = *(u16 *)Byte; + Byte += 2; + + Item->Action.Content.Data = PushArrayNoClear(SceneView->SceneArena, u8, Item->Action.Content.Count); + Copy(Item->Action.Content.Data, Byte, Item->Action.Content.Count); + Byte += Item->Action.Content.Count; + } goto Next; + } + + //- sixten: no op found, assume item done + break; + + Next:; + } + } + } + ReleaseScratch(Scratch); } static void SV_SetCurrentSource(compiled_scene *Compiled) { - scene_view *SceneView = SV_GetState(); - scene_runtime *Runtime = &SceneView->Runtime; - - // sixten(TODO): extract runtime information required to seamlessly transition between compilations - SV_Reset(); - - ArenaClear(SceneView->SceneArena); - Runtime->Compiled = S_CopyCompiledScene(SceneView->SceneArena, Compiled); - - //- sixten: run top level - Runtime->CurrentProc = Runtime->Compiled.GlobalScope; - if(Runtime->CurrentProc) - { - temporary_memory Scratch = GetScratch(0, 0); - for(;;) - { - scene_runtime_result RunResult = S_Run(Runtime, Scratch.Arena, false); - if(RunResult.ReachedAwait || RunResult.HadError) - { - break; - } - } - ReleaseScratch(Scratch); - } - - //- sixten: find main proc - Runtime->CurrentProc = S_FindProcByName(&Runtime->Compiled, StrLit("main")); - Runtime->IP = 0; + scene_view *SceneView = SV_GetState(); + scene_runtime *Runtime = &SceneView->Runtime; + + // sixten(TODO): extract runtime information required to seamlessly transition between compilations + SV_Reset(); + + ArenaClear(SceneView->SceneArena); + Runtime->Compiled = S_CopyCompiledScene(SceneView->SceneArena, Compiled); + + SV_LoadNavItems(); + + + //- sixten: run top level + Runtime->CurrentProc = Runtime->Compiled.GlobalScope; + if(Runtime->CurrentProc) + { + temp Scratch = GetScratch(0, 0); + for(;;) + { + scene_runtime_result RunResult = S_Run(Runtime, Scratch.Arena, false); + if(RunResult.ReachedAwait || RunResult.HadError) + { + break; + } + } + ReleaseScratch(Scratch); + } + + //- sixten: find main proc + Runtime->CurrentProc = S_FindProcByName(&Runtime->Compiled, StrLit("main")); + Runtime->IP = 0; } static void SV_Init(scene_view *SceneView, arena *TextboxArena) { - SV_SetState(SceneView); - - SceneView->SceneArena = ArenaAlloc(Kilobytes(16), true); - SceneView->Runtime.ErrorArena = ArenaAlloc(Kilobytes(4), true); - - SceneView->Textbox.Capacity = 4096; - SceneView->Textbox.String.Data = PushArray(TextboxArena, u8, SceneView->Textbox.Capacity); - - SV_Reset(); + SV_SetState(SceneView); + + SceneView->SceneArena = ArenaAlloc(Kilobytes(16), true); + SceneView->Runtime.ErrorArena = ArenaAlloc(Kilobytes(4), true); + + SceneView->Textbox.Capacity = 4096; + SceneView->Textbox.String.Data = PushArray(TextboxArena, u8, SceneView->Textbox.Capacity); + + SV_Reset(); } static b32 SV_CurrentlyInProc(void) { - scene_view *SceneView = SV_GetState(); - b32 Result = (SceneView->Runtime.CurrentProc != 0); - return(Result); + scene_view *SceneView = SV_GetState(); + b32 Result = (SceneView->Runtime.CurrentProc != 0); + return(Result); +} + +static scene_view_character_data *SV_FindTalkingCharacter(void) +{ + scene_view *SceneView = SV_GetState(); + scene_view_character_data *Result = 0; + for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Character = SceneView->Characters+CharacterIndex; + if(Character->Talking) + { + Result = Character; + break; + } + } + return(Result); +} + +static string SV_DisplayNameFromCharacter(scene_view_character_data *Character) +{ + scene_view *SceneView = SV_GetState(); + string Result = StrLit("invalid character"); + if(Character) + { + Result = StrLit("undefined name"); + + scene_named_value *CharacterNameValue = S_FindGlobalVariableByName(&SceneView->Runtime, Character->Name, false); + if(CharacterNameValue && CharacterNameValue->Value.Kind == S_ValueKind_String) + { + Result = CharacterNameValue->Value.String; + } + } + return(Result); } struct text_properties { - font_id Font; - r32 FontSize; - r32 LineHeight; + 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); - v2_r32 ShadowOffset = V2R32(Properties.FontSize*0.1, Properties.FontSize*0.1); - - 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; - } - - PushText(Group, Atlas, Properties.Font, P+Offset+ShadowOffset, Properties.FontSize, Color_Black, Word); - 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; - } - } + v2_r32 Offset = V2R32(0, 0); + v2_r32 ShadowOffset = V2R32(Properties.FontSize*0.1, Properties.FontSize*0.1); + + 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; + } + + PushText(Group, Atlas, Properties.Font, P+Offset+ShadowOffset, Properties.FontSize, Color_Black, Word); + 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 CalculateGlobalScaleFromDim(v2_r32 Dim) { - r32 GlobalScale = SquareRoot(Dim.x*Dim.y)/45; - return(GlobalScale); + r32 GlobalScale = SquareRoot(Dim.x*Dim.y)/45; + return(GlobalScale); } static r32 CalculateGlobalScaleFromRootBox(ui_box *Box) { - v2 RenderDim = DimOfRange(Box->Rect); - r32 GlobalScale = SquareRoot(RenderDim.x*RenderDim.y)/45; - return(GlobalScale); + v2 RenderDim = UI_CalculateBoxDim(Box); + r32 GlobalScale = SquareRoot(RenderDim.x*RenderDim.y)/45; + return(GlobalScale); } struct scene_textbox_data { - textbox *Textbox; - ui_box *SceneViewBox; - string Name; - r32 NameT; + textbox *Textbox; + ui_box *SceneViewBox; + string Name; + r32 NameT; }; UI_CUSTOM_DRAW_CALLBACK(BuildSceneTextboxDrawCallback) { - scene_textbox_data *TextboxData = (scene_textbox_data *)Data; - textbox *Textbox = TextboxData->Textbox; - - r32 GlobalScale = CalculateGlobalScaleFromRootBox(TextboxData->SceneViewBox); - - //- sixten: render textbox - { - v4 TopColor = V4(0, 0, 0, 0.8f); - v4 BottomColor = V4(0, 0, 0, 0.5f); - range2_r32 Dest = Pad(Range2R32(Box->Rect.Min, Box->Rect.Max), V2(GlobalScale, GlobalScale)*2.0f); - PushQuad(Group, Dest, TopColor, TopColor, BottomColor, BottomColor, 0, GlobalScale, 0); - } - - //- sixten: render text - { - string Text = Textbox->String; - r32 CharsRevealedT = Textbox->CharsRevealedT; - text_properties Properties = {}; - Properties.Font = Font_Fancy; - Properties.FontSize = GlobalScale; - Properties.LineHeight = GlobalScale*1.5f; - r32 Padding = 1.5f*GlobalScale; - v2 Offset = V2R32(Padding, Padding); - RenderAnimatedText(Group, Atlas, Properties, Text, CharsRevealedT, Box->Rect.Min+Offset, DimOfRange(Box->Rect).x-2*Padding); - } - - //- sixten: render character name - { - string Name = TextboxData->Name; - r32 T = TextboxData->NameT; - v2 TextP = Box->Rect.Min + V2(1.5f*GlobalScale, -GlobalScale*T); - PushText(Group, Atlas, Font_Fancy, TextP+V2(GlobalScale*0.1, GlobalScale*0.1), GlobalScale, SetAlpha(Color_Black, T), Name); - PushText(Group, Atlas, Font_Fancy, TextP, GlobalScale, SetAlpha(Color_White, T), Name); - } - + scene_textbox_data *TextboxData = (scene_textbox_data *)Data; + textbox *Textbox = TextboxData->Textbox; + + r32 GlobalScale = CalculateGlobalScaleFromRootBox(TextboxData->SceneViewBox); + + //- sixten: render textbox + { + v4 TopColor = V4(0, 0, 0, 0.8f); + v4 BottomColor = V4(0, 0, 0, 0.5f); + range2_r32 Dest = Pad(Range2R32(Box->Rect.Min, Box->Rect.Max), V2(GlobalScale, GlobalScale)*2.0f); + PushQuad(Group, Dest, TopColor, TopColor, BottomColor, BottomColor, 0, GlobalScale, 0); + } + + //- sixten: render text + { + string Text = Textbox->String; + r32 CharsRevealedT = Textbox->CharsRevealedT; + text_properties Properties = {}; + Properties.Font = Font_Fancy; + Properties.FontSize = GlobalScale; + Properties.LineHeight = GlobalScale*1.5f; + r32 Padding = 1.5f*GlobalScale; + v2 Offset = V2R32(Padding, Padding); + RenderAnimatedText(Group, Atlas, Properties, Text, CharsRevealedT, Box->Rect.Min+Offset, DimOfRange(Box->Rect).x-2*Padding); + } + + //- sixten: render character name + { + string Name = TextboxData->Name; + r32 T = TextboxData->NameT; + v2 TextP = Box->Rect.Min + V2(1.5f*GlobalScale, -GlobalScale*T); + PushText(Group, Atlas, Font_Fancy, TextP+V2(GlobalScale*0.1, GlobalScale*0.1), GlobalScale, SetAlpha(Color_Black, T), Name); + PushText(Group, Atlas, Font_Fancy, TextP, GlobalScale, SetAlpha(Color_White, T), Name); + } + } static void SV_DrawBackground(scene_view *SceneView, ui_box *Box, render_group *Group) { - v2 RenderDim = DimOfRange(Box->Rect); - - //- 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); + + render_handle Texture = TextureFromAssetID(SceneView->Runtime.Compiled.BackgroundTexture); + + //- 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. #if 0 - persist r32 Time = 0; - Time += 1 / 1200.0f; - r32 r = 30; - v2_r32 Offset = V2(Sin(Time)+0.5*Sin(43+2.43*Time)+Sin(424+Time*16)*0.1, Sin(8+Time)+0.5*Sin(43+2.43*Time)+Sin(4242+Time*16)*0.1)*(1.0f/1.6f)*r; - range2_r32 BackgroundDest = Range2R32(Box->Rect.Min-V2(r, r)+Offset, RenderDim+Box->Rect.Min+V2(r, r)+Offset); - range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(SceneView->BackgroundTexture))); - PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, SceneView->BackgroundTexture); + persist r32 Time = 0; + Time += 1 / 1200.0f; + r32 r = 30; + v2_r32 Offset = V2(Sin(Time)+0.5*Sin(43+2.43*Time)+Sin(424+Time*16)*0.1, Sin(8+Time)+0.5*Sin(43+2.43*Time)+Sin(4242+Time*16)*0.1)*(1.0f/1.6f)*r; + range2_r32 BackgroundDest = Range2R32(Box->Rect.Min-V2(r, r)+Offset, RenderDim+Box->Rect.Min+V2(r, r)+Offset); + range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(SceneView->BackgroundTexture))); + PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, Texture); #else - range2_r32 BackgroundDest = Range2R32(Box->Rect.Min, RenderDim+Box->Rect.Min); - range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(SceneView->BackgroundTexture))); - PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, SceneView->BackgroundTexture); + range2_r32 BackgroundDest = Range2R32(Box->Rect.Min, RenderDim+Box->Rect.Min); + range2_r32 BackgroundSource = Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(SceneView->BackgroundTexture))); + PushTexturedQuad(Group, BackgroundDest, BackgroundSource, Color_White, Color_White, Color_White, Color_White, 0, 0, 0, Texture); #endif - + } UI_CUSTOM_DRAW_CALLBACK(BuildSceneDrawCallback) { - scene_view *SceneView = (scene_view *)Data; - - SV_DrawBackground(SceneView, Box, Group); - v2 RenderDim = DimOfRange(Box->Rect); - r32 GlobalScale = CalculateGlobalScaleFromDim(RenderDim); - - //- sixten: render characters - for(s32 CharacterIndex = 0; CharacterIndex < SceneView->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Character = SceneView->OnscreenCharacters + CharacterIndex; - - v4_r32 BlendColor = LinearBlend(Color_White, Color_Black, 0.5-Character->TalkingT*0.5); - BlendColor.a = Character->ActiveT; - r32 Scale = (Character->Info.Scale + Character->TalkingT*0.001)*(0.95+Character->ActiveT*0.05)*GlobalScale; - render_handle CharacterHandle = Character->Info.Texture; - v2_r32 CharacterDim = ConvertV2ToR32(DimFromTexture(CharacterHandle)); - v2_r32 CharacterOriginP = V2R32(RenderDim.x*Character->PctP, RenderDim.y); - v2_r32 CharacterMidP = Box->Rect.Min+V2R32(CharacterOriginP.x, CharacterOriginP.y - CharacterDim.y*Scale/2); - range2_r32 CharacterDest = Range2R32(CharacterMidP-CharacterDim*0.5f*Scale, CharacterMidP+CharacterDim*0.5f*Scale); - range2_r32 CharacterSource = Range2R32(V2R32(0, 0), CharacterDim); - PushTexturedQuad(Group, CharacterDest, CharacterSource, BlendColor, BlendColor, BlendColor, BlendColor, 0, 0, 0, CharacterHandle); - } + scene_view *SceneView = (scene_view *)Data; + + SV_DrawBackground(SceneView, Box, Group); + v2 RenderDim = DimOfRange(Box->Rect); + r32 GlobalScale = CalculateGlobalScaleFromDim(RenderDim); + + //- sixten: render characters + for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Character = SceneView->Characters + CharacterIndex; + + v4_r32 BlendColor = LinearBlend(Color_White, Color_Black, 0.5-Character->TalkingT*0.5); + BlendColor.a = Character->ActiveT; + r32 Scale = (Character->Info.Scale + Character->TalkingT*0.001)*(0.95+Character->ActiveT*0.05)*GlobalScale; + render_handle CharacterHandle = Character->Info.Texture; + v2_r32 CharacterDim = ConvertV2ToR32(DimFromTexture(CharacterHandle)); + v2_r32 CharacterOriginP = V2R32(RenderDim.x*Character->PctP, RenderDim.y); + v2_r32 CharacterMidP = Box->Rect.Min+V2R32(CharacterOriginP.x, CharacterOriginP.y - CharacterDim.y*Scale/2); + range2_r32 CharacterDest = Range2R32(CharacterMidP-CharacterDim*0.5f*Scale, CharacterMidP+CharacterDim*0.5f*Scale); + range2_r32 CharacterSource = Range2R32(V2R32(0, 0), CharacterDim); + PushTexturedQuad(Group, CharacterDest, CharacterSource, BlendColor, BlendColor, BlendColor, BlendColor, 0, 0, 0, CharacterHandle); + } } static b32 BuildSceneBranchButton(string Text, r32 GlobalScale) { - UI_SetNextFontSize(GlobalScale); - UI_SetNextFont(Font_Fancy); - UI_SetNextCornerRadius(5.0f); - - b32 Result = UI_Button(Text).Clicked; - return(Result); + UI_SetNextFontSize(GlobalScale); + UI_SetNextFont(Font_Fancy); + UI_SetNextCornerRadius(5.0f); + + b32 Result = UI_Button(Text).Clicked; + return(Result); } -static void BuildProcView(scene_view *View, ui_box *Box) +static void BuildProcView(scene_view *View, ui_box *Box, v2_r32 BoxDim) { - temporary_memory Scratch = GetScratch(); - - r32 GlobalScale = CalculateGlobalScaleFromRootBox(Box); - r32 ActiveScale = GlobalScale * 0.75f; - - scene_runtime *Runtime = &View->Runtime; - textbox *Textbox = &View->Textbox; - - //- sixten: build branches - UI_FillPadding UI_WidthFill UI_Height(UI_Pixels(2*ActiveScale, 1)) 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(BuildSceneBranchButton(PushFormat(Scratch.Arena, "%S#%i", Branch->Name, BranchIndex), ActiveScale)) - { - Offset = Branch->Offset; - FoundOffset = true; - } - - if(BranchIndex != Runtime->BranchCount - 1) - { - UI_Spacer(UI_Em(1, 1)); - } - } - if(FoundOffset) - { - Runtime->IP += 1+Offset; - Runtime->BranchCount = 0; - } - } - - //- sixten: build textbox - UI_Size(UI_Percent(1, 1), 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; - TextboxData->Name = View->LastTalkingCharacter; - TextboxData->NameT = View->CharacterIsTalkingT; - UI_EquipBoxCustomDrawCallback(TextBox, BuildSceneTextboxDrawCallback, TextboxData); - } - - ReleaseScratch(Scratch); + temp Scratch = GetScratch(); + + r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); + r32 ActiveScale = GlobalScale * 0.75f; + + scene_runtime *Runtime = &View->Runtime; + textbox *Textbox = &View->Textbox; + + //- sixten: build branches + UI_FillPadding UI_WidthFill UI_Height(UI_Pixels(2*ActiveScale, 1)) 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(BuildSceneBranchButton(PushFormat(Scratch.Arena, "%S#%i", Branch->Name, BranchIndex), ActiveScale)) + { + Offset = Branch->Offset; + FoundOffset = true; + } + + if(BranchIndex != Runtime->BranchCount - 1) + { + UI_Spacer(UI_Em(1, 1)); + } + } + if(FoundOffset) + { + Runtime->IP += 1+Offset; + Runtime->BranchCount = 0; + } + } + + //- sixten: build textbox + UI_Size(UI_Percent(1, 1), 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; + + scene_view_character_data *TalkingCharacter = SV_FindTalkingCharacter(); + TextboxData->Name = SV_DisplayNameFromCharacter(TalkingCharacter); + TextboxData->NameT = AC_AnimateValueF(TalkingCharacter != 0, 0, 0.3f, "Scene View Talking Character %p", View); + + UI_EquipBoxCustomDrawCallback(TextBox, BuildSceneTextboxDrawCallback, TextboxData); + } + + ReleaseScratch(Scratch); } struct scene_nav_item_info { - scene_nav_item *Item; - ui_signal Signal; + scene_nav_item *Item; + ui_signal Signal; }; UI_CUSTOM_DRAW_CALLBACK(BuildNavItemDrawCallback) { - scene_nav_item_info *Info = (scene_nav_item_info *)Data; - render_handle Texture = TextureFromAssetID(Info->Item->TextureID); - v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); - - range2_r32 DestRect = Range2R32(Box->Rect.Min, Box->Rect.Max); - range2_r32 SourceRect = Range2R32(V2R32(0, 0), TextureDim); - - v4_r32 Color = LinearBlend(Color_Grey, Color_White, Info->Signal.Hovering); - - PushTexturedQuad(Group, DestRect, SourceRect, Color, Color, Color, Color, 0, 0, 0, Texture); + scene_nav_item_info *Info = (scene_nav_item_info *)Data; + render_handle Texture = TextureFromAssetID(Info->Item->TextureID); + v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); + + range2_r32 DestRect = Range2R32(Box->Rect.Min, Box->Rect.Max); + range2_r32 SourceRect = Range2R32(V2R32(0, 0), TextureDim); + + v4_r32 Color = LinearBlend(Color_Grey, Color_White, Info->Signal.Hovering); + + PushTexturedQuad(Group, DestRect, SourceRect, Color, Color, Color, Color, 0, 0, 0, Texture); } inline u32 U32FromRawR32(r32 Value) { - u32 Result = *(u32 *)&Value; - return(Result); + u32 Result = *(u32 *)&Value; + return(Result); } static ui_signal SV_BuildNavItem(scene_nav_item *Item, r32 GlobalScale, v2_r32 GlobalDim) { - r32 AppliedScale = GlobalScale*Item->Scale; - - scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info); - Data->Item = Item; - - render_handle Texture = TextureFromAssetID(Item->TextureID); - - v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); - v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin); - - v2_r32 OffsetP = GlobalDim*(V2R32(1, 1) + Item->P)*0.5f; - v2_r32 OriginP = TextureOrigin*AppliedScale; - v2_r32 Dim = TextureDim*AppliedScale; - range2_r32 Dest = Range2R32(OffsetP-OriginP, OffsetP-OriginP+Dim); - - UI_SetNextFixedP(Dest.Min); - UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1)); - UI_SetNextHoverCursor(PlatformCursor_Hand); - - ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, - "View Item Box %i %S %p", - (U32FromRawR32(Item->Scale)<<4)+ - (U32FromRawR32(Item->Origin.x)<<17)+ - (U32FromRawR32(Item->Origin.y)<<11), - Item->HoverText, Item); - UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data); - - ui_signal Signal = UI_SignalFromBox(ItemBox); - Data->Signal = Signal; - - return(Signal); + r32 AppliedScale = GlobalScale*Item->Scale; + + scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info); + Data->Item = Item; + + render_handle Texture = TextureFromAssetID(Item->TextureID); + + v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); + v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin); + + v2_r32 OffsetP = GlobalDim*(V2R32(1, 1) + Item->P)*0.5f; + v2_r32 OriginP = TextureOrigin*AppliedScale; + v2_r32 Dim = TextureDim*AppliedScale; + range2_r32 Dest = Range2R32(OffsetP-OriginP, OffsetP-OriginP+Dim); + + UI_SetNextFixedP(Dest.Min); + UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1)); + UI_SetNextHoverCursor(PlatformCursor_Hand); + + ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, + "View Item Box %i %S %p", + (U32FromRawR32(Item->Scale)<<4)+ + (U32FromRawR32(Item->Origin.x)<<17)+ + (U32FromRawR32(Item->Origin.y)<<11), + Item->HoverText, Item); + UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data); + + ui_signal Signal = UI_SignalFromBox(ItemBox); + Data->Signal = Signal; + + return(Signal); } static ui_signal BuildNavItemAndLabel(scene_nav_item *Item, r32 GlobalScale, v2_r32 GlobalDim) { - ui_signal Signal = SV_BuildNavItem(Item, GlobalScale, GlobalDim); - if(Signal.Hovering) - { - UI_FontSize(GlobalScale*0.5f) UI_Font(Font_Fancy) UI_CornerRadius(4.0f) - UI_TooltipLabel(Item->HoverText, UI_MouseP()); - } - return(Signal); + ui_signal Signal = SV_BuildNavItem(Item, GlobalScale, GlobalDim); + if(Item->HoverText.Count && Signal.Hovering) + { + UI_FontSize(GlobalScale*0.5f) UI_Font(Font_Fancy) UI_CornerRadius(4.0f) + UI_TooltipLabel(Item->HoverText, UI_MouseP()); + } + return(Signal); } -scene_nav_item Items[] = +static void BuildNavView(scene_view *View, ui_box *Box, v2_r32 BoxDim) { - { - AssetID_ArthurHappy, - 0.01f, - V2R32(0.5f, 1.0f), - V2R32(0.15f, 0.6f), - StrLit("Talk to Arthur"), - { - S_NavAction_Proc, - StrLit("arthur_talk"), - } - }, - { - AssetID_MonikaLeaning, - 0.02f, - V2R32(0.5f, 1.0f), - V2R32(-0.4f, 0.6f), - StrLit("Talk to Monika"), - { - S_NavAction_Scene, - StrLit("ddlc"), - }, - }, -}; - -static void BuildNavView(scene_view *View, ui_box *Box) -{ - r32 GlobalScale = CalculateGlobalScaleFromRootBox(Box); - - for(int ItemIndex = 0; ItemIndex < ArrayCount(Items); ++ItemIndex) - { - scene_nav_item *Item = Items+ItemIndex; - if(BuildNavItemAndLabel(Item, GlobalScale, DimOfRange(Box->Rect)).Clicked) - { - // sixten: apply the action - if(Item->Action.Kind == S_NavAction_Proc) - { - S_SetCurrentProc(&View->Runtime, S_FindProcByName(&View->Runtime.Compiled, Item->Action.Content)); - } - else if(Item->Action.Kind == S_NavAction_Scene) - { - temporary_memory Scratch = GetScratch(); - string Filepath = PushFormat(Scratch.Arena, "data/%S.vns", Item->Action.Content);; - - string SceneInput = Platform_ReadEntireFile(Scratch.Arena, Filepath); - compiled_scene Scene = S_ScriptFromText(Scratch.Arena, SceneInput); - SV_SetCurrentSource(&Scene); - - ReleaseScratch(Scratch); - } - } - } + r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); + + for(int ItemIndex = 0; ItemIndex < View->NavItemCount; ++ItemIndex) + { + scene_nav_item *Item = View->NavItems+ItemIndex; + if(BuildNavItemAndLabel(Item, GlobalScale, BoxDim).Clicked) + { + // sixten: apply the action + if(Item->Action.Kind == S_NavAction_Proc) + { + S_SetCurrentProc(&View->Runtime, S_FindProcByName(&View->Runtime.Compiled, Item->Action.Content)); + } + else if(Item->Action.Kind == S_NavAction_Scene) + { + temp Scratch = GetScratch(); + string Filepath = PushFormat(Scratch.Arena, "data/%S.vns", Item->Action.Content);; + + string SceneInput = Platform_ReadEntireFile(Scratch.Arena, Filepath); + compiled_scene Scene = S_ScriptFromText(Scratch.Arena, SceneInput); + SV_SetCurrentSource(&Scene); + + ReleaseScratch(Scratch); + } + } + } } static void BuildScene(scene_view *View) { - 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")); - UI_EquipBoxCustomDrawCallback(Box, BuildSceneDrawCallback, View); - - UI_Parent(Box) - { - if(SV_CurrentlyInProc()) - { - BuildProcView(View, Box); - } - else - { - BuildNavView(View, Box); - } - } + v2_r32 ParentDim = UI_CalculateBoxDim(UI_TopParent()); + + r32 TargetRatio = 16.0f/9.0f; + r32 ActualRatio = ParentDim.x/ParentDim.y; + + v2_r32 BoxDim = ParentDim; + + if(ActualRatio>TargetRatio) + { + BoxDim.x = BoxDim.y*TargetRatio; + } + else + { + BoxDim.y = BoxDim.x/TargetRatio; + } + + UI_SetNextWidth(UI_Pixels(BoxDim.x, 1)); + UI_SetNextHeight(UI_Pixels(BoxDim.y, 1)); + UI_SetNextLayoutAxis(Axis2_Y); + UI_SetNextFixedP((ParentDim-BoxDim)*0.5f); + ui_box *Box = UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, StrLit("Scene View")); + UI_EquipBoxCustomDrawCallback(Box, BuildSceneDrawCallback, View); + + UI_Parent(Box) + { + if(SV_CurrentlyInProc()) + { + BuildProcView(View, Box, BoxDim); + } + else + { + BuildNavView(View, Box, BoxDim); + } + } } 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->Errors.First; 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) - { - ZeroStruct(&Runtime->Errors); - 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) - { - SV_Reset(); - } - - UI_Spacer(UI_Em(1, 1)); - if(UI_ButtonF("Exit Program").Clicked) - { - Input->ExitRequested = true; - } - } - } - } + 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->Errors.First; 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) + { + ZeroStruct(&Runtime->Errors); + 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) + { + SV_Reset(); + } + + UI_Spacer(UI_Em(1, 1)); + if(UI_ButtonF("Exit Program").Clicked) + { + Input->ExitRequested = true; + } + } + } + } } static scene_view_character_data *SV_CharacterDataFromName(string Name) { - scene_view_character_data *Result = 0; - - scene_view *View = SV_GetState(); - - for(s32 CharacterIndex = 0; CharacterIndex < View->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Character = View->OnscreenCharacters + CharacterIndex; - if(AreEqual(Character->Name, Name)) - { - Result = Character; - break; - } - } - - //- sixten: create character if not initialized - if(!Result && View->OnscreenCharacterCount < ArrayCount(View->OnscreenCharacters)) - { - s32 CharacterIndex = View->OnscreenCharacterCount; - View->OnscreenCharacterCount += 1; - - Result = View->OnscreenCharacters + CharacterIndex; - *Result = {}; - Result->Name = Name; - Result->Active = true; - Result->PctP = (r32)(CharacterIndex + 1) / (View->OnscreenCharacterCount + 1); - } - - return(Result); + scene_view_character_data *Result = 0; + + scene_view *View = SV_GetState(); + + for(s32 CharacterIndex = 0; CharacterIndex < View->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Character = View->Characters + CharacterIndex; + if(AreEqual(Character->Name, Name)) + { + Result = Character; + break; + } + } + + //- sixten: create character if not initialized + if(!Result && View->CharacterCount < ArrayCount(View->Characters)) + { + s32 CharacterIndex = View->CharacterCount; + View->CharacterCount += 1; + + Result = View->Characters + CharacterIndex; + *Result = {}; + Result->Name = Name; + Result->Active = true; + Result->PctP = (r32)(CharacterIndex + 1) / (View->CharacterCount + 1); + } + + return(Result); } static scene_view_character_texture_info SV_CharacterTextureFromAction(scene_character_action *Action) { - scene_view_character_texture_info Result = {}; - Result.Texture = EmptyRenderHandle(); - Result.Scale = 1.0f; - - scene_view *View = SV_GetState(); - - if(AreEqual(StrLit("arthur"), Action->Target)) - { - switch(Action->State) - { - case CR_State_Normal: { Result.Texture = View->TestNormal; } break; - case CR_State_Happy: { Result.Texture = View->TestHappy; } break; - default: break; - } - - Result.Scale = 0.017f; - } - else if(AreEqual(StrLit("monika"), Action->Target)) - { - switch(Action->State) - { - case CR_State_Leaning: { Result.Texture = View->MonikaLeaning; } break; - default: break; - } - Result.Scale = 0.033f; - } - return(Result); + scene_view_character_texture_info Result = {}; + Result.Texture = EmptyRenderHandle(); + Result.Scale = 1.0f; + + scene_view *View = SV_GetState(); + + if(AreEqual(StrLit("arthur"), Action->Target)) + { + switch(Action->State) + { + case CR_State_Normal: { Result.Texture = View->TestNormal; } break; + case CR_State_Happy: { Result.Texture = View->TestHappy; } break; + default: break; + } + + Result.Scale = 0.017f; + } + else if(AreEqual(StrLit("monika"), Action->Target)) + { + switch(Action->State) + { + case CR_State_Leaning: { Result.Texture = View->MonikaLeaning; } break; + default: break; + } + Result.Scale = 0.033f; + } + return(Result); } static r32 SV_CalculateTargetPctP(s32 TrueCharacterIndex) { - scene_view *View = SV_GetState(); - s32 CharacterCount = 0; - s32 AssumedCharacterIndex = 0; - for(s32 CharacterIndex = 0; CharacterIndex < View->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Data = View->OnscreenCharacters + CharacterIndex; - if(Data->Active) - { - CharacterCount += 1; - if(CharacterIndex < TrueCharacterIndex) - { - AssumedCharacterIndex += 1; - } - } - } - r32 Result = (r32)(AssumedCharacterIndex + 1) / (Max(CharacterCount, 1) + 1); - return(Result); + scene_view *View = SV_GetState(); + s32 CharacterCount = 0; + s32 AssumedCharacterIndex = 0; + for(s32 CharacterIndex = 0; CharacterIndex < View->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Data = View->Characters + CharacterIndex; + if(Data->Active) + { + CharacterCount += 1; + if(CharacterIndex < TrueCharacterIndex) + { + AssumedCharacterIndex += 1; + } + } + } + r32 Result = (r32)(AssumedCharacterIndex + 1) / (Max(CharacterCount, 1) + 1); + return(Result); } static void SV_UpdateInDialog(arena *FrameArena) { - scene_view *SceneView = SV_GetState(); - textbox *Textbox = &SceneView->Textbox; - scene_runtime *Runtime = &SceneView->Runtime; - platform_event_list *EventList = SceneView->EventList; - r32 dtForFrame = SceneView->dtForFrame; - compiled_scene *Compiled = &Runtime->Compiled; - - if(Compiled && Compiled->IsValid) - { - b32 PlayerAction = (Platform_KeyPress(EventList, Key_Space)||Platform_KeyPress(EventList, Key_MouseLeft)); - - if(!Runtime->LastResult.HadError) - { - 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 = 25.0f; - Textbox->CharsRevealed += dtForFrame*CharsPerSecond; - Textbox->CharsRevealed = Min(Textbox->CharsRevealed, (r32)Textbox->String.Count); - - if(Textbox->CharsRevealed < Textbox->String.Count && PlayerAction) - { - Textbox->CharsRevealed = Textbox->String.Count; - } - - AC_AnimateValueDirect(Textbox->CharsRevealed, 0.05f, &Textbox->CharsRevealedT); - - //- sixten: apply the textbox actions - for(scene_textbox_action *Action = Runtime->FirstTextboxAction; Action != 0; Action = Action->Next) - { - if(Action->Kind == S_TextboxActionKind_Set) - { - string ReplaceString = Action->String; - Textbox->String.Count = Min(ReplaceString.Count, Textbox->Capacity); - Copy(Textbox->String.Data, ReplaceString.Data, Textbox->String.Count); - Textbox->CharsRevealedT = 0; - Textbox->CharsRevealed = 0; - } - else if(Action->Kind == S_TextboxActionKind_Append) - { - string Addend = Action->String; - Textbox->CharsRevealedT = 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; - - //- sixten: make all characters non-talking - if(Runtime->FirstCharacterAction != 0) - { - for(s32 CharacterIndex = 0; CharacterIndex < SceneView->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Data = SceneView->OnscreenCharacters + CharacterIndex; - Data->Talking = false; - } - - SceneView->CharacterIsTalking = false; - } - - //- sixten: apply character actions - for(scene_character_action *Action = Runtime->FirstCharacterAction; Action != 0; Action = Action->Next) - { - // sixten: find character - scene_view_character_data *Data = SV_CharacterDataFromName(Action->Target); - - if(Action->State == CR_State_None) - { - Data->Active = false; - } - else - { - if(Action->State != CR_State_Invalid) - { - Data->Info = SV_CharacterTextureFromAction(Action);; - } - Data->Talking = true; - - SceneView->CharacterIsTalking = true; - - string CharacterName = Action->Target; - scene_named_value *CharacterNameValue = S_FindGlobalVariableByName(Runtime, CharacterName, false); - if(CharacterNameValue && CharacterNameValue->Value.Kind == S_ValueKind_String) - { - CharacterName = CharacterNameValue->Value.String; - } - - SceneView->LastTalkingCharacter = MakeString(SceneView->LastTalkingCharacterBuffer, CharacterName.Count); - Copy(SceneView->LastTalkingCharacterBuffer, CharacterName.Data, CharacterName.Count); - } - } - - Runtime->FirstCharacterAction = Runtime->LastCharacterAction = 0; - } + scene_view *SceneView = SV_GetState(); + textbox *Textbox = &SceneView->Textbox; + scene_runtime *Runtime = &SceneView->Runtime; + platform_event_list *EventList = SceneView->EventList; + r32 dtForFrame = SceneView->dtForFrame; + compiled_scene *Compiled = &Runtime->Compiled; + + if(Compiled && Compiled->IsValid) + { + //- sixten: gather user input + b32 MousePressed = false; + for(platform_event *Event = EventList->First; Event != 0; Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press && Event->Key == Key_MouseLeft) + { + MousePressed = true; + } + } + + b32 PlayerAction = (Platform_KeyPress(EventList, Key_Space)||MousePressed); + + //- sixten: run the scene + if(!Runtime->LastResult.HadError) + { + b32 AdvanceOnAwait = (Textbox->CharsRevealed >= Textbox->String.Count) && PlayerAction; + + for(;;) + { + scene_runtime_result RunResult = S_Run(Runtime, FrameArena, AdvanceOnAwait); + if(RunResult.ReachedAwait || RunResult.HadError) + { + break; + } + } + } + + //- sixten: advance textbox + { + r32 CharsPerSecond = 25.0f; + Textbox->CharsRevealed += dtForFrame*CharsPerSecond; + Textbox->CharsRevealed = Min(Textbox->CharsRevealed, (r32)Textbox->String.Count); + + if(Textbox->CharsRevealed < Textbox->String.Count && PlayerAction) + { + Textbox->CharsRevealed = Textbox->String.Count; + } + + AC_AnimateValueDirect(Textbox->CharsRevealed, 0.05f, &Textbox->CharsRevealedT); + } + + //- sixten: apply the textbox actions + for(scene_textbox_action *Action = Runtime->FirstTextboxAction; Action != 0; Action = Action->Next) + { + if(Action->Kind == S_TextboxActionKind_Set) + { + string ReplaceString = Action->String; + Textbox->String.Count = Min(ReplaceString.Count, Textbox->Capacity); + Copy(Textbox->String.Data, ReplaceString.Data, Textbox->String.Count); + Textbox->CharsRevealedT = 0; + Textbox->CharsRevealed = 0; + } + else if(Action->Kind == S_TextboxActionKind_Append) + { + string Addend = Action->String; + Textbox->CharsRevealedT = 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; + + // sixten: update character state only if there has been a change + if(Runtime->FirstCharacterAction != 0) + { + //- sixten: make all characters non-talking + for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Data = SceneView->Characters + CharacterIndex; + Data->Talking = false; + } + + //- sixten: apply character actions + for(scene_character_action *Action = Runtime->FirstCharacterAction; Action != 0; Action = Action->Next) + { + // sixten: find character + scene_view_character_data *Data = SV_CharacterDataFromName(Action->Target); + + if(Action->State == CR_State_None) + { + Data->Active = false; + } + else + { + if(Action->State != CR_State_Invalid) + { + Data->Info = SV_CharacterTextureFromAction(Action); + } + Data->Talking = true; + + string CharacterName = Action->Target; + scene_named_value *CharacterNameValue = S_FindGlobalVariableByName(Runtime, CharacterName, false); + if(CharacterNameValue && CharacterNameValue->Value.Kind == S_ValueKind_String) + { + CharacterName = CharacterNameValue->Value.String; + } + } + } + } + + Runtime->FirstCharacterAction = Runtime->LastCharacterAction = 0; + } } static void SV_Update(arena *FrameArena) { - scene_view *SceneView = SV_GetState(); - - //- sixten: update the characters - { - AC_AnimateValueDirect(SceneView->CharacterIsTalking, 0.3f, &SceneView->CharacterIsTalkingT); - - for(s32 CharacterIndex = 0; CharacterIndex < SceneView->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Data = SceneView->OnscreenCharacters + CharacterIndex; - - if(!SV_CurrentlyInProc()) - { - Data->Active = false; - Data->Talking = false; - } - - AC_AnimateValueDirect(Data->Active, 0.5f, &Data->ActiveT); - AC_AnimateValueDirect(Data->Talking, 0.4f, &Data->TalkingT); - - r32 TargetPctP; - if(Data->Active) - { - TargetPctP = SV_CalculateTargetPctP(CharacterIndex); - } - else - { - TargetPctP = (r32)(CharacterIndex+1)/(SceneView->OnscreenCharacterCount+1); - } - AC_AnimateValueDirect(TargetPctP, 0.4f, &Data->PctP); - } - } - - //- sixten: prune any unactive characters - for(s32 CharacterIndex = 0; CharacterIndex < SceneView->OnscreenCharacterCount; CharacterIndex += 1) - { - scene_view_character_data *Data = SceneView->OnscreenCharacters + CharacterIndex; - if(!Data->Active && Data->ActiveT < 0.01) - { - Move(Data, Data+1, (SceneView->OnscreenCharacterCount-CharacterIndex-1)*sizeof(scene_view_character_data)); - SceneView->OnscreenCharacterCount -= 1; - CharacterIndex -= 1; - } - } - - //- sixten: update scene - if(SV_CurrentlyInProc()) - { - SV_UpdateInDialog(FrameArena); - } - else - { - - } + scene_view *SceneView = SV_GetState(); + + //- sixten: update the characters + { + for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Data = SceneView->Characters + CharacterIndex; + + if(!SV_CurrentlyInProc()) + { + Data->Active = false; + Data->Talking = false; + } + + AC_AnimateValueDirect(Data->Active, 0.5f, &Data->ActiveT); + AC_AnimateValueDirect(Data->Talking, 0.4f, &Data->TalkingT); + + r32 TargetPctP; + if(Data->Active) + { + TargetPctP = SV_CalculateTargetPctP(CharacterIndex); + } + else + { + TargetPctP = (r32)(CharacterIndex+1)/(SceneView->CharacterCount+1); + } + AC_AnimateValueDirect(TargetPctP, 0.4f, &Data->PctP); + } + } + + //- sixten: prune any unactive characters + for(s32 CharacterIndex = 0; CharacterIndex < SceneView->CharacterCount; CharacterIndex += 1) + { + scene_view_character_data *Data = SceneView->Characters + CharacterIndex; + if(!Data->Active && Data->ActiveT < 0.01) + { + Move(Data, Data+1, (SceneView->CharacterCount-CharacterIndex-1)*sizeof(scene_view_character_data)); + SceneView->CharacterCount -= 1; + CharacterIndex -= 1; + } + } + + //- sixten: update scene + if(SV_CurrentlyInProc()) + { + SV_UpdateInDialog(FrameArena); + } + else + { + + } } 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); - } - else - { - UI_LabelF("Invalid source"); - } + 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); + } + 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 index 7c64db7..c5cdcf7 100644 --- a/code/vn_scene_view.h +++ b/code/vn_scene_view.h @@ -5,100 +5,101 @@ struct textbox { - string String; - s64 Capacity; - r32 CharsRevealedT; - r32 CharsRevealed; + string String; + s64 Capacity; + r32 CharsRevealedT; + r32 CharsRevealed; }; struct scene_view_character_texture_info { - render_handle Texture; - r32 Scale; + render_handle Texture; + r32 Scale; }; struct scene_view_character_data { - string Name; - - scene_view_character_texture_info Info; - - b32 Active; - r32 ActiveT; - - b32 Talking; - r32 TalkingT; - - r32 PctP; + string Name; + + scene_view_character_texture_info Info; + + b32 Active; + r32 ActiveT; + + b32 Talking; + r32 TalkingT; + + r32 PctP; }; enum scene_nav_action_kind { - S_NavAction_None, - S_NavAction_Proc, - S_NavAction_Scene, + S_NavAction_None, + S_NavAction_Proc, + S_NavAction_Scene, }; struct scene_nav_action { - scene_nav_action_kind Kind; - string Content; + scene_nav_action_kind Kind; + string Content; }; struct scene_nav_item { - asset_id TextureID; - r32 Scale; - v2_r32 Origin; - v2_r32 P; - string HoverText; - scene_nav_action Action; + asset_id TextureID; + r32 Scale; + v2_r32 Origin; + v2_r32 P; + string HoverText; + scene_nav_action Action; }; -global read_only scene_nav_item G_DefaultSceneNavItem = +typedef u8 scene_nav_item_op; +enum { - AssetID_ArthurNormal, - 0.01f, - V2R32(0.5f, 1.0f), - V2R32(0.0f, 0.0f), - StrLit(""), - S_NavAction_None + S_NavItemOp_None = 0, + S_NavItemOp_TextureID, + S_NavItemOp_Scale, + S_NavItemOp_Origin, + S_NavItemOp_P, + S_NavItemOp_HoverText, + S_NavItemOp_Action, }; struct scene_nav { - scene_nav_item *Items; - s32 ItemCount; - s32 ItemMax; + scene_nav_item *Items; + s32 ItemCount; + s32 ItemMax; }; struct scene_view { - arena *SceneArena; - - //- sixten: state - scene_runtime Runtime; - textbox Textbox; - - u8 LastTalkingCharacterBuffer[32]; - string LastTalkingCharacter; - - b32 CharacterIsTalking; - r32 CharacterIsTalkingT; - - s32 OnscreenCharacterCount; - scene_view_character_data OnscreenCharacters[16]; - - //- sixten: input per frame - platform_event_list *EventList; - r32 dtForFrame; - - //- sixten: temporary texture hub - render_handle BackgroundTexture; - render_handle TestHappy; - render_handle TestNormal; - render_handle MonikaLeaning; - + arena *SceneArena; + + //- sixten: state + scene_runtime Runtime; + textbox Textbox; + + //- sixten: characters + s32 CharacterCount; + scene_view_character_data Characters[16]; + + //- sixten: nav items + scene_nav_item *NavItems; + u64 NavItemCount; + + //- sixten: input per frame + platform_event_list *EventList; + r32 dtForFrame; + + //- sixten: temporary texture hub + render_handle BackgroundTexture; + render_handle TestHappy; + render_handle TestNormal; + render_handle MonikaLeaning; + }; static void SV_SetState(scene_view *View); @@ -111,6 +112,9 @@ static void SV_Init(scene_view *View, arena *TextboxArena); static b32 SV_CurrentlyInProc(void); +static scene_view_character_data *SV_FindTalkingCharacter(void); +static string SV_DisplayNameFromCharacter(scene_view_character_data *Character); + static void SV_DrawBackground(scene_view *SceneView, ui_box *Box, render_group *Group); static ui_signal SV_BuildNavItem(scene_nav_item * Item, r32 GlobalScale, v2_r32 GlobalDim); diff --git a/code/vn_thread_context.h b/code/vn_thread_context.h index c968672..e91055f 100644 --- a/code/vn_thread_context.h +++ b/code/vn_thread_context.h @@ -7,55 +7,55 @@ struct thread_context { - memory_arena Arenas[2]; + memory_arena Arenas[2]; }; per_thread thread_context *ThreadLocal_ThreadContext = 0; inline void SetThreadContext(thread_context *Context) { - ThreadLocal_ThreadContext = Context; + ThreadLocal_ThreadContext = Context; } inline thread_context *GetThreadContext(void) { - return(ThreadLocal_ThreadContext); + return(ThreadLocal_ThreadContext); } static temporary_memory GetScratch(memory_arena **Conflicts = 0, u64 ConflictCount = 0) { - temporary_memory Scratch = {}; - thread_context *Context = GetThreadContext(); - - for(u64 ArenaIndex = 0; - ArenaIndex < ArrayCount(Context->Arenas); - ++ArenaIndex) - { - b32 FoundConflict = false; - for(u64 ConflictIndex = 0; - ConflictIndex < ConflictCount; - ++ConflictIndex) - { - memory_arena *Conflict = Conflicts[ConflictIndex]; - if(Conflict == Context->Arenas + ArenaIndex) - { - FoundConflict = true; - break; - } - } - - if(!FoundConflict) - { - Scratch = BeginTemporaryMemory(Context->Arenas + ArenaIndex); - break; - } - } - - Assert(Scratch.Arena); - - return(Scratch); + temporary_memory Scratch = {}; + thread_context *Context = GetThreadContext(); + + for(u64 ArenaIndex = 0; + ArenaIndex < ArrayCount(Context->Arenas); + ++ArenaIndex) + { + b32 FoundConflict = false; + for(u64 ConflictIndex = 0; + ConflictIndex < ConflictCount; + ++ConflictIndex) + { + memory_arena *Conflict = Conflicts[ConflictIndex]; + if(Conflict == Context->Arenas + ArenaIndex) + { + FoundConflict = true; + break; + } + } + + if(!FoundConflict) + { + Scratch = BeginTemporaryMemory(Context->Arenas + ArenaIndex); + break; + } + } + + Assert(Scratch.Arena); + + return(Scratch); } -#define ReleaseScratch(Scratch) EndTemporaryMemory(Scratch) +#define ReleaseScratch(Scratch) EndTemp(Scratch) #endif //VN_THREAD_CONTEXT_H diff --git a/code/vn_tokenizer.cpp b/code/vn_tokenizer.cpp index 72420c5..b654755 100644 --- a/code/vn_tokenizer.cpp +++ b/code/vn_tokenizer.cpp @@ -3,38 +3,38 @@ static string T_StringFromToken(string Text, token Token) { - string Result = Substring(Text, Token.Range); - return(Result); + string Result = Substring(Text, Token.Range); + return(Result); } static void T_TokenChunkListPush(arena *Arena, token_chunk_list *List, token Token, s64 MaxTokenCountPerNode) { - token_chunk_node *Node = List->Last; - if(!Node || Node->Count >= Node->MaxCount) - { - Node = PushStruct(Arena, token_chunk_node); - Node->Count = 0; - Node->MaxCount = MaxTokenCountPerNode; - Node->Tokens = PushArrayNoClear(Arena, token, Node->MaxCount); - QueuePush(List->First, List->Last, Node); - } - Node->Tokens[Node->Count] = Token; - Node->Count += 1; - List->Count += 1; + token_chunk_node *Node = List->Last; + if(!Node || Node->Count >= Node->MaxCount) + { + Node = PushStruct(Arena, token_chunk_node); + Node->Count = 0; + Node->MaxCount = MaxTokenCountPerNode; + Node->Tokens = PushArrayNoClear(Arena, token, Node->MaxCount); + QueuePush(List->First, List->Last, Node); + } + Node->Tokens[Node->Count] = Token; + Node->Count += 1; + List->Count += 1; } static token_array T_TokenArrayFromList(arena *Arena, token_chunk_list *List) { - token_array Result = {}; - Result.Tokens = PushArrayNoClear(Arena, token, List->Count); - Result.Count = List->Count; - s64 Index = 0; - for(token_chunk_node *Node = List->First; Node != 0; Node = Node->Next) - { - Copy(Result.Tokens + Index, Node->Tokens, sizeof(token)*Node->Count); - Index += Node->Count; - } - return(Result); + token_array Result = {}; + Result.Tokens = PushArrayNoClear(Arena, token, List->Count); + Result.Count = List->Count; + s64 Index = 0; + for(token_chunk_node *Node = List->First; Node != 0; Node = Node->Next) + { + Copy(Result.Tokens + Index, Node->Tokens, sizeof(token)*Node->Count); + Index += Node->Count; + } + return(Result); } //////////////////////////////// @@ -42,11 +42,11 @@ static token_array T_TokenArrayFromList(arena *Arena, token_chunk_list *List) static void T_MessageListPush(arena *Arena, tokenizer_message_list *List, tokenizer_message_kind Kind, s64 Offset, string String) { - tokenizer_message *Message = PushStructNoClear(Arena, tokenizer_message); - Message->Kind = Kind; - Message->Offset = Offset; - Message->String = String; - QueuePush(List->First, List->Last, Message); + tokenizer_message *Message = PushStructNoClear(Arena, tokenizer_message); + Message->Kind = Kind; + Message->Offset = Offset; + Message->String = String; + QueuePush(List->First, List->Last, Message); } //////////////////////////////// @@ -54,262 +54,263 @@ static void T_MessageListPush(arena *Arena, tokenizer_message_list *List, tokeni static tokenize_result T_TokenizeFromText(arena *Arena, string Text, tokenizer_filter_function *ExcludeFilter) { - temporary_memory Scratch = GetScratch(&Arena, 1); - token_chunk_list Tokens = {}; - tokenizer_message_list Messages = {}; - u8 *TextStart = Text.Data; - u8 *TextEnd = TextStart + Text.Count; - u8 *Byte = TextStart; - s64 Line = 0; - - //- sixten: scan string & produce tokens - for(;Byte < TextEnd;) - { - token_kind TokenKind = TokenKind_None; - u8 *TokenStart = 0; - u8 *TokenEnd = 0; - - //- sixten: whitespace - if(TokenKind == TokenKind_None && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) - { - TokenKind = TokenKind_Whitespace; - TokenStart = Byte; - TokenEnd = Byte; - Byte += 1; - for(;Byte <= TextEnd; Byte += 1) - { - TokenEnd += 1; - if(Byte == TextEnd || !(*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) - { - break; - } - } - } - - //- sixten: newlines - if(TokenKind == TokenKind_None && *Byte == '\n') - { - TokenKind = TokenKind_Newline; - TokenStart = Byte; - TokenEnd = Byte + 1; - Line += 1; - Byte += 1; - } - - //- sixten: single-line comments - if(TokenKind == TokenKind_None && (Byte[0] == '/' && Byte[1] == '/')) - { - TokenKind = TokenKind_Comment;; - TokenStart = Byte; - TokenEnd = Byte + 2; - Byte += 2; - for(;Byte <= TextEnd; Byte += 1) - { - if(Byte == TextEnd || *Byte == '\n' || *Byte == '\r') - { - break; - } - TokenEnd += 1; - } - } - - //- sixten: multi-line comments - if(TokenKind == TokenKind_None && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*')) - { - TokenKind = TokenKind_Comment; - TokenStart = Byte; - TokenEnd = Byte + 2; - Byte += 2; - for(;Byte <= TextEnd; Byte += 1) - { - // sixten(NOTE): This could potentially be wrong. The TokenEnd += 1 statement could currently make the token include the EOF. - if(Byte == TextEnd) - { - TokenKind = TokenKind_BrokenComment; - break; - } - TokenEnd += 1; - if(Byte+1 < TextEnd && Byte[0] == '*' && Byte[1] == '/') - { - TokenEnd += 1; - Byte += 2; - break; - } - } - } - - //- sixten: identifiers - if(TokenKind == TokenKind_None && (('A' <= *Byte && *Byte <= 'Z') || - ('a' <= *Byte && *Byte <= 'z') || - (UTF8Lengths[*Byte>>3] > 1) || - *Byte == '_')) - { - TokenKind = TokenKind_Identifier; - TokenStart = Byte; - TokenEnd = Byte; - Byte += UTF8Lengths[*Byte>>3]; - for(;Byte <= TextEnd; Byte += UTF8Lengths[*Byte>>3]) - { - if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') || - ('a' <= *Byte && *Byte <= 'z') || - ('0' <= *Byte && *Byte <= '9') || - (UTF8Lengths[*Byte>>3] > 1) || - *Byte == '_')) - { - TokenEnd = Byte; - break; - } - } - - string String = MakeString(TokenStart, TokenEnd-TokenStart); - if(0) {} - else if(AreEqual(String, StrLit("and"))) { TokenKind = TokenKind_And; } - else if(AreEqual(String, StrLit("branch"))) { TokenKind = TokenKind_Branch; } - else if(AreEqual(String, StrLit("else"))) { TokenKind = TokenKind_Else; } - else if(AreEqual(String, StrLit("false"))) { TokenKind = TokenKind_False; } - else if(AreEqual(String, StrLit("for"))) { TokenKind = TokenKind_For; } - else if(AreEqual(String, StrLit("if"))) { TokenKind = TokenKind_If; } - else if(AreEqual(String, StrLit("jump"))) { TokenKind = TokenKind_Jump; } - else if(AreEqual(String, StrLit("or"))) { TokenKind = TokenKind_Or; } - else if(AreEqual(String, StrLit("proc"))) { TokenKind = TokenKind_Proc; } - else if(AreEqual(String, StrLit("true"))) { TokenKind = TokenKind_True; } - else if(AreEqual(String, StrLit("var"))) { TokenKind = TokenKind_Var; } - else if(AreEqual(String, StrLit("while"))) { TokenKind = TokenKind_While; } - else if(AreEqual(String, StrLit("define"))) { TokenKind = TokenKind_Define; } - } - - //- sixten: numerics - if(TokenKind == TokenKind_None && (('0' <= *Byte && *Byte <= '9') || - (*Byte == '-' && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9'))) - { - TokenKind = TokenKind_Numeric; - TokenStart = Byte; - TokenEnd = Byte; - Byte += 1; - for(;Byte <= TextEnd; Byte += 1) - { - TokenEnd += 1; - if(Byte == TextEnd || - !(('0' <= *Byte && *Byte <= '9') || - *Byte == '_' || *Byte == '.')) - { - break; - } - } - } - - //- sixten: string literals - if(TokenKind == TokenKind_None && *Byte == '"') - { - TokenKind = TokenKind_StringLiteral; - TokenStart = Byte; - TokenEnd = Byte; - Byte += 1; - for(;Byte <= TextEnd; Byte += 1) - { - TokenEnd += 1; - if(Byte == TextEnd || *Byte == '\n') - { - TokenKind = TokenKind_BrokenStringLiteral; - break; - } - if(*Byte == '"') - { - Byte += 1; - TokenEnd += 1; - break; - } - } - } - - //- sixten: symbols - if(TokenKind == TokenKind_None && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' || - *Byte == ',' || *Byte == '.' || *Byte == '@' || *Byte == '#' || - *Byte == ';' || *Byte == '+' || *Byte == '-' || *Byte == '*' || - *Byte == '/')) - { - TokenStart = Byte; - TokenEnd = Byte+1; - - switch(*Byte) - { - case '{': { TokenKind = TokenKind_CurlyOpen; } break; - case '}': { TokenKind = TokenKind_CurlyClose; } break; - case '(': { TokenKind = TokenKind_ParenthesisOpen; } break; - case ')': { TokenKind = TokenKind_ParenthesisClose; } break; - case ',': { TokenKind = TokenKind_Comma; } break; - case '.': { TokenKind = TokenKind_Dot; } break; - case '@': { TokenKind = TokenKind_At; } break; - case '#': { TokenKind = TokenKind_PoundSign; } break; - case ';': { TokenKind = TokenKind_Semicolon; } break; - case '+': { TokenKind = TokenKind_Plus; } break; - case '-': { TokenKind = TokenKind_Minus; } break; - case '*': { TokenKind = TokenKind_Star; } break; - case '/': { TokenKind = TokenKind_Slash; } break; - InvalidDefaultCase; - } - - Byte += 1; - } - if(TokenKind == TokenKind_None && (*Byte == '!' || *Byte == '=' || *Byte == '>' || *Byte == '<')) - { - TokenStart = Byte; - TokenEnd = Byte+1; - - switch(*Byte) - { - case '!': { TokenKind = TokenKind_Bang; } break; - case '=': { TokenKind = TokenKind_Equal; } break; - case '>': { TokenKind = TokenKind_Greater; } break; - case '<': { TokenKind = TokenKind_Less; } break; - InvalidDefaultCase; - } - - Byte += 1; - - if(Byte < TextEnd && (*Byte == '=')) - { - TokenKind = (token_kind)(TokenKind + 1); - TokenEnd += 1; - Byte += 1; - } - } - - //- sixten: bad character - if(TokenKind == TokenKind_None) - { - TokenKind = TokenKind_BadCharacter; - TokenStart = Byte; - TokenEnd = Byte + 1; - Byte += 1; - } - - //- sixten: push token - if(TokenKind != 0 && (!ExcludeFilter || (ExcludeFilter && !ExcludeFilter(TokenKind))) && TokenStart != 0 && TokenEnd > TokenStart) - { - token Token = {TokenKind, {TokenStart - TextStart, TokenEnd - TextStart}, Line}; - T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096); - } - - if(TokenKind == TokenKind_BrokenComment) - { - string Message = StrLit("broken comment"); - T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); - } - - if(TokenKind == TokenKind_BrokenStringLiteral) - { - string Message = StrLit("broken string literal"); - T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); - } - } - - tokenize_result Result = - { - T_TokenArrayFromList(Arena, &Tokens), - Messages - }; - ReleaseScratch(Scratch); - return(Result); + temp Scratch = GetScratch(&Arena, 1); + token_chunk_list Tokens = {}; + tokenizer_message_list Messages = {}; + u8 *TextStart = Text.Data; + u8 *TextEnd = TextStart + Text.Count; + u8 *Byte = TextStart; + s64 Line = 0; + + //- sixten: scan string & produce tokens + for(;Byte < TextEnd;) + { + token_kind TokenKind = TokenKind_None; + u8 *TokenStart = 0; + u8 *TokenEnd = 0; + + //- sixten: whitespace + if(TokenKind == TokenKind_None && (*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) + { + TokenKind = TokenKind_Whitespace; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || !(*Byte == ' ' || *Byte == '\t' || *Byte == '\v' || *Byte == '\r')) + { + break; + } + } + } + + //- sixten: newlines + if(TokenKind == TokenKind_None && *Byte == '\n') + { + TokenKind = TokenKind_Newline; + TokenStart = Byte; + TokenEnd = Byte + 1; + Line += 1; + Byte += 1; + } + + //- sixten: single-line comments + if(TokenKind == TokenKind_None && (Byte[0] == '/' && Byte[1] == '/')) + { + TokenKind = TokenKind_Comment;; + TokenStart = Byte; + TokenEnd = Byte + 2; + Byte += 2; + for(;Byte <= TextEnd; Byte += 1) + { + if(Byte == TextEnd || *Byte == '\n' || *Byte == '\r') + { + break; + } + TokenEnd += 1; + } + } + + //- sixten: multi-line comments + if(TokenKind == TokenKind_None && (Byte+1 < TextEnd && Byte[0] == '/' && Byte[1] == '*')) + { + TokenKind = TokenKind_Comment; + TokenStart = Byte; + TokenEnd = Byte + 2; + Byte += 2; + for(;Byte <= TextEnd; Byte += 1) + { + // sixten(NOTE): This could potentially be wrong. The TokenEnd += 1 statement could currently make the token include the EOF. + if(Byte == TextEnd) + { + TokenKind = TokenKind_BrokenComment; + break; + } + TokenEnd += 1; + if(Byte+1 < TextEnd && Byte[0] == '*' && Byte[1] == '/') + { + TokenEnd += 1; + Byte += 2; + break; + } + } + } + + //- sixten: identifiers + if(TokenKind == TokenKind_None && (('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + (UTF8Lengths[*Byte>>3] > 1) || + *Byte == '_')) + { + TokenKind = TokenKind_Identifier; + TokenStart = Byte; + TokenEnd = Byte; + Byte += UTF8Lengths[*Byte>>3]; + for(;Byte <= TextEnd; Byte += UTF8Lengths[*Byte>>3]) + { + if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') || + ('a' <= *Byte && *Byte <= 'z') || + ('0' <= *Byte && *Byte <= '9') || + (UTF8Lengths[*Byte>>3] > 1) || + *Byte == '_')) + { + TokenEnd = Byte; + break; + } + } + + string String = MakeString(TokenStart, TokenEnd-TokenStart); + if(0) {} + else if(AreEqual(String, StrLit("and"))) { TokenKind = TokenKind_And; } + else if(AreEqual(String, StrLit("branch"))) { TokenKind = TokenKind_Branch; } + else if(AreEqual(String, StrLit("else"))) { TokenKind = TokenKind_Else; } + else if(AreEqual(String, StrLit("false"))) { TokenKind = TokenKind_False; } + else if(AreEqual(String, StrLit("for"))) { TokenKind = TokenKind_For; } + else if(AreEqual(String, StrLit("if"))) { TokenKind = TokenKind_If; } + else if(AreEqual(String, StrLit("jump"))) { TokenKind = TokenKind_Jump; } + else if(AreEqual(String, StrLit("or"))) { TokenKind = TokenKind_Or; } + else if(AreEqual(String, StrLit("proc"))) { TokenKind = TokenKind_Proc; } + else if(AreEqual(String, StrLit("true"))) { TokenKind = TokenKind_True; } + else if(AreEqual(String, StrLit("var"))) { TokenKind = TokenKind_Var; } + else if(AreEqual(String, StrLit("while"))) { TokenKind = TokenKind_While; } + else if(AreEqual(String, StrLit("nav"))) { TokenKind = TokenKind_Nav; } + else if(AreEqual(String, StrLit("background"))) { TokenKind = TokenKind_Background; } + } + + //- sixten: numerics + if(TokenKind == TokenKind_None && (('0' <= *Byte && *Byte <= '9') || + (*Byte == '-' && Byte + 1 < TextEnd && '0' <= Byte[1] && Byte[1] <= '9'))) + { + TokenKind = TokenKind_Numeric; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || + !(('0' <= *Byte && *Byte <= '9') || + *Byte == '_' || *Byte == '.')) + { + break; + } + } + } + + //- sixten: string literals + if(TokenKind == TokenKind_None && *Byte == '"') + { + TokenKind = TokenKind_StringLiteral; + TokenStart = Byte; + TokenEnd = Byte; + Byte += 1; + for(;Byte <= TextEnd; Byte += 1) + { + TokenEnd += 1; + if(Byte == TextEnd || *Byte == '\n') + { + TokenKind = TokenKind_BrokenStringLiteral; + break; + } + if(*Byte == '"') + { + Byte += 1; + TokenEnd += 1; + break; + } + } + } + + //- sixten: symbols + if(TokenKind == TokenKind_None && (*Byte == '{' || *Byte == '}' || *Byte == '(' || *Byte == ')' || + *Byte == ',' || *Byte == '.' || *Byte == '@' || *Byte == '#' || + *Byte == ';' || *Byte == '+' || *Byte == '-' || *Byte == '*' || + *Byte == '/')) + { + TokenStart = Byte; + TokenEnd = Byte+1; + + switch(*Byte) + { + case '{': { TokenKind = TokenKind_CurlyOpen; } break; + case '}': { TokenKind = TokenKind_CurlyClose; } break; + case '(': { TokenKind = TokenKind_ParenthesisOpen; } break; + case ')': { TokenKind = TokenKind_ParenthesisClose; } break; + case ',': { TokenKind = TokenKind_Comma; } break; + case '.': { TokenKind = TokenKind_Dot; } break; + case '@': { TokenKind = TokenKind_At; } break; + case '#': { TokenKind = TokenKind_PoundSign; } break; + case ';': { TokenKind = TokenKind_Semicolon; } break; + case '+': { TokenKind = TokenKind_Plus; } break; + case '-': { TokenKind = TokenKind_Minus; } break; + case '*': { TokenKind = TokenKind_Star; } break; + case '/': { TokenKind = TokenKind_Slash; } break; + InvalidDefaultCase; + } + + Byte += 1; + } + if(TokenKind == TokenKind_None && (*Byte == '!' || *Byte == '=' || *Byte == '>' || *Byte == '<')) + { + TokenStart = Byte; + TokenEnd = Byte+1; + + switch(*Byte) + { + case '!': { TokenKind = TokenKind_Bang; } break; + case '=': { TokenKind = TokenKind_Equal; } break; + case '>': { TokenKind = TokenKind_Greater; } break; + case '<': { TokenKind = TokenKind_Less; } break; + InvalidDefaultCase; + } + + Byte += 1; + + if(Byte < TextEnd && (*Byte == '=')) + { + TokenKind = (token_kind)(TokenKind + 1); + TokenEnd += 1; + Byte += 1; + } + } + + //- sixten: bad character + if(TokenKind == TokenKind_None) + { + TokenKind = TokenKind_BadCharacter; + TokenStart = Byte; + TokenEnd = Byte + 1; + Byte += 1; + } + + //- sixten: push token + if(TokenKind != 0 && (!ExcludeFilter || (ExcludeFilter && !ExcludeFilter(TokenKind))) && TokenStart != 0 && TokenEnd > TokenStart) + { + token Token = {TokenKind, {TokenStart - TextStart, TokenEnd - TextStart}, Line}; + T_TokenChunkListPush(Scratch.Arena, &Tokens, Token, 4096); + } + + if(TokenKind == TokenKind_BrokenComment) + { + string Message = StrLit("broken comment"); + T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); + } + + if(TokenKind == TokenKind_BrokenStringLiteral) + { + string Message = StrLit("broken string literal"); + T_MessageListPush(Arena, &Messages, T_MessageKind_Error, TokenStart - TextStart, Message); + } + } + + tokenize_result Result = + { + T_TokenArrayFromList(Arena, &Tokens), + Messages + }; + ReleaseScratch(Scratch); + return(Result); } diff --git a/code/vn_tokenizer.h b/code/vn_tokenizer.h index b103463..f20dab3 100644 --- a/code/vn_tokenizer.h +++ b/code/vn_tokenizer.h @@ -7,129 +7,130 @@ //~ sixten: Token Types enum token_kind { - TokenKind_None, - - // sixten: labels - TokenKind_Identifier, - TokenKind_Numeric, - TokenKind_StringLiteral, - - // sixten: symbols - TokenKind_SymbolsBegin, - // sixten: (single char) - TokenKind_CurlyOpen, - TokenKind_CurlyClose, - TokenKind_ParenthesisOpen, - TokenKind_ParenthesisClose, - TokenKind_Comma, - TokenKind_Dot, - TokenKind_At, - TokenKind_PoundSign, - TokenKind_Semicolon, - TokenKind_Plus, - TokenKind_Minus, - TokenKind_Star, - TokenKind_Slash, - // sixten: (one or two chars) - TokenKind_Bang, - TokenKind_BangEqual, - TokenKind_Equal, - TokenKind_EqualEqual, - TokenKind_Greater, - TokenKind_GreaterEqual, - TokenKind_Less, - TokenKind_LessEqual, - TokenKind_SymbolsEnd, - - // sixten: keywords - TokenKind_KeywordsBegin, - TokenKind_And, - TokenKind_Branch, - TokenKind_Else, - TokenKind_False, - TokenKind_For, - TokenKind_If, - TokenKind_Jump, - TokenKind_Or, - TokenKind_Proc, - TokenKind_True, - TokenKind_Var, - TokenKind_While, - TokenKind_Define, - TokenKind_KeywordsEnd, - - // sixten: whitespace - TokenKind_Whitespace, - TokenKind_Newline, - TokenKind_Comment, - - // sixten: invalid kinds - TokenKind_BrokenComment, - TokenKind_BrokenStringLiteral, - TokenKind_BadCharacter, + TokenKind_None, + + // sixten: labels + TokenKind_Identifier, + TokenKind_Numeric, + TokenKind_StringLiteral, + + // sixten: symbols + TokenKind_SymbolsBegin, + // sixten: (single char) + TokenKind_CurlyOpen, + TokenKind_CurlyClose, + TokenKind_ParenthesisOpen, + TokenKind_ParenthesisClose, + TokenKind_Comma, + TokenKind_Dot, + TokenKind_At, + TokenKind_PoundSign, + TokenKind_Semicolon, + TokenKind_Plus, + TokenKind_Minus, + TokenKind_Star, + TokenKind_Slash, + // sixten: (one or two chars) + TokenKind_Bang, + TokenKind_BangEqual, + TokenKind_Equal, + TokenKind_EqualEqual, + TokenKind_Greater, + TokenKind_GreaterEqual, + TokenKind_Less, + TokenKind_LessEqual, + TokenKind_SymbolsEnd, + + // sixten: keywords + TokenKind_KeywordsBegin, + TokenKind_And, + TokenKind_Branch, + TokenKind_Else, + TokenKind_False, + TokenKind_For, + TokenKind_If, + TokenKind_Jump, + TokenKind_Or, + TokenKind_Proc, + TokenKind_True, + TokenKind_Var, + TokenKind_While, + TokenKind_Nav, + TokenKind_Background, + TokenKind_KeywordsEnd, + + // sixten: whitespace + TokenKind_Whitespace, + TokenKind_Newline, + TokenKind_Comment, + + // sixten: invalid kinds + TokenKind_BrokenComment, + TokenKind_BrokenStringLiteral, + TokenKind_BadCharacter, }; typedef b32 tokenizer_filter_function(token_kind Kind); struct token { - token_kind Kind; - range1_s64 Range; - s64 Line; + token_kind Kind; + range1_s64 Range; + s64 Line; }; struct token_chunk_node { - token *Tokens; - s64 MaxCount; - s64 Count; - token_chunk_node *Next; + token *Tokens; + s64 MaxCount; + s64 Count; + token_chunk_node *Next; }; struct token_chunk_list { - token_chunk_node *First; - token_chunk_node *Last; - s64 Count; + token_chunk_node *First; + token_chunk_node *Last; + s64 Count; }; struct token_array { - token *Tokens; - s64 Count; + token *Tokens; + s64 Count; }; //////////////////////////////// //~ sixten: Tokenizer Message Types enum tokenizer_message_kind { - T_MessageKind_Invalid, - T_MessageKind_Note, - T_MessageKind_Warning, - T_MessageKind_Error, + T_MessageKind_Invalid, + T_MessageKind_Note, + T_MessageKind_Warning, + T_MessageKind_Error, }; struct tokenizer_message { - tokenizer_message *Next; - tokenizer_message_kind Kind; - s64 Offset; - string String; + tokenizer_message *Next; + tokenizer_message_kind Kind; + s64 Offset; + string String; }; struct tokenizer_message_list { - tokenizer_message *First; - tokenizer_message *Last; - s64 Count; + tokenizer_message *First; + tokenizer_message *Last; + s64 Count; }; //////////////////////////////// //~ sixten: Text -> Token Types struct tokenize_result { - token_array Tokens; - tokenizer_message_list Messages; + token_array Tokens; + tokenizer_message_list Messages; }; //////////////////////////////// diff --git a/code/vn_ui.cpp b/code/vn_ui.cpp index 4a65dd3..2177f7d 100644 --- a/code/vn_ui.cpp +++ b/code/vn_ui.cpp @@ -2,989 +2,1003 @@ static ui_size UI_MakeSize(ui_size_kind Kind, r32 Value, r32 Strictness) { - ui_size Result; - Result.Kind = Kind; - Result.Value = Value; - Result.Strictness = Strictness; - return(Result); + ui_size Result; + Result.Kind = Kind; + Result.Value = Value; + Result.Strictness = Strictness; + return(Result); } per_thread ui *ThreadLocal_UI; inline void UI_SetState(ui *UI) { - ThreadLocal_UI = UI; + ThreadLocal_UI = UI; } inline ui *UI_GetState(void) { - return(ThreadLocal_UI); + return(ThreadLocal_UI); } inline ui_key UI_HotKey(void) { - return(UI_GetState()->Hot); + return(UI_GetState()->Hot); } inline ui_key UI_ActiveKey(void) { - return(UI_GetState()->Active); + return(UI_GetState()->Active); } inline platform_event_list *UI_EventList(void) { - return(UI_GetState()->EventList); + return(UI_GetState()->EventList); } inline arena *UI_FrameArena(void) { - return(UI_GetState()->FrameArena); + return(UI_GetState()->FrameArena); } inline v2_r32 UI_MouseP(void) { - return(UI_GetState()->MouseP); + return(UI_GetState()->MouseP); } inline glyph_atlas *UI_GlyphAtlas(void) { - return(UI_GetState()->GlyphAtlas); + return(UI_GetState()->GlyphAtlas); } inline void UI_SetDragStartP(v2 P) { - ui *UI = UI_GetState(); - UI->DragStartP = P; + ui *UI = UI_GetState(); + UI->DragStartP = P; } inline void UI_UpdateDragStartP(void) { - ui *UI = UI_GetState(); - UI->DragStartP = UI->MouseP; + ui *UI = UI_GetState(); + UI->DragStartP = UI->MouseP; } inline v2 UI_GetDragStartP(void) { - ui *UI = UI_GetState(); - return(UI->DragStartP); + ui *UI = UI_GetState(); + return(UI->DragStartP); } inline void UI_StoreDragV2(v2 DragData) { - ui *UI = UI_GetState(); - *(v2 *)&UI->DragData = DragData; + ui *UI = UI_GetState(); + *(v2 *)&UI->DragData = DragData; } inline v2 UI_GetDragV2(void) { - ui *UI = UI_GetState(); - v2 Result = *(v2 *)UI->DragData; - return(Result); + ui *UI = UI_GetState(); + v2 Result = *(v2 *)UI->DragData; + return(Result); } inline void UI_StoreDragR32(r32 DragData) { - ui *UI = UI_GetState(); - *(r32 *)&UI->DragData = DragData; + ui *UI = UI_GetState(); + *(r32 *)&UI->DragData = DragData; } inline r32 UI_GetDragR32(void) { - ui *UI = UI_GetState(); - r32 Result = *(r32 *)UI->DragData; - return(Result); + ui *UI = UI_GetState(); + r32 Result = *(r32 *)UI->DragData; + return(Result); } inline void UI_StoreDragPayload(void *Source) { - ui *UI = UI_GetState(); - Copy(UI->DragData, Source, sizeof(UI->DragData)); + ui *UI = UI_GetState(); + Copy(UI->DragData, Source, sizeof(UI->DragData)); } inline void UI_GetDragPayload(void *Dest) { - ui *UI = UI_GetState(); - Copy(Dest, UI->DragData, sizeof(UI->DragData)); + ui *UI = UI_GetState(); + Copy(Dest, UI->DragData, sizeof(UI->DragData)); } inline void UI_StoreDragPointer(void *Data) { - ui *UI = UI_GetState(); - *(void **)&UI->DragData = Data; + ui *UI = UI_GetState(); + *(void **)&UI->DragData = Data; } inline void *UI_GetDragDataPointer(void) { - ui *UI = UI_GetState(); - void *Result = *(void **)UI->DragData; - return(Result); + ui *UI = UI_GetState(); + void *Result = *(void **)UI->DragData; + return(Result); } static r64 UI_Time(void) { - return(UI_GetState()->Time); + return(UI_GetState()->Time); +} + +static r64 UI_Blink(void) +{ + return(Cos(UI_GetState()->BlinkTime*10.0f)*0.5f+0.5f); +} + +static void UI_ResetBlink(void) +{ + UI_GetState()->BlinkTime = 0; } inline ui_key UI_EmptyKey(void) { - ui_key Key = {}; - return(Key); + ui_key Key = {}; + return(Key); } inline ui_key UI_SeedKey(ui_key Key, ui_key Seed) { - ui_key Result = {((Key.Value + Seed.Value) << 5) + Key.Value}; - return(Result); + ui_key Result = {((Key.Value + Seed.Value) << 5) + Key.Value}; + return(Result); } inline ui_key UI_KeyFromString(string String) { - ui_key Key; - if(String.Count) - { - Key.Value = HashString(String); - } - else - { - Key = UI_EmptyKey(); - } - - return(Key); + ui_key Key; + if(String.Count) + { + Key.Value = HashString(String); + } + else + { + Key = UI_EmptyKey(); + } + + return(Key); } static string UI_BoxStringFromKey(ui_key Key) { - ui *UI = UI_GetState(); - string Result = StrLit("Empty or Not Found"); - - if(!AreEqual(Key, UI_EmptyKey())) - { - for(s32 BucketIndex = 0; - BucketIndex < ArrayCount(UI->BoxBuckets); - ++BucketIndex) - { - ui_box_bucket *Bucket = UI->BoxBuckets + BucketIndex; - for(ui_box *Box = Bucket->First; - Box != 0; - Box = Box->HashNext) - { - if(AreEqual(Key, Box->Key)) - { - Result = Box->String; - goto FoundName; - } - } - } - } - - FoundName: - return(Result); + ui *UI = UI_GetState(); + string Result = StrLit("Empty or Not Found"); + + if(!AreEqual(Key, UI_EmptyKey())) + { + for(s32 BucketIndex = 0; + BucketIndex < ArrayCount(UI->BoxBuckets); + ++BucketIndex) + { + ui_box_bucket *Bucket = UI->BoxBuckets + BucketIndex; + for(ui_box *Box = Bucket->First; + Box != 0; + Box = Box->HashNext) + { + if(AreEqual(Key, Box->Key)) + { + Result = Box->String; + goto FoundName; + } + } + } + } + + FoundName: + return(Result); } inline ui_box *UI_BoxFromKey(ui *UI, ui_key Key) { - u64 Hash = Key.Value; - u64 Slot = Hash % ArrayCount(UI->BoxBuckets); - - ui_box_bucket *Bucket = UI->BoxBuckets + Slot; - - ui_box *Result = 0; - if(!AreEqual(Key, UI_EmptyKey())) - { - for(ui_box *Box = Bucket->First; - Box != 0; - Box = Box->HashNext) - { - if(AreEqual(Box->Key, Key)) - { - Result = Box; - break; - } - } - } - - if(!Result) - { - // sixten: Check if we have already allocated a box - if(DLLIsEmpty(UI->FirstFreeBox)) - { - // sixten: If not, simply allocate one - Result = PushStruct(UI->Arena, ui_box); - } - else - { - // sixten: If there exists an already allocated, remove it from the free list and use it. - Result = UI->FirstFreeBox; - DLLRemove_NP(UI->FirstFreeBox, UI->LastFreeBox, Result, HashNext, HashPrev); - } - - // sixten: Insert the box into the hashmap. - DLLInsertLast_NP(Bucket->First, Bucket->Last, Result, HashNext, HashPrev); - } - - Result->Key = Key; - - return(Result); + u64 Hash = Key.Value; + u64 Slot = Hash % ArrayCount(UI->BoxBuckets); + + ui_box_bucket *Bucket = UI->BoxBuckets + Slot; + + ui_box *Result = 0; + if(!AreEqual(Key, UI_EmptyKey())) + { + for(ui_box *Box = Bucket->First; + Box != 0; + Box = Box->HashNext) + { + if(AreEqual(Box->Key, Key)) + { + Result = Box; + break; + } + } + } + + if(!Result) + { + // sixten: Check if we have already allocated a box + if(DLLIsEmpty(UI->FirstFreeBox)) + { + // sixten: If not, simply allocate one + Result = PushStruct(UI->Arena, ui_box); + } + else + { + // sixten: If there exists an already allocated, remove it from the free list and use it. + Result = UI->FirstFreeBox; + DLLRemove_NP(UI->FirstFreeBox, UI->LastFreeBox, Result, HashNext, HashPrev); + } + + // sixten: Insert the box into the hashmap. + DLLInsertLast_NP(Bucket->First, Bucket->Last, Result, HashNext, HashPrev); + } + + Result->Key = Key; + + return(Result); } inline ui_box *UI_MakeBox(ui_box_flags Flags, string String) { - ui *UI = UI_GetState(); - - ui_box *Parent = UI_TopParent(); - - ui_key BaseKey = UI_KeyFromString(String); - ui_key Seed = UI_SeedKey(BaseKey, Parent ? Parent->Seed : UI_EmptyKey()); - ui_key Key = BaseKey.Value ? Seed : UI_EmptyKey(); - - // sixten: Check for duplicate keys. + ui *UI = UI_GetState(); + + ui_box *Parent = UI_TopParent(); + + string KeyString = String; + s64 HashIndex = LastIndexOf(KeyString, StrLit("##")); + if(HashIndex != -1) + { + KeyString = Suffix(KeyString, KeyString.Count-HashIndex-2); + } + + ui_key BaseKey = UI_KeyFromString(KeyString); + ui_key Seed = UI_SeedKey(BaseKey, Parent ? Parent->Seed : UI_EmptyKey()); + ui_key Key = BaseKey.Value ? Seed : UI_EmptyKey(); + + // sixten: Check for duplicate keys. #if VN_SLOW - if(Parent && !AreEqual(Key, UI_EmptyKey())) - { - for(ui_box *Child = Parent->First; - Child != 0; - Child = Child->Next) - { - Assert(!AreEqual(Child->Key, Key)); - } - } + if(Parent && !AreEqual(Key, UI_EmptyKey())) + { + for(ui_box *Child = Parent->First; + Child != 0; + Child = Child->Next) + { + Assert(!AreEqual(Child->Key, Key)); + } + } #endif - - ui_box *Box = UI_BoxFromKey(UI, Key); - Box->Seed = Seed; - - Box->First = Box->Last = Box->Next = Box->Prev = Box->Parent = 0; - - Box->ComputedRelativeP = V2(0, 0); - Box->ComputedDim = V2(0, 0); - - Box->LastFrameTouched = UI->CurrentFrame; - - Box->Flags = Flags; - Box->String = PushString(UI->FrameArena, String); - - s64 HashIndex = LastIndexOf(Box->String, '#'); - if(HashIndex != -1) - { - Box->String = Prefix(Box->String, HashIndex); - } - - UI_ApplyStyles(Box); - - if(Parent) - { - DLLInsertLast(Parent->First, Parent->Last, Box); - } - - return(Box); + + ui_box *Box = UI_BoxFromKey(UI, Key); + Box->Seed = Seed; + + Box->First = Box->Last = Box->Next = Box->Prev = Box->Parent = 0; + + Box->ComputedRelativeP = V2(0, 0); + Box->ComputedDim = V2(0, 0); + + Box->LastFrameTouched = UI->CurrentFrame; + + Box->Flags = Flags; + Box->String = PushString(UI->FrameArena, String); + + if(HashIndex != -1) + { + Box->String = Prefix(Box->String, HashIndex); + } + else + { + HashIndex = LastIndexOf(Box->String, '#'); + if(HashIndex != -1) + { + Box->String = Prefix(Box->String, HashIndex); + } + } + + UI_ApplyStyles(Box); + + if(Parent) + { + DLLInsertLast(Parent->First, Parent->Last, Box); + } + + return(Box); } inline ui_box *UI_MakeBoxF(ui_box_flags Flags, char *Format, ...) { - ui *UI = UI_GetState(); - - // sixten(TODO): Allocate on scratch, copy to frame arena - // (alternatively keep two versions of UI_MakeBox, to make sure everything works) - - va_list Arguments; - va_start(Arguments, Format); - string String = PushFormatVariadic(UI->FrameArena, Format, Arguments); - va_end(Arguments); - - ui_box *Box = UI_MakeBox(Flags, String); - return(Box); + ui *UI = UI_GetState(); + + // sixten(TODO): Allocate on scratch, copy to frame arena + // (alternatively keep two versions of UI_MakeBox, to make sure everything works) + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(UI->FrameArena, Format, Arguments); + va_end(Arguments); + + ui_box *Box = UI_MakeBox(Flags, String); + return(Box); } inline void UI_SetNextHot(ui_key Key) { - ui *UI = UI_GetState(); - if(!UI->NextHotSet) - { - UI->NextHot = Key; - UI->NextHotSet = true; - } + ui *UI = UI_GetState(); + if(!UI->NextHotSet) + { + UI->NextHot = Key; + UI->NextHotSet = true; + } } static void UI_EquipBoxText(ui_box *Box, string String) { - Box->String = PushString(UI_FrameArena(), 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; + 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) { - b32 Result = false; - - for(ui_box *Child = Parent->First; - Child != 0; - Child = Child->Next) - { - range2_r32 IntersectionRect = Intersection(Clip, Child->Rect); - - if(Child->Flags & UI_BoxFlag_Clickable && InRange(IntersectionRect, P)) - { - Result = true; - break; - } - else - { - if(Child->Flags & UI_BoxFlag_Clip) - { - Result = UI_ChildrenContainsP(Child, P, IntersectionRect); - } - else - { - Result = UI_ChildrenContainsP(Child, P, Clip); - } - - if(Result) - { - break; - } - } - - if(Child == Parent->Last) - { - break; - } - } - - return(Result); + b32 Result = false; + + for(ui_box *Child = Parent->First; + Child != 0; + Child = Child->Next) + { + range2_r32 IntersectionRect = Intersection(Clip, Child->Rect); + + if(Child->Flags & UI_BoxFlag_Clickable && InRange(IntersectionRect, P)) + { + Result = true; + break; + } + else + { + if(Child->Flags & UI_BoxFlag_Clip) + { + Result = UI_ChildrenContainsP(Child, P, IntersectionRect); + } + else + { + Result = UI_ChildrenContainsP(Child, P, Clip); + } + + if(Result) + { + break; + } + } + + if(Child == Parent->Last) + { + break; + } + } + + return(Result); } static ui_signal UI_SignalFromBox(ui_box *Box) { - ui *UI = UI_GetState(); - - ui_signal Signal = {}; - - // sixten: Gather the clipping rects of all the parents. - range2_r32 ClippedRect = Box->Rect; - for(ui_box *Parent = Box->Parent; - Parent != 0; - Parent = Parent->Parent) - { - if(Parent->Flags & UI_BoxFlag_Clip) - { - ClippedRect = Intersection(ClippedRect, Parent->Rect); - } - } - - Signal.Hovering = InRange(ClippedRect, UI->MouseP); - - // sixten: Make sure no children boxes overlap. - Signal.Hovering &= !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect); - - // sixten: Make sure no previous boxes overlap. - if(Box->Prev) - { - ui_box PrevBoxParent = {}; - PrevBoxParent.Rect = Box->Rect; - PrevBoxParent.First = Box->Parent->First; - PrevBoxParent.Last = Box->Prev; - - Signal.Hovering &= !UI_ChildrenContainsP(&PrevBoxParent, UI->MouseP, ClippedRect); - } - - // sixten: Make sure the tooltip is not overlapping. - { - // sixten: Are we the tooltip? - b32 FoundTooltip = false; - for(ui_box *Parent = Box->Parent; - Parent != 0; - Parent = Parent->Parent) - { - if(Parent == UI->TooltipNode) - { - FoundTooltip = true; - break; - } - } - - if(!FoundTooltip) - { - Signal.Hovering &= ~UI_ChildrenContainsP(UI->TooltipNode, UI->MouseP, - Range2R32(V2(0, 0), V2(InfinityR32, InfinityR32))); - } - } - - if(Box->Flags & UI_BoxFlag_Clickable) - { - if(AreEqual(UI->Active, Box->Key)) - { - if(Platform_KeyRelease(UI->EventList, Key_MouseLeft)) - { - Signal.Clicked = Signal.Hovering; - Signal.Released = true; - - UI->Active = UI_EmptyKey(); - - if(!Signal.Hovering) - { - UI_SetNextHot(UI_EmptyKey()); - } - } - else - { - Signal.Dragging = true; - } - } - else - { - if(AreEqual(UI->Hot, Box->Key)) - { - if(Platform_KeyPress(UI->EventList, Key_MouseLeft)) - { - UI->Active = Box->Key; - UI->DragStartP = UI->MouseP; - - Signal.Dragging = true; - Signal.Pressed = true; - } - else if(!Signal.Hovering) - { - UI_SetNextHot(UI_EmptyKey()); - } - - if(Platform_KeyPress(UI->EventList, Key_MouseRight)) - { - Signal.PressedRight = true; - } - } - else - { - if(AreEqual(UI->Hot, UI_EmptyKey()) && Signal.Hovering) - { - UI_SetNextHot(Box->Key); - } - } - } - } - - if(AreEqual(UI->Hot, Box->Key)) - { - Platform.SetCursor(Box->HoverCursor); - } - - Signal.MouseP = UI->MouseP; - Signal.dMouseP = UI->dMouseP; - Signal.DragDelta = UI->MouseP - UI->DragStartP; - Signal.Box = Box; - - return(Signal); + ui *UI = UI_GetState(); + + ui_signal Signal = {}; + + // sixten: Gather the clipping rects of all the parents. + range2_r32 ClippedRect = Box->Rect; + for(ui_box *Parent = Box->Parent; + Parent != 0; + Parent = Parent->Parent) + { + if(Parent->Flags & UI_BoxFlag_Clip) + { + ClippedRect = Intersection(ClippedRect, Parent->Rect); + } + } + + Signal.Hovering = InRange(ClippedRect, UI->MouseP); + + // sixten: Make sure no children boxes overlap. + Signal.Hovering &= !UI_ChildrenContainsP(Box, UI->MouseP, ClippedRect); + + // sixten: Make sure no previous boxes overlap. + if(Box->Prev) + { + ui_box PrevBoxParent = {}; + PrevBoxParent.Rect = Box->Rect; + PrevBoxParent.First = Box->Parent->First; + PrevBoxParent.Last = Box->Prev; + + Signal.Hovering &= !UI_ChildrenContainsP(&PrevBoxParent, UI->MouseP, ClippedRect); + } + + // sixten: Make sure the tooltip is not overlapping. + { + // sixten: Are we the tooltip? + b32 FoundTooltip = false; + for(ui_box *Parent = Box->Parent; + Parent != 0; + Parent = Parent->Parent) + { + if(Parent == UI->TooltipNode) + { + FoundTooltip = true; + break; + } + } + + if(!FoundTooltip) + { + Signal.Hovering &= ~UI_ChildrenContainsP(UI->TooltipNode, UI->MouseP, + Range2R32(V2(0, 0), V2(InfinityR32, InfinityR32))); + } + } + + if(Box->Flags & UI_BoxFlag_Clickable) + { + if(AreEqual(UI->Active, Box->Key)) + { + if(Platform_KeyRelease(UI->EventList, Key_MouseLeft)) + { + Signal.Clicked = Signal.Hovering; + Signal.Released = true; + + UI->Active = UI_EmptyKey(); + + if(!Signal.Hovering) + { + UI_SetNextHot(UI_EmptyKey()); + } + } + else + { + Signal.Dragging = true; + } + } + else + { + if(AreEqual(UI->Hot, Box->Key)) + { + if(Platform_KeyPress(UI->EventList, Key_MouseLeft)) + { + UI->Active = Box->Key; + UI->DragStartP = UI->MouseP; + + Signal.Dragging = true; + Signal.Pressed = true; + } + else if(!Signal.Hovering) + { + UI_SetNextHot(UI_EmptyKey()); + } + + if(Platform_KeyPress(UI->EventList, Key_MouseRight)) + { + Signal.PressedRight = true; + } + } + else + { + if(AreEqual(UI->Hot, UI_EmptyKey()) && Signal.Hovering) + { + UI_SetNextHot(Box->Key); + } + } + } + } + + if(AreEqual(UI->Hot, Box->Key)) + { + Platform.SetCursor(Box->HoverCursor); + } + + Signal.MouseP = UI->MouseP; + Signal.dMouseP = UI->dMouseP; + Signal.DragDelta = UI->MouseP - UI->DragStartP; + Signal.Box = Box; + + return(Signal); } static void UI_SolveSizeViolations(ui_box *Box, axis2 Axis) { - r32 TotalSpace = 0; - r32 FixupBudget = 0; - - if(!(Box->Flags & (UI_BoxFlag_OverflowX<First; - Child != 0; - Child = Child->Next) - { - if(!(Child->Flags & (UI_BoxFlag_FloatingX<LayoutAxis) - { - TotalSpace += Child->ComputedDim.E[Axis]; - } - else - { - TotalSpace = Maximum(TotalSpace, Child->ComputedDim.E[Axis]); - } - FixupBudget += Child->ComputedDim.E[Axis] * (1 - Child->SemanticSize[Axis].Strictness); - } - } - - r32 Violation = TotalSpace - Box->ComputedDim.E[Axis]; - if(Violation > 0 && FixupBudget > 0) - { - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - if(!(Child->Flags & (UI_BoxFlag_FloatingX<ComputedDim.E[Axis] * (1 - Child->SemanticSize[Axis].Strictness); - r32 ChildFixupSize = 0; - if(Axis == Box->LayoutAxis) - { - ChildFixupSize = ChildFixupBudget * (Violation / FixupBudget); - } - else - { - ChildFixupSize = Child->ComputedDim.E[Axis] - Box->ComputedDim.E[Axis]; - } - - ChildFixupSize = Clamp(ChildFixupSize, 0, ChildFixupBudget); - Child->ComputedDim.E[Axis] -= ChildFixupSize; - } - } - } - } - - if(Axis == Box->LayoutAxis) - { - r32 Position = 0; - for(ui_box *Child = Box->First; - Child; - Child = Child->Next) - { - if((Child->Flags & (UI_BoxFlag_FloatingX<ComputedRelativeP.E[Axis] = Child->FixedP.E[Axis]; - } - else - { - Child->ComputedRelativeP.E[Axis] = Position; - Position += Child->ComputedDim.E[Axis]; - } - } - } - else - { - for(ui_box *Child = Box->First; - Child; - Child = Child->Next) - { - if((Child->Flags & (UI_BoxFlag_FloatingX<ComputedRelativeP.E[Axis] = Child->FixedP.E[Axis]; - } - else - { - Child->ComputedRelativeP.E[Axis] = 0; - } - } - } + r32 TotalSpace = 0; + r32 FixupBudget = 0; + + if(!(Box->Flags & (UI_BoxFlag_OverflowX<First; + Child != 0; + Child = Child->Next) + { + if(!(Child->Flags & (UI_BoxFlag_FloatingX<LayoutAxis) + { + TotalSpace += Child->ComputedDim.E[Axis]; + } + else + { + TotalSpace = Maximum(TotalSpace, Child->ComputedDim.E[Axis]); + } + FixupBudget += Child->ComputedDim.E[Axis] * (1 - Child->SemanticSize[Axis].Strictness); + } + } + + r32 Violation = TotalSpace - Box->ComputedDim.E[Axis]; + if(Violation > 0 && FixupBudget > 0) + { + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + if(!(Child->Flags & (UI_BoxFlag_FloatingX<ComputedDim.E[Axis] * (1 - Child->SemanticSize[Axis].Strictness); + r32 ChildFixupSize = 0; + if(Axis == Box->LayoutAxis) + { + ChildFixupSize = ChildFixupBudget * (Violation / FixupBudget); + } + else + { + ChildFixupSize = Child->ComputedDim.E[Axis] - Box->ComputedDim.E[Axis]; + } + + ChildFixupSize = Clamp(ChildFixupSize, 0, ChildFixupBudget); + Child->ComputedDim.E[Axis] -= ChildFixupSize; + } + } + } + } + + if(Axis == Box->LayoutAxis) + { + r32 Position = 0; + for(ui_box *Child = Box->First; + Child; + Child = Child->Next) + { + if((Child->Flags & (UI_BoxFlag_FloatingX<ComputedRelativeP.E[Axis] = Child->FixedP.E[Axis]; + } + else + { + Child->ComputedRelativeP.E[Axis] = Position; + Position += Child->ComputedDim.E[Axis]; + } + } + } + else + { + for(ui_box *Child = Box->First; + Child; + Child = Child->Next) + { + if((Child->Flags & (UI_BoxFlag_FloatingX<ComputedRelativeP.E[Axis] = Child->FixedP.E[Axis]; + } + else + { + Child->ComputedRelativeP.E[Axis] = 0; + } + } + } } inline void UI_BeginTooltip(void) { - ui *UI = UI_GetState(); - UI_PushParent(UI->TooltipNode); + ui *UI = UI_GetState(); + UI_PushParent(UI->TooltipNode); } inline void UI_EndTooltip(void) { - UI_PopParent(); + UI_PopParent(); } inline void UI_SetNextTooltip(void) { - ui *UI = UI_GetState(); - UI_SetNextParent(UI->TooltipNode); + ui *UI = UI_GetState(); + UI_SetNextParent(UI->TooltipNode); } #define UI_Tooltip DeferLoop(UI_BeginTooltip(), UI_EndTooltip()) static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas) { - ui *UI = UI_GetState(); - Box->HotTransition += ((r32)AreEqual(UI->Hot, Box->Key) - Box->HotTransition) * 0.6f; - Box->ActiveTransition += ((r32)AreEqual(UI->Active, Box->Key) - Box->ActiveTransition) * 0.6f; - - if(Box->Flags & UI_BoxFlag_DrawBackground) - { - PushQuad(Group, Box->Rect, Box->BackgroundColor, Box->CornerRadius, 0, 0); - } - - if(Box->Flags & UI_BoxFlag_HotAnimation) - { - v4 Top = V4(1, 1, 1, 0.08F*Box->HotTransition); - v4 Bottom = V4(1, 1, 1, 0.0); - - PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); - } - - if(Box->Flags & UI_BoxFlag_ActiveAnimation) - { - v4 Top = V4(0, 0, 0, 0.7F*Box->ActiveTransition); - v4 Bottom = V4(0, 0, 0, 0.1F*Box->ActiveTransition); - - PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); - } - - if(Box->Flags & UI_BoxFlag_DrawText) - { - v2 TextDim = V2(CalculateRasterizedTextWidth(GlyphAtlas, Box->Font, Box->FontSize, Box->String), - CalculateRasterizedTextHeight(GlyphAtlas, Box->Font, Box->FontSize, Box->String)); - - v2 P = Box->Rect.Min + (Box->ComputedDim - TextDim)*0.5; - - PushText(Group, GlyphAtlas, Box->Font, P, Box->FontSize, Box->TextColor, Box->String); - } - - if(Box->DrawCallback) - { - Box->DrawCallback(Group, GlyphAtlas, Box, Box->DrawCallbackData); - } - + ui *UI = UI_GetState(); + Box->HotTransition += ((r32)AreEqual(UI->Hot, Box->Key) - Box->HotTransition) * 0.6f; + Box->ActiveTransition += ((r32)AreEqual(UI->Active, Box->Key) - Box->ActiveTransition) * 0.6f; + + if(Box->Flags & UI_BoxFlag_DrawBackground) + { + PushQuad(Group, Box->Rect, Box->BackgroundColor, Box->CornerRadius, 0, 0); + } + + if(Box->Flags & UI_BoxFlag_HotAnimation) + { + v4 Top = V4(1, 1, 1, 0.08F*Box->HotTransition); + v4 Bottom = V4(1, 1, 1, 0.0); + + PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); + } + + if(Box->Flags & UI_BoxFlag_ActiveAnimation) + { + v4 Top = V4(0, 0, 0, 0.7F*Box->ActiveTransition); + v4 Bottom = V4(0, 0, 0, 0.1F*Box->ActiveTransition); + + PushQuad(Group, Box->Rect, Top, Top, Bottom, Bottom, Box->CornerRadius, 0, 0); + } + + if(Box->Flags & UI_BoxFlag_DrawText) + { + v2 TextDim = V2(CalculateRasterizedTextWidth(GlyphAtlas, Box->Font, Box->FontSize, Box->String), + CalculateRasterizedTextHeight(GlyphAtlas, Box->Font, Box->FontSize, Box->String)); + + v2 P = Box->Rect.Min + (Box->ComputedDim - TextDim)*0.5; + + PushText(Group, GlyphAtlas, Box->Font, P, Box->FontSize, Box->TextColor, Box->String); + } + + if(Box->DrawCallback) + { + Box->DrawCallback(Group, GlyphAtlas, Box, Box->DrawCallbackData); + } + #if VN_INTERNAL - if(DEBUG_DebugSettings->RenderUIDebugRects) - { - // sixten: Render debug rects around boxes. - r32 R = (((Box->Key.Value >> 0) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); - r32 G = (((Box->Key.Value >> 21) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); - r32 B = (((Box->Key.Value >> 42) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); - v4 Red = V4R32(R, G, B, 1); - PushQuad(Group, Box->Rect, Red, Red, Red, Red, 0, 1.8, 1.8); - } + if(DEBUG_DebugSettings->RenderUIDebugRects) + { + // sixten: Render debug rects around boxes. + r32 R = (((Box->Key.Value >> 0) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); + r32 G = (((Box->Key.Value >> 21) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); + r32 B = (((Box->Key.Value >> 42) & ((1 << 22) - 1)) / (r32)((1 << 22) - 1)); + v4 Red = V4R32(R, G, B, 1); + PushQuad(Group, Box->Rect, Red, Red, Red, Red, 0, 1.8, 1.8); + } #endif - - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - if(Child->Flags & UI_BoxFlag_DrawDropShadow) - { - r32 ShadowRadius = 10; - - range2_r32 Rect = Range2R32(Child->Rect.Min - V2R32(ShadowRadius, ShadowRadius), - Child->Rect.Max + V2R32(ShadowRadius, ShadowRadius)); - - v4 ShadowColor = V4(0, 0, 0, 0.3); - - PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0); - } - } - - if(Box->Flags & UI_BoxFlag_Clip) - { - range2_r32 Rect = Intersection(Group->ClipStack[Group->ClipStackUsed], Box->Rect); - PushClip(Group, Rect); - } - - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - UI_DrawBox(Child, Group, GlyphAtlas); - } - - if(Box->Flags & UI_BoxFlag_Clip) - { - PopClip(Group); - } - - if(Box->Flags & UI_BoxFlag_DrawBorder) - { - PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness); - } - + + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + if(Child->Flags & UI_BoxFlag_DrawDropShadow) + { + r32 ShadowRadius = 10; + + range2_r32 Rect = Range2R32(Child->Rect.Min - V2R32(ShadowRadius, ShadowRadius), + Child->Rect.Max + V2R32(ShadowRadius, ShadowRadius)); + + v4 ShadowColor = V4(0, 0, 0, 0.3); + + PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0); + } + } + + if(Box->Flags & UI_BoxFlag_Clip) + { + range2_r32 Rect = Intersection(Group->ClipStack[Group->ClipStackUsed], Box->Rect); + PushClip(Group, Rect); + } + + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + UI_DrawBox(Child, Group, GlyphAtlas); + } + + if(Box->Flags & UI_BoxFlag_Clip) + { + PopClip(Group); + } + + if(Box->Flags & UI_BoxFlag_DrawBorder) + { + PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness); + } + } static r32 UI_CalculateChildrenSum(ui_box *Box, axis2 Axis, b32 ForceCalculate); static r32 UI_CalculateBoxSize(ui_box *Box, axis2 Axis, b32 ForceCalculate = false) { - r32 Result = 0; - - ui *UI = UI_GetState(); - ui_box *Parent = Box->Parent; - - switch(Box->SemanticSize[Axis].Kind) - { - case UI_SizeKind_Pixels: - { - Result = Box->SemanticSize[Axis].Value; - } break; - - case UI_SizeKind_TextContent: - { - glyph_atlas *Atlas = UI->GlyphAtlas; - - Result = ((Axis == Axis2_X) ? - CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, Box->String) : - CalculateRasterizedTextHeight(Atlas, Box->Font, Box->FontSize, Box->String)) + - Box->SemanticSize[Axis].Value; - } break; - - case UI_SizeKind_PercentOfParent: - { - if(Parent && Parent->SemanticSize[Axis].Kind != UI_SizeKind_ChildrenSum) - { - r32 Size = Parent->ComputedDim.E[Axis]; - // sixten: if the size is zero, try to find it. - if(Size == 0.0) - { - if(ForceCalculate) - { - Size = UI_CalculateBoxSize(Parent, Axis, ForceCalculate); - } - else - { - Size = DimOfRange(Parent->Rect).E[Axis]; - } - } - Result = Size*Box->SemanticSize[Axis].Value; - } - else - { - // sixten(NOTE): A box that depends on the size of the parent can not be allowed if the parent depends - // on the children sum. - InvalidCodepath; - } - } break; - - case UI_SizeKind_ChildrenSum: - { - Result = UI_CalculateChildrenSum(Box, Axis, ForceCalculate)*Box->SemanticSize[Axis].Value; - } break; - - InvalidDefaultCase; - } - - return(Result); + r32 Result = 0; + + ui *UI = UI_GetState(); + ui_box *Parent = Box->Parent; + + switch(Box->SemanticSize[Axis].Kind) + { + case UI_SizeKind_Pixels: + { + Result = Box->SemanticSize[Axis].Value; + } break; + + case UI_SizeKind_TextContent: + { + glyph_atlas *Atlas = UI->GlyphAtlas; + + Result = ((Axis == Axis2_X) ? + CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, Box->String) : + CalculateRasterizedTextHeight(Atlas, Box->Font, Box->FontSize, Box->String)) + + Box->SemanticSize[Axis].Value; + } break; + + case UI_SizeKind_PercentOfParent: + { + if(Parent && Parent->SemanticSize[Axis].Kind != UI_SizeKind_ChildrenSum) + { + r32 Size = Parent->ComputedDim.E[Axis]; + // sixten: if the size is zero, try to find it. + if(Size == 0.0) + { + if(ForceCalculate) + { + Size = UI_CalculateBoxSize(Parent, Axis, ForceCalculate); + } + else + { + Size = DimOfRange(Parent->Rect).E[Axis]; + } + } + Result = Size*Box->SemanticSize[Axis].Value; + } + else + { + // sixten(NOTE): A box that depends on the size of the parent can not be allowed if the parent depends + // on the children sum. + InvalidCodepath; + } + } break; + + case UI_SizeKind_ChildrenSum: + { + Result = UI_CalculateChildrenSum(Box, Axis, ForceCalculate)*Box->SemanticSize[Axis].Value; + } break; + + InvalidDefaultCase; + } + + return(Result); } static v2_r32 UI_CalculateBoxDim(ui_box *Box) { - v2_r32 Result = V2R32(UI_CalculateBoxSize(Box, Axis2_X, true), UI_CalculateBoxSize(Box, Axis2_Y, true)); - return(Result); + v2_r32 Result = V2R32(UI_CalculateBoxSize(Box, Axis2_X, true), UI_CalculateBoxSize(Box, Axis2_Y, true)); + return(Result); } static r32 UI_CalculateChildrenSum(ui_box *Box, axis2 Axis, b32 ForceCalculate = false) { - r32 Result = 0; - - if(Box->LayoutAxis == Axis) - { - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - Result += UI_CalculateBoxSize(Child, Axis, ForceCalculate); - } - } - else - { - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - Result = Max(Result, UI_CalculateBoxSize(Child, Axis, ForceCalculate)); - } - } - return(Result); + r32 Result = 0; + + if(Box->LayoutAxis == Axis) + { + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + Result += UI_CalculateBoxSize(Child, Axis, ForceCalculate); + } + } + else + { + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + Result = Max(Result, UI_CalculateBoxSize(Child, Axis, ForceCalculate)); + } + } + return(Result); } static void UI_LayoutBox(ui_box *Box) { - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - Child->ComputedDim.E[Axis2_X] = UI_CalculateBoxSize(Child, Axis2_X); - Child->ComputedDim.E[Axis2_Y] = UI_CalculateBoxSize(Child, Axis2_Y); - } - - UI_SolveSizeViolations(Box, Axis2_X); - UI_SolveSizeViolations(Box, Axis2_Y); - - for(ui_box *Child = Box->First; - Child != 0; - Child = Child->Next) - { - if(Child->Flags & UI_BoxFlag_AnimatePosition) - { - Child->ApproachingRelativeP.x = AC_AnimateValueF(Child->ComputedRelativeP.x, Child->ComputedRelativeP.x, 0.5, "Box P.X %p", Child); - Child->ApproachingRelativeP.y = AC_AnimateValueF(Child->ComputedRelativeP.y, Child->ComputedRelativeP.y, 0.5, "Box P.Y %p", Child); - - Child->Rect.Min = Box->Rect.Min + Child->ApproachingRelativeP + Box->Offset; - Child->Rect.Max = Child->Rect.Min + Child->ComputedDim; - } - else - { - Child->Rect.Min = Box->Rect.Min + Child->ComputedRelativeP + Box->Offset; - Child->Rect.Max = Child->Rect.Min + Child->ComputedDim; - } - UI_LayoutBox(Child); - } + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + Child->ComputedDim.E[Axis2_X] = UI_CalculateBoxSize(Child, Axis2_X); + Child->ComputedDim.E[Axis2_Y] = UI_CalculateBoxSize(Child, Axis2_Y); + } + + UI_SolveSizeViolations(Box, Axis2_X); + UI_SolveSizeViolations(Box, Axis2_Y); + + for(ui_box *Child = Box->First; + Child != 0; + Child = Child->Next) + { + Child->Rect.Min = Box->Rect.Min + Child->ComputedRelativeP + Box->Offset; + Child->Rect.Max = Child->Rect.Min + Child->ComputedDim; + UI_LayoutBox(Child); + } } static void UI_Init(ui *UI) { - UI->Arena = ArenaAlloc(Kilobytes(4), true); - UI->FrameArena = ArenaAlloc(Kilobytes(16), true); + UI->Arena = ArenaAlloc(Kilobytes(4), true); + UI->FrameArena = ArenaAlloc(Kilobytes(16), true); } static void UI_BeginBuild(v2 ScreenDim) { - ui *UI = UI_GetState(); - - UI_PushParent(0); - UI_PushWidth(UI_Pixels(ScreenDim.x, 1)); - UI_PushHeight(UI_Pixels(ScreenDim.y, 1)); - UI_PushFixedX(0); - UI_PushFixedY(0); - UI_PushTextColor(Theme_TextColor); - UI_PushBackgroundColor(ColorFromHex(0x111111FF)); - UI_PushBorderColor(Theme_BorderColor); - UI_PushBorderThickness(1.8); - UI_PushLayoutAxis(Axis2_Y); - UI_PushCornerRadius(0); - UI_PushFont(Font_Regular); - 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; - - UI->ContainerNode = UI_MakeBox(UI_BoxFlag_Clickable, StrLit("UI Container Node")); - UI->TooltipNode = UI_MakeBox(UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, StrLit("UI Tooltip Node")); - - UI->Stacks.ParentStack[0] = UI->ContainerNode; - - UI->NextHotSet = false; + ui *UI = UI_GetState(); + + UI_PushParent(0); + UI_PushWidth(UI_Pixels(ScreenDim.x, 1)); + UI_PushHeight(UI_Pixels(ScreenDim.y, 1)); + UI_PushFixedX(0); + UI_PushFixedY(0); + UI_PushTextColor(Theme_TextColor); + UI_PushBackgroundColor(ColorFromHex(0x111111FF)); + UI_PushBorderColor(Theme_BorderColor); + UI_PushBorderThickness(1.8); + UI_PushLayoutAxis(Axis2_Y); + UI_PushCornerRadius(0); + UI_PushFont(Font_Regular); + 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; + + UI->ContainerNode = UI_MakeBox(UI_BoxFlag_Clickable, StrLit("UI Container Node")); + UI->TooltipNode = UI_MakeBox(UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, StrLit("UI Tooltip Node")); + + UI->Stacks.ParentStack[0] = UI->ContainerNode; + + UI->NextHotSet = false; } static void UI_EndBuild(void) { - ui *UI = UI_GetState(); - - UI_SignalFromBox(UI_GetState()->ContainerNode); - - if(UI->NextHotSet) - { - UI->Hot = UI->NextHot; - } - - UI_PopParent(); - UI_PopWidth(); - UI_PopHeight(); - UI_PopFixedX(); - UI_PopFixedY(); - UI_PopTextColor(); - UI_PopBackgroundColor(); - UI_PopBorderColor(); - UI_PopBorderThickness(); - UI_PopLayoutAxis(); - UI_PopCornerRadius(); - UI_PopFont(); - UI_PopFontSize(); - UI_PopOffsetX(); - UI_PopOffsetY(); - UI_PopHoverCursor(); - - UI_LayoutBox(UI->RootNode); + ui *UI = UI_GetState(); + + UI_SignalFromBox(UI_GetState()->ContainerNode); + + if(UI->NextHotSet) + { + UI->Hot = UI->NextHot; + } + + UI_PopParent(); + UI_PopWidth(); + UI_PopHeight(); + UI_PopFixedX(); + UI_PopFixedY(); + UI_PopTextColor(); + UI_PopBackgroundColor(); + UI_PopBorderColor(); + UI_PopBorderThickness(); + UI_PopLayoutAxis(); + UI_PopCornerRadius(); + UI_PopFont(); + UI_PopFontSize(); + UI_PopOffsetX(); + UI_PopOffsetY(); + UI_PopHoverCursor(); + + UI_LayoutBox(UI->RootNode); } static void UI_RenderFrame(render_group *Group, glyph_atlas *GlyphAtlas) { - ui *UI = UI_GetState(); - UI_DrawBox(UI->RootNode, Group, GlyphAtlas); + ui *UI = UI_GetState(); + UI_DrawBox(UI->RootNode, Group, GlyphAtlas); } inline void UI_ScanForHotAndActive(ui_box *Box, b32 *FoundHot, b32 *FoundActive) { - ui *UI = UI_GetState(); - - if(AreEqual(UI->Hot, Box->Key) && (Box->Flags & UI_BoxFlag_Clickable)) - { - *FoundHot = true; - } - if(AreEqual(UI->Active, Box->Key) && (Box->Flags & UI_BoxFlag_Clickable)) - { - *FoundActive = true; - } - - if(Box->First) - { - UI_ScanForHotAndActive(Box->First, FoundHot, FoundActive); - } - if(Box->Next) - { - UI_ScanForHotAndActive(Box->Next, FoundHot, FoundActive); - } + ui *UI = UI_GetState(); + + if(AreEqual(UI->Hot, Box->Key) && (Box->Flags & UI_BoxFlag_Clickable)) + { + *FoundHot = true; + } + if(AreEqual(UI->Active, Box->Key) && (Box->Flags & UI_BoxFlag_Clickable)) + { + *FoundActive = true; + } + + if(Box->First) + { + UI_ScanForHotAndActive(Box->First, FoundHot, FoundActive); + } + if(Box->Next) + { + UI_ScanForHotAndActive(Box->Next, FoundHot, FoundActive); + } } static void UI_NewFrame(ui *UI, platform_event_list *EventList, v2 MouseP, r32 dtForFrame, glyph_atlas *GlyphAtlas) { - UI_SetState(UI); - - ArenaClear(UI->FrameArena); - - UI->EventList = EventList; - UI->dMouseP = MouseP - UI->MouseP; - UI->MouseP = MouseP; - UI->Time += dtForFrame; - - UI->GlyphAtlas = GlyphAtlas; - - // sixten: Make sure that the hot and active boxes are valid. - if(UI->RootNode) - { - b32 FoundHot = false; - b32 FoundActive = false; - UI_ScanForHotAndActive(UI->RootNode, &FoundHot, &FoundActive); - - // sixten(TODO): Do we inform the builder code about this somehow? - if(!FoundHot) - { - UI->Hot = UI_EmptyKey(); - } - if(!FoundActive) - { - UI->Active = UI_EmptyKey(); - } - } - - // sixten: Prune uncached boxes. - ui_box_bucket *FirstBucket = UI->BoxBuckets; - for(ui_box *Box = FirstBucket->First; Box != 0;) - { - if(AreEqual(Box->Key, UI_EmptyKey())) - { - ui_box *ToRemove = Box; - Box = Box->HashNext; - - DLLRemove_NP(FirstBucket->First, FirstBucket->Last, ToRemove, HashNext, HashPrev); - *ToRemove = {}; - - DLLInsertLast_NP(UI->FirstFreeBox, UI->LastFreeBox, ToRemove, HashNext, HashPrev); - } - else - { - Box = Box->HashNext; - } - } - - // sixten: Prune any unused boxes. - for(s32 BucketIndex = 0; - BucketIndex < ArrayCount(UI->BoxBuckets); - ++BucketIndex) - { - ui_box_bucket *Bucket = UI->BoxBuckets + BucketIndex; - for(ui_box *Box = Bucket->First; Box != 0;) - { - if(Box->LastFrameTouched != UI->CurrentFrame) - { - ui_box *ToRemove = Box; - Box = Box->HashNext; - - DLLRemove_NP(Bucket->First, Bucket->Last, ToRemove, HashNext, HashPrev); - *ToRemove = {}; - - DLLInsertLast_NP(UI->FirstFreeBox, UI->LastFreeBox, ToRemove, HashNext, HashPrev); - } - else - { - Box = Box->HashNext; - } - } - } - - ++UI->CurrentFrame; + UI_SetState(UI); + + ArenaClear(UI->FrameArena); + + UI->EventList = EventList; + UI->dMouseP = MouseP - UI->MouseP; + UI->MouseP = MouseP; + UI->Time += dtForFrame; + UI->BlinkTime += dtForFrame; + + UI->GlyphAtlas = GlyphAtlas; + + // sixten: Make sure that the hot and active boxes are valid. + if(UI->RootNode) + { + b32 FoundHot = false; + b32 FoundActive = false; + UI_ScanForHotAndActive(UI->RootNode, &FoundHot, &FoundActive); + + // sixten(TODO): Do we inform the builder code about this somehow? + if(!FoundHot) + { + UI->Hot = UI_EmptyKey(); + } + if(!FoundActive) + { + UI->Active = UI_EmptyKey(); + } + } + + // sixten: Prune uncached boxes. + ui_box_bucket *FirstBucket = UI->BoxBuckets; + for(ui_box *Box = FirstBucket->First; Box != 0;) + { + if(AreEqual(Box->Key, UI_EmptyKey())) + { + ui_box *ToRemove = Box; + Box = Box->HashNext; + + DLLRemove_NP(FirstBucket->First, FirstBucket->Last, ToRemove, HashNext, HashPrev); + *ToRemove = {}; + + DLLInsertLast_NP(UI->FirstFreeBox, UI->LastFreeBox, ToRemove, HashNext, HashPrev); + } + else + { + Box = Box->HashNext; + } + } + + // sixten: Prune any unused boxes. + for(s32 BucketIndex = 0; + BucketIndex < ArrayCount(UI->BoxBuckets); + ++BucketIndex) + { + ui_box_bucket *Bucket = UI->BoxBuckets + BucketIndex; + for(ui_box *Box = Bucket->First; Box != 0;) + { + if(Box->LastFrameTouched != UI->CurrentFrame) + { + ui_box *ToRemove = Box; + Box = Box->HashNext; + + DLLRemove_NP(Bucket->First, Bucket->Last, ToRemove, HashNext, HashPrev); + *ToRemove = {}; + + DLLInsertLast_NP(UI->FirstFreeBox, UI->LastFreeBox, ToRemove, HashNext, HashPrev); + } + else + { + Box = Box->HashNext; + } + } + } + + ++UI->CurrentFrame; } diff --git a/code/vn_ui.h b/code/vn_ui.h index 0946b29..25543e6 100644 --- a/code/vn_ui.h +++ b/code/vn_ui.h @@ -6,29 +6,29 @@ //- sixten: Keying struct ui_key { - u64 Value; + u64 Value; }; inline b32 AreEqual(ui_key A, ui_key B) { - b32 Result = (A.Value == B.Value); - return(Result); + b32 Result = (A.Value == B.Value); + return(Result); } //- sixten: Semantic sizing enum ui_size_kind { - UI_SizeKind_Pixels, - UI_SizeKind_TextContent, - UI_SizeKind_PercentOfParent, - UI_SizeKind_ChildrenSum, + UI_SizeKind_Pixels, + UI_SizeKind_TextContent, + UI_SizeKind_PercentOfParent, + UI_SizeKind_ChildrenSum, }; struct ui_size { - ui_size_kind Kind; - r32 Value; - r32 Strictness; + ui_size_kind Kind; + r32 Value; + r32 Strictness; }; static ui_size UI_MakeSize(ui_size_kind Kind, r32 Value, r32 Strictness); @@ -42,20 +42,19 @@ static ui_size UI_MakeSize(ui_size_kind Kind, r32 Value, r32 Strictness); //- sixten: UI core enum { - UI_BoxFlag_Clickable = (1 << 0), - UI_BoxFlag_DrawText = (1 << 1), - UI_BoxFlag_DrawBorder = (1 << 2), - UI_BoxFlag_DrawBackground = (1 << 3), - UI_BoxFlag_DrawDropShadow = (1 << 4), - UI_BoxFlag_Clip = (1 << 5), - UI_BoxFlag_HotAnimation = (1 << 6), - UI_BoxFlag_ActiveAnimation = (1 << 7), - UI_BoxFlag_OverflowX = (1 << 8), - UI_BoxFlag_OverflowY = (1 << 9), - UI_BoxFlag_FloatingX = (1 << 10), - UI_BoxFlag_FloatingY = (1 << 11), - UI_BoxFlag_Scrollable = (1 << 12), - UI_BoxFlag_AnimatePosition = (1 << 13), + UI_BoxFlag_Clickable = (1 << 0), + UI_BoxFlag_DrawText = (1 << 1), + UI_BoxFlag_DrawBorder = (1 << 2), + UI_BoxFlag_DrawBackground = (1 << 3), + UI_BoxFlag_DrawDropShadow = (1 << 4), + UI_BoxFlag_Clip = (1 << 5), + UI_BoxFlag_HotAnimation = (1 << 6), + UI_BoxFlag_ActiveAnimation = (1 << 7), + UI_BoxFlag_OverflowX = (1 << 8), + UI_BoxFlag_OverflowY = (1 << 9), + UI_BoxFlag_FloatingX = (1 << 10), + UI_BoxFlag_FloatingY = (1 << 11), + UI_BoxFlag_Scrollable = (1 << 12), }; typedef u32 ui_box_flags; @@ -64,103 +63,105 @@ typedef UI_CUSTOM_DRAW_CALLBACK(ui_custom_draw_callback); struct ui_box { - // sixten: building - ui_box *First; - ui_box *Last; - ui_box *Next; - ui_box *Prev; - ui_box *Parent; - - // sixten: hashing - ui_box *HashNext; - ui_box *HashPrev; - ui_key Key; - ui_key Seed; - u64 LastFrameTouched; - - // sixten: per-frame data - ui_box_flags Flags; - string String; - ui_size SemanticSize[Axis2_Count]; - v2 FixedP; - v4 TextColor; - v4 BackgroundColor; - v4 BorderColor; - r32 BorderThickness; - axis2 LayoutAxis; - r32 CornerRadius; - font_id Font; - r32 FontSize; - v2 Offset; - platform_cursor HoverCursor; - ui_custom_draw_callback *DrawCallback; - void *DrawCallbackData; - - v2 ComputedRelativeP; - v2 ComputedDim; - - // sixten: retained data - range2_r32 Rect; - r32 HotTransition; - r32 ActiveTransition; - v2 ApproachingRelativeP; + // sixten: building + ui_box *First; + ui_box *Last; + ui_box *Next; + ui_box *Prev; + ui_box *Parent; + + // sixten: hashing + ui_box *HashNext; + ui_box *HashPrev; + ui_key Key; + ui_key Seed; + u64 LastFrameTouched; + + // sixten: per-frame data + ui_box_flags Flags; + string String; + ui_size SemanticSize[Axis2_Count]; + v2 FixedP; + v4 TextColor; + v4 BackgroundColor; + v4 BorderColor; + r32 BorderThickness; + axis2 LayoutAxis; + r32 CornerRadius; + font_id Font; + r32 FontSize; + v2 Offset; + platform_cursor HoverCursor; + ui_custom_draw_callback *DrawCallback; + void *DrawCallbackData; + + v2 ComputedRelativeP; + v2 ComputedDim; + + // sixten: retained data + range2_r32 Rect; + r32 HotTransition; + r32 ActiveTransition; + v2 ApproachingRelativeP; }; struct ui_box_bucket { - ui_box *First; - ui_box *Last; + ui_box *First; + ui_box *Last; }; struct ui_signal { - ui_box *Box; - v2 MouseP; - v2 dMouseP; - v2 DragDelta; - v2 Scroll; - b8 Clicked; - b8 Pressed; - b8 PressedRight; - b8 Released; - b8 Hovering; - b8 Dragging; + ui_box *Box; + v2 MouseP; + v2 dMouseP; + v2 DragDelta; + v2 Scroll; + b8 Clicked; + b8 Pressed; + b8 PressedRight; + b8 Released; + b8 Hovering; + b8 Dragging; }; #include "generated/vn_ui.meta.h" struct ui { - arena *Arena; - arena *FrameArena; - ui_box *FirstFreeBox; - ui_box *LastFreeBox; - - ui_box_bucket BoxBuckets[256]; - - ui_box *RootNode; - ui_box *TooltipNode; - ui_box *ContainerNode; - - u64 CurrentFrame; - ui_key Hot; - ui_key Active; - - r64 Time; - - ui_key NextHot; - b32 NextHotSet; - - u64 DragData[8]; - v2 DragStartP; - - ui_style_stacks Stacks; - - platform_event_list *EventList; - v2 dMouseP; - v2 MouseP; - - glyph_atlas *GlyphAtlas; + arena *Arena; + arena *FrameArena; + ui_box *FirstFreeBox; + ui_box *LastFreeBox; + + ui_box_bucket BoxBuckets[256]; + + ui_box *RootNode; + ui_box *TooltipNode; + ui_box *ContainerNode; + + u64 CurrentFrame; + ui_key Hot; + ui_key Active; + + r64 Time; + r64 BlinkTime; + r64 Blink; + + ui_key NextHot; + b32 NextHotSet; + + u64 DragData[8]; + v2 DragStartP; + + ui_style_stacks Stacks; + + platform_event_list *EventList; + v2 dMouseP; + v2 MouseP; + + glyph_atlas *GlyphAtlas; }; //- sixten: State management @@ -188,6 +189,8 @@ inline void *UI_GetDragDataPointer(void); //- sixten: Helpers static r64 UI_Time(void); +static r64 UI_Blink(void); +static void UI_ResetBlink(void); //- sixten: Key functions static ui_key UI_EmptyKey(void); diff --git a/code/vn_ui_utils.cpp b/code/vn_ui_utils.cpp index 05ed5e6..6afd2c1 100644 --- a/code/vn_ui_utils.cpp +++ b/code/vn_ui_utils.cpp @@ -1,94 +1,94 @@ //- sixten: Rows and columns. inline void UI_RowBegin(u32 Flags, string Name) { - UI_SetNextLayoutAxis(Axis2_X); - ui_box *Box = UI_MakeBox(Flags, Name); - UI_PushParent(Box); + UI_SetNextLayoutAxis(Axis2_X); + ui_box *Box = UI_MakeBox(Flags, Name); + UI_PushParent(Box); } inline void UI_RowEnd(void) { - UI_PopParent(); + UI_PopParent(); } inline void UI_ColumnBegin(u32 Flags, string Name) { - UI_SetNextLayoutAxis(Axis2_Y); - ui_box *Box = UI_MakeBox(Flags, Name); - UI_PushParent(Box); + UI_SetNextLayoutAxis(Axis2_Y); + ui_box *Box = UI_MakeBox(Flags, Name); + UI_PushParent(Box); } inline void UI_ColumnEnd(void) { - UI_PopParent(); + UI_PopParent(); } //- sixten: Compositions inline void UI_PushAxisSize(axis2 Axis, ui_size Size) { - if(Axis == Axis2_X) - { - UI_PushWidth(Size); - } - else - { - UI_PushHeight(Size); - } + if(Axis == Axis2_X) + { + UI_PushWidth(Size); + } + else + { + UI_PushHeight(Size); + } } inline void UI_PopAxisSize(axis2 Axis) { - if(Axis == Axis2_X) - { - UI_PopWidth(); - } - else - { - UI_PopHeight(); - } + if(Axis == Axis2_X) + { + UI_PopWidth(); + } + else + { + UI_PopHeight(); + } } inline void UI_SetNextAxisSize(axis2 Axis, ui_size Size) { - if(Axis == Axis2_X) - { - UI_SetNextWidth(Size); - } - else - { - UI_SetNextHeight(Size); - } + if(Axis == Axis2_X) + { + UI_SetNextWidth(Size); + } + else + { + UI_SetNextHeight(Size); + } } //- sixten: Spacing static ui_box *UI_NamedSpacer(ui_size Size, string String) { - ui_box *Parent = UI_TopParent(); - UI_SetNextAxisSize(Parent->LayoutAxis, Size); - - ui_box *Box = UI_MakeBox(0, String); - return(Box); + ui_box *Parent = UI_TopParent(); + UI_SetNextAxisSize(Parent->LayoutAxis, Size); + + ui_box *Box = UI_MakeBox(0, String); + return(Box); } static ui_box *UI_NamedSpacerF(ui_size Size, char *Format, ...) { - temporary_memory Scratch = GetScratch(0, 0); - - va_list Arguments; - va_start(Arguments, Format); - string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); - va_end(Arguments); - - ui_box *Box = UI_NamedSpacer(Size, String); - - ReleaseScratch(Scratch); - - return(Box); + temp Scratch = GetScratch(0, 0); + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); + va_end(Arguments); + + ui_box *Box = UI_NamedSpacer(Size, String); + + ReleaseScratch(Scratch); + + return(Box); } static ui_box *UI_Spacer(ui_size Size) { - return(UI_NamedSpacer(Size, StrLit(""))); + return(UI_NamedSpacer(Size, StrLit(""))); } //- sixten: Scrollable regions @@ -97,340 +97,504 @@ static ui_box *UI_Spacer(ui_size Size) //- sixten: Common widgets static ui_box *UI_Label(string String) { - ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); - return(Box); + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); + return(Box); } static ui_box *UI_LabelF(char *Format, ...) { - temporary_memory Scratch = GetScratch(0, 0); - - va_list Arguments; - va_start(Arguments, Format); - string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); - va_end(Arguments); - - ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); - - ReleaseScratch(Scratch); - - return(Box); + temp Scratch = GetScratch(0, 0); + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); + va_end(Arguments); + + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); + + ReleaseScratch(Scratch); + + return(Box); } 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| - UI_BoxFlag_HotAnimation| - UI_BoxFlag_ActiveAnimation| - UI_BoxFlag_Clickable, - String); - ui_signal Signal = UI_SignalFromBox(Box); - return(Signal); + UI_SetNextHoverCursor(PlatformCursor_Hand); + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| + UI_BoxFlag_DrawBackground| + UI_BoxFlag_DrawBorder| + UI_BoxFlag_HotAnimation| + UI_BoxFlag_ActiveAnimation| + UI_BoxFlag_Clickable, + String); + ui_signal Signal = UI_SignalFromBox(Box); + return(Signal); } 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); - va_end(Arguments); - - ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| - UI_BoxFlag_DrawBackground| - UI_BoxFlag_DrawBorder| - UI_BoxFlag_HotAnimation| - UI_BoxFlag_ActiveAnimation| - UI_BoxFlag_Clickable, - String); - ui_signal Signal = UI_SignalFromBox(Box); - - ReleaseScratch(Scratch); - - return(Signal); + temp Scratch = GetScratch(0, 0); + + UI_SetNextHoverCursor(PlatformCursor_Hand); + + va_list Arguments; + va_start(Arguments, Format); + string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); + va_end(Arguments); + + ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| + UI_BoxFlag_DrawBackground| + UI_BoxFlag_DrawBorder| + UI_BoxFlag_HotAnimation| + UI_BoxFlag_ActiveAnimation| + UI_BoxFlag_Clickable, + String); + ui_signal Signal = UI_SignalFromBox(Box); + + ReleaseScratch(Scratch); + + return(Signal); } 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 = AC_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked); - - v4 TextColor = UI_TopTextColor(); - TextColor.a = OpacityTransition; - - UI_CornerRadius(2) UI_Size(UI_Em(1, 1), UI_Em(1, 1)) UI_Font(Font_Icons) UI_TextColor(TextColor) - UI_MakeBoxF(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawText, "%U", FontIcon_Cancel); - - UI_Size(UI_TextContent(15, 1), UI_Em(1, 1)) UI_Label(String); - } - - ui_signal Signal = UI_SignalFromBox(ContainerBox); - - if(Signal.Pressed) - { - *Checked = !*Checked; - } - - return(Signal); + 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 = AC_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked); + + v4 TextColor = UI_TopTextColor(); + TextColor.a = OpacityTransition; + + UI_CornerRadius(2) UI_Size(UI_Em(1, 1), UI_Em(1, 1)) UI_Font(Font_Icons) UI_TextColor(TextColor) + UI_MakeBoxF(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawText, "%U", FontIcon_Cancel); + + UI_Size(UI_TextContent(15, 1), UI_Em(1, 1)) UI_Label(String); + } + + ui_signal Signal = UI_SignalFromBox(ContainerBox); + + if(Signal.Pressed) + { + *Checked = !*Checked; + } + + return(Signal); } //- sixten: Scrollable regions static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset) { - ui_signal Signal; - - UI_SetNextAxisSize(Axis, UI_Percent(1, 0)); - UI_SetNextAxisSize(Opposite(Axis), UI_Em(1, 1)); - UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); - UI_SetNextLayoutAxis(Axis); - UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, Name)) - { - UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); - UI_SetNextFont(Font_Icons); - UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_UpDir:FontIcon_LeftDir); - - UI_Spacer(UI_Pixels(Offset, 1)); - - UI_SetNextCornerRadius(4.0f); - UI_SetNextAxisSize(Axis, UI_Pixels(Size, 1)); - UI_SetNextAxisSize(Opposite(Axis), UI_Percent(1, 1)); - - Signal = UI_SignalFromBox(UI_MakeBox(UI_BoxFlag_DrawBorder | - UI_BoxFlag_DrawBackground | - UI_BoxFlag_Clickable | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation, - StrLit("Slider"))); - - UI_Spacer(UI_Percent(1, 0)); - - UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); - UI_SetNextFont(Font_Icons); - UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_DownDir:FontIcon_RightDir); - } - - return(Signal); + ui_signal Signal; + + UI_SetNextAxisSize(Axis, UI_Percent(1, 0)); + UI_SetNextAxisSize(Opposite(Axis), UI_Em(1, 1)); + UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); + UI_SetNextLayoutAxis(Axis); + UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, Name)) + { + UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); + UI_SetNextFont(Font_Icons); + UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_UpDir:FontIcon_LeftDir); + + UI_Spacer(UI_Pixels(Offset, 1)); + + UI_SetNextCornerRadius(4.0f); + UI_SetNextAxisSize(Axis, UI_Pixels(Size, 1)); + UI_SetNextAxisSize(Opposite(Axis), UI_Percent(1, 1)); + + Signal = UI_SignalFromBox(UI_MakeBox(UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_Clickable | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation, + StrLit("Slider"))); + + UI_Spacer(UI_Percent(1, 0)); + + UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); + UI_SetNextFont(Font_Icons); + UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_DownDir:FontIcon_RightDir); + } + + return(Signal); } static void UI_ScrollBegin(r32 *X, r32 *Y, ui_box_flags Flags, string Name) { - b32 AllowOnX = (X != 0); - b32 AllowOnY = (Y != 0); - - UI_RowBegin(Flags, Name); - { - UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); - UI_ColumnBegin(); - { - u32 ScrollFlags = 0; - if(AllowOnX) - { - ScrollFlags |= UI_BoxFlag_OverflowX; - UI_SetNextOffsetX(-AC_AnimateValueF(*X, *X, 0.2f, "Scroll %S X", Name)); - } - - if(AllowOnY) - { - ScrollFlags |= UI_BoxFlag_OverflowY; - UI_SetNextOffsetY(-AC_AnimateValueF(*Y, *Y, 0.2f, "Scroll %S Y", Name)); - } - - 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(ScrollFlags, StrLit("Scrollable Box")); - - UI_PushParent(ScrollableBox); - } - } + b32 AllowOnX = (X != 0); + b32 AllowOnY = (Y != 0); + + UI_RowBegin(Flags, Name); + { + UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); + UI_ColumnBegin(); + { + u32 ScrollFlags = 0; + if(AllowOnX) + { + ScrollFlags |= UI_BoxFlag_OverflowX; + UI_SetNextOffsetX(-AC_AnimateValueF(*X, *X, 0.2f, "Scroll %S X", Name)); + } + + if(AllowOnY) + { + ScrollFlags |= UI_BoxFlag_OverflowY; + UI_SetNextOffsetY(-AC_AnimateValueF(*Y, *Y, 0.2f, "Scroll %S Y", Name)); + } + + 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(ScrollFlags, StrLit("Scrollable Box")); + + UI_PushParent(ScrollableBox); + } + } } static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags, string Name) { - b32 AllowOnX = (X != 0); - b32 AllowOnY = (Y != 0); - - r32 ScrollAmount = UI_TopFontSize()*4.0f; - - ui_box *ScrollableBox = UI_TopParent(); - ui_signal ScrollableBoxSignal = UI_SignalFromBox(ScrollableBox); - { - { - UI_PopParent(); - - if(AllowOnX) - { - r32 TotalWidth = UI_CalculateBoxSize(ScrollableBox, Axis2_X); - r32 ViewWidth = UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_X); - - if(ViewWidth / TotalWidth < 1) - { - r32 TotalScrollWidth = ViewWidth - ScrollableBox->FontSize*2; - r32 ScrollScale = TotalScrollWidth / TotalWidth; - - // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. - r32 ScrollWidth = ViewWidth*ScrollScale; - r32 ScrollOffset = (*X)*ScrollScale; - - ui_signal Signal = UI_Scrollbar(Axis2_X, StrLit("Scroll X"), ScrollWidth, ScrollOffset); - { - if(Signal.Dragging) - { - if(Signal.Pressed) - { - UI_StoreDragR32(*X); - } - - r32 StartOffset = UI_GetDragR32(); - r32 EndOffset = StartOffset + Signal.DragDelta.x/ScrollScale; - - *X = EndOffset; - } - else - { - for(platform_event *Event = UI_EventList()->First; - Event != 0; - Event = Event->Next) - { - if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.x != 0) - { - *X -= Event->Scroll.x*ScrollAmount; - Platform_ConsumeEvent(UI_EventList(), Event); - } - } - } - } - } - *X = Clamp(*X, 0, Max(0.0, TotalWidth - ViewWidth)); - } - } - UI_ColumnEnd(); - - if(AllowOnY) - { - UI_SetNextSize(UI_ChildrenSum(1, 1), UI_Percent(1, 0)); - UI_Column() - { - r32 TotalHeight = UI_CalculateBoxSize(ScrollableBox, Axis2_Y); - r32 ViewHeight = DimOfRange(ScrollableBox->Parent->Parent->Rect).y;//UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_Y); - - if(ViewHeight / TotalHeight < 1) - { - r32 TotalScrollHeight = ViewHeight - ScrollableBox->FontSize*2; - r32 ScrollScale = TotalScrollHeight / TotalHeight; - - // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. - r32 ScrollHeight = ViewHeight*ScrollScale; - r32 ScrollOffset = (*Y)*ScrollScale; - - ui_signal Signal = UI_Scrollbar(Axis2_Y, StrLit("Scroll Y"), ScrollHeight, ScrollOffset); - { - if(Signal.Dragging) - { - if(Signal.Pressed) - { - UI_StoreDragR32(*Y); - } - - r32 StartOffset = UI_GetDragR32(); - r32 EndOffset = StartOffset + Signal.DragDelta.y/ScrollScale; - - *Y = EndOffset; - } - else - { - for(platform_event *Event = UI_EventList()->First; - Event != 0; - Event = Event->Next) - { - if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0) - { - *Y -= Event->Scroll.y*ScrollAmount; - Platform_ConsumeEvent(UI_EventList(), Event); - } - } - } - } - } - *Y = Clamp(*Y, 0, Max(0.0, TotalHeight - ViewHeight)); - } - // sixten: Add padding - if(AllowOnX && AllowOnY) - { - UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); - UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); - UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Padding")); - } - } - } - UI_RowEnd(); + b32 AllowOnX = (X != 0); + b32 AllowOnY = (Y != 0); + + r32 ScrollAmount = UI_TopFontSize()*4.0f; + + ui_box *ScrollableBox = UI_TopParent(); + ui_signal ScrollableBoxSignal = UI_SignalFromBox(ScrollableBox); + { + { + UI_PopParent(); + + if(AllowOnX) + { + r32 TotalWidth = UI_CalculateBoxSize(ScrollableBox, Axis2_X); + r32 ViewWidth = UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_X); + + if(ViewWidth / TotalWidth < 1) + { + r32 TotalScrollWidth = ViewWidth - ScrollableBox->FontSize*2; + r32 ScrollScale = TotalScrollWidth / TotalWidth; + + // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. + r32 ScrollWidth = ViewWidth*ScrollScale; + r32 ScrollOffset = (*X)*ScrollScale; + + ui_signal Signal = UI_Scrollbar(Axis2_X, StrLit("Scroll X"), ScrollWidth, ScrollOffset); + { + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragR32(*X); + } + + r32 StartOffset = UI_GetDragR32(); + r32 EndOffset = StartOffset + Signal.DragDelta.x/ScrollScale; + + *X = EndOffset; + } + else + { + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.x != 0) + { + *X -= Event->Scroll.x*ScrollAmount; + Platform_ConsumeEvent(UI_EventList(), Event); + } + } + } + } + } + *X = Clamp(*X, 0, Max(0.0, TotalWidth - ViewWidth)); + } + } + UI_ColumnEnd(); + + if(AllowOnY) + { + UI_SetNextSize(UI_ChildrenSum(1, 1), UI_Percent(1, 0)); + UI_Column() + { + r32 TotalHeight = UI_CalculateBoxSize(ScrollableBox, Axis2_Y); + r32 ViewHeight = DimOfRange(ScrollableBox->Parent->Parent->Rect).y;//UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_Y); + + if(ViewHeight / TotalHeight < 1) + { + r32 TotalScrollHeight = ViewHeight - ScrollableBox->FontSize*2; + r32 ScrollScale = TotalScrollHeight / TotalHeight; + + // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. + r32 ScrollHeight = ViewHeight*ScrollScale; + r32 ScrollOffset = (*Y)*ScrollScale; + + ui_signal Signal = UI_Scrollbar(Axis2_Y, StrLit("Scroll Y"), ScrollHeight, ScrollOffset); + { + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragR32(*Y); + } + + r32 StartOffset = UI_GetDragR32(); + r32 EndOffset = StartOffset + Signal.DragDelta.y/ScrollScale; + + *Y = EndOffset; + } + else + { + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0) + { + *Y -= Event->Scroll.y*ScrollAmount; + Platform_ConsumeEvent(UI_EventList(), Event); + } + } + } + } + } + *Y = Clamp(*Y, 0, Max(0.0, TotalHeight - ViewHeight)); + } + // sixten: Add padding + if(AllowOnX && AllowOnY) + { + UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); + UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); + UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Padding")); + } + } + } + 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); + 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); - } + 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); + } +} + +struct ui_line_edit_callback_data +{ + text_edit_state State; + b32 Focus; +}; + +UI_CUSTOM_DRAW_CALLBACK(UI_LineEditCallback) +{ + ui_line_edit_callback_data *EditData = (ui_line_edit_callback_data *)Data; + s64 ClampedCursor = Clamp(0, Box->String.Count, EditData->State.Cursor); + s64 ClampedMark = Clamp(0, Box->String.Count, EditData->State.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, "UI Input Cursor %p", Box); + r32 MarkerX = AC_AnimateValueF(TargetMarkerX, 0, 0.175, "UI 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); + r32 FocusT = AC_AnimateValueF(EditData->Focus, 0, 0.175, "UI Input Focus %p", Box); + + v2 P = Box->Rect.Min + Offset; + v4 Color = SetAlpha(V4(0.3, 1, 0.3, 0.7), UI_Blink()*FocusT); + PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0); + } +} + +// sixten(NOTE): this function only kinda works. it is meant for single line text, if that is any indication of its scope. +static s64 UI_TextIndexFromP(ui_box *Box, v2_r32 P) +{ + // sixten: yes, usually I don't like doing this, but hey there is only one glyph atlas used throughout the entire application, nobody will notice ._. + glyph_atlas *Atlas = UI_GetState()->GlyphAtlas; + + s64 Result = 0; + // sixten(NOTE): from now on, we ignore the y-axis + r32 Dim = CalculateRasterizedTextWidth(UI_GetState()->GlyphAtlas, Box->Font, Box->FontSize, Box->String); + r32 Offset = P.x - Box->Rect.Min.x - (Box->Rect.Max.x - Box->Rect.Min.x - Dim)*0.5; + + r32 Advance = 0; + u8 *TextBegin = Box->String.Data; + u8 *TextEnd = TextBegin+Box->String.Count; + for(u8 *Byte = TextBegin; Byte < TextEnd;) + { + string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte); + Byte += Decode.Size; + u32 Codepoint = Decode.Codepoint; + + glyph *Glyph = GetGlyph(Atlas, Box->Font, Codepoint, Box->FontSize*Font_Oversample, GetSubpixelSegmentAtP(Advance*Font_Oversample)); + Assert(Glyph); + + Advance += Glyph->Advance/Font_Oversample; + if(Advance > Offset) + { + break; + } + else + { + Result += 1; + } + } + return(Result); +} + +static ui_signal UI_LineEdit(text_edit_state *State, u64 BufferSize, u8 *Buffer, s64 *BufferUsed, string String, b32 Focused) +{ + temp Scratch = GetScratch(); + if(Focused) + { + for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next) + { + if((Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) && (Event->Codepoint != '/' && Event->Codepoint != '\\')) + { + text_action Action = SingleLineTextActionFromEvent(Event); + if(IsValid(&Action)) + { + text_op Op = TextOpFromAction(Scratch.Arena, MakeString(Buffer, *BufferUsed), State, &Action); + if(Op.NewCursor >= 0 && Op.NewMark >= 0) + { + string Left = MakeString(Buffer, Op.Range.Min); + string Right = MakeString(Buffer + Op.Range.Max, *BufferUsed - Op.Range.Max); + + u64 NewStringSize = Left.Count + Right.Count + Op.ReplaceString.Count; + char *NewString = PushArray(Scratch.Arena, char, NewStringSize); + Copy(NewString, Left.Data, Left.Count); + Copy(NewString + Left.Count, Op.ReplaceString.Data, Op.ReplaceString.Count); + Copy(NewString + Left.Count + Op.ReplaceString.Count, Right.Data, Right.Count); + *BufferUsed = Minimum(BufferSize, NewStringSize); + Copy(Buffer, NewString, *BufferUsed); + + State->Cursor = Minimum(Op.NewCursor, *BufferUsed); + State->Mark = Minimum(Op.NewMark, *BufferUsed); + Platform_ConsumeEvent(UI_EventList(), Event); + + UI_ResetBlink(); + } + } + } + } + } + + UI_SetNextHoverCursor(PlatformCursor_IBeam); + ui_box *Container = UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, String); + ui_box *InputBox = 0; + + UI_Parent(Container) UI_Height(UI_Percent(1, 1)) + { + UI_SetNextWidth(UI_TextContent(15, 1)); + InputBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Text Input")); + UI_EquipBoxText(InputBox, MakeString(Buffer, *BufferUsed)); + ui_line_edit_callback_data *Data = PushStruct(UI_FrameArena(), ui_line_edit_callback_data); + Data->State = *State; + Data->Focus = Focused; + UI_EquipBoxCustomDrawCallback(InputBox, UI_LineEditCallback, Data); + + UI_Spacer(UI_Percent(1, 0)); + } + ReleaseScratch(Scratch); + + //- sixten: handle mouse dragging + ui_signal Signal = UI_SignalFromBox(Container); + if(Signal.Dragging) + { + if(Signal.Pressed) + { + State->Cursor = State->Mark = UI_TextIndexFromP(InputBox, UI_MouseP()); + } + else + { + State->Cursor = UI_TextIndexFromP(InputBox, UI_MouseP()); + } + } + + return(Signal); } \ No newline at end of file diff --git a/code/vn_ui_utils.h b/code/vn_ui_utils.h index 802acc1..65196bb 100644 --- a/code/vn_ui_utils.h +++ b/code/vn_ui_utils.h @@ -57,5 +57,6 @@ 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); +static b32 UI_LineEdit(text_edit_state *State, u64 BufferSize, u8 *Buffer, u64 *BufferUsed, string String, b32 Focused); #endif //VN_UI_UTILS_H diff --git a/code/vn_workspace.cpp b/code/vn_workspace.cpp index 5c366f5..41151c6 100644 --- a/code/vn_workspace.cpp +++ b/code/vn_workspace.cpp @@ -9,834 +9,823 @@ per_thread workspace *ThreadLocal_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_ViewKind_FileLister}, - {Key_P, PlatformModifier_Ctrl|PlatformModifier_Shift, W_Command_OpenView, W_ViewKind_Settings}, - + {Key_P, PlatformModifier_Ctrl, W_Command_SplitPanelHorizontal}, + {Key_L, PlatformModifier_Ctrl, W_Command_SplitPanelVertical}, + + {Key_O, PlatformModifier_Ctrl, W_Command_OpenView, W_ViewKind_FileLister}, + {Key_P, PlatformModifier_Ctrl|PlatformModifier_Shift, W_Command_OpenView, W_ViewKind_Settings}, + #if VN_INTERNAL - {Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, W_Command_ToggleRenderUIDebugRects}, + {Key_U, PlatformModifier_Ctrl|PlatformModifier_Shift, W_Command_ToggleRenderUIDebugRects}, #endif }; static void W_SetState(workspace *Workspace) { - ThreadLocal_Workspace = Workspace; + ThreadLocal_Workspace = Workspace; } static workspace *W_GetState(void) { - return(ThreadLocal_Workspace); + return(ThreadLocal_Workspace); } //- sixten: Commands static void W_IssueCommand(workspace_command_sig *Sig, u64 Argument) { - workspace_command *Result = 0; - - workspace *Workspace = W_GetState(); - - if(Workspace->FirstFreeCommand) - { - Result = Workspace->FirstFreeCommand; - DLLRemove(Workspace->FirstFreeCommand, Workspace->LastFreeCommand, Result); - } - - if(!Result) - { - Result = PushStruct(Workspace->CommandArena, workspace_command); - } - - Result->Command = Sig; - Result->Argument = Argument; - DLLInsertLast(Workspace->FirstCommand, Workspace->LastCommand, Result); + workspace_command *Result = 0; + + workspace *Workspace = W_GetState(); + + if(Workspace->FirstFreeCommand) + { + Result = Workspace->FirstFreeCommand; + DLLRemove(Workspace->FirstFreeCommand, Workspace->LastFreeCommand, Result); + } + + if(!Result) + { + Result = PushStruct(Workspace->CommandArena, workspace_command); + } + + Result->Command = Sig; + Result->Argument = Argument; + DLLInsertLast(Workspace->FirstCommand, Workspace->LastCommand, Result); } static void W_ProcessCommands(void) { - workspace *Workspace = W_GetState(); - - workspace_command *Command = Workspace->FirstCommand; - while(Command != 0) - { - Command->Command(Command->Argument); - - workspace_command *ToRemove = Command; - Command = Command->Next; - - DLLRemove(Workspace->FirstCommand, Workspace->LastCommand, ToRemove); - Fill(ToRemove, 0, sizeof(workspace_command)); - DLLInsertLast(Workspace->FirstFreeCommand, Workspace->LastFreeCommand, ToRemove); - } - - ArenaClear(Workspace->CommandDataArena); + workspace *Workspace = W_GetState(); + + workspace_command *Command = Workspace->FirstCommand; + while(Command != 0) + { + Command->Command(Command->Argument); + + workspace_command *ToRemove = Command; + Command = Command->Next; + + DLLRemove(Workspace->FirstCommand, Workspace->LastCommand, ToRemove); + Fill(ToRemove, 0, sizeof(workspace_command)); + DLLInsertLast(Workspace->FirstFreeCommand, Workspace->LastFreeCommand, ToRemove); + } + + ArenaClear(Workspace->CommandDataArena); } static void W_ProcessKeyBinds() { - workspace *Workspace = W_GetState(); - - platform_event_list *EventList = Workspace->EventList; - - for(platform_event *Event = EventList->First; - Event != 0; - Event = Event->Next) - { - if(Event->Type == PlatformEvent_Press) - { - for(s32 KeybindIndex = 0; - KeybindIndex < ArrayCount(Workspace_Keybinds); - ++KeybindIndex) - { - workspace_keybind *Keybind = Workspace_Keybinds + KeybindIndex; - if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers)) - { - W_IssueCommand(Keybind->Command, Keybind->Argument); - } - } - } - } + workspace *Workspace = W_GetState(); + + platform_event_list *EventList = Workspace->EventList; + + for(platform_event *Event = EventList->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press) + { + for(s32 KeybindIndex = 0; + KeybindIndex < ArrayCount(Workspace_Keybinds); + ++KeybindIndex) + { + workspace_keybind *Keybind = Workspace_Keybinds + KeybindIndex; + if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers)) + { + W_IssueCommand(Keybind->Command, Keybind->Argument); + } + } + } + } } //- sixten: Builder code static ui_signal W_BuildToolbarButton(char *Text, workspace_toolbar_menu Menu) { - workspace *Workspace = W_GetState(); - - UI_SetNextWidth(UI_TextContent(20, 1)); - UI_SetNextHeight(UI_Pixels(30, 1)); - UI_SetNextCornerRadius(4); - UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); - - ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawBorder | - UI_BoxFlag_DrawText | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation | - UI_BoxFlag_Clickable, - Text); - - ui_signal Signal = UI_SignalFromBox(Box); - - if(Workspace->Menu == W_ToolbarMenu_None) - { - if(Signal.Clicked) - { - Workspace->Menu = Menu; - Workspace->MenuT = 0; - } - } - else - { - if(Signal.Hovering) - { - if(Workspace->Menu != Menu) - { - Workspace->MenuT = 0; - } - - Workspace->Menu = Menu; - Workspace->MenuP = V2(Box->Rect.Min.x, Box->Rect.Max.y); - } - } - - return(Signal); + workspace *Workspace = W_GetState(); + + UI_SetNextWidth(UI_TextContent(20, 1)); + UI_SetNextHeight(UI_Pixels(30, 1)); + UI_SetNextCornerRadius(4); + UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); + + ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawText | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation | + UI_BoxFlag_Clickable, + Text); + + ui_signal Signal = UI_SignalFromBox(Box); + + if(Workspace->Menu == W_ToolbarMenu_None) + { + if(Signal.Clicked) + { + Workspace->Menu = Menu; + Workspace->MenuT = 0; + } + } + else + { + if(Signal.Hovering) + { + if(Workspace->Menu != Menu) + { + Workspace->MenuT = 0; + } + + Workspace->Menu = Menu; + Workspace->MenuP = V2(Box->Rect.Min.x, Box->Rect.Max.y); + } + } + + return(Signal); } 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 | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation | - UI_BoxFlag_Clickable, - "Menu Item %s", Text); - - UI_Parent(Box) - { - UI_Width(UI_Pixels(25, 1)) UI_Font(Font_Icons) UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Icon); - UI_Width(UI_TextContent(5, 1)) UI_MakeBoxF(UI_BoxFlag_DrawText, Text); - UI_Spacer(UI_Percent(1, 0)); - - UI_TextColor(V4(0.5, 0.5, 0.5, 1.0)) UI_Width(UI_TextContent(15, 1)) - UI_MakeBoxF(UI_BoxFlag_DrawText, Shortcut); - } - - ReleaseScratch(Scratch); - - ui_signal Signal = UI_SignalFromBox(Box); - return(Signal); + temp Scratch = GetScratch(0, 0); + + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHoverCursor(PlatformCursor_Hand); + + ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation | + UI_BoxFlag_Clickable, + "Menu Item %s", Text); + + UI_Parent(Box) + { + UI_Width(UI_Pixels(25, 1)) UI_Font(Font_Icons) UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Icon); + UI_Width(UI_TextContent(5, 1)) UI_MakeBoxF(UI_BoxFlag_DrawText, Text); + UI_Spacer(UI_Percent(1, 0)); + + UI_TextColor(V4(0.5, 0.5, 0.5, 1.0)) UI_Width(UI_TextContent(15, 1)) + UI_MakeBoxF(UI_BoxFlag_DrawText, Shortcut); + } + + ReleaseScratch(Scratch); + + ui_signal Signal = UI_SignalFromBox(Box); + return(Signal); } static void W_BuildToolbar(void) { - workspace *Workspace = W_GetState(); - - UI_SetNextLayoutAxis(Axis2_X); - UI_SetNextHeight(UI_Pixels(30, 1)); - UI_SetNextBackgroundColor(Theme_BackgroundColor); - - ui_box *ToolbarBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, - "Workspace Toolbar"); - - UI_Parent(ToolbarBox) - { - W_BuildToolbarButton("Panel", W_ToolbarMenu_Panel); - W_BuildToolbarButton("View", W_ToolbarMenu_View); - W_BuildToolbarButton("Window", W_ToolbarMenu_Window); - } - - if(Workspace->Menu != W_ToolbarMenu_None) - { - r32 MenuT = Workspace->MenuT; - - UI_SetNextTooltip(); - UI_SetNextFixedX(Workspace->MenuP.x); - UI_SetNextFixedY(Workspace->MenuP.y); - UI_SetNextLayoutAxis(Axis2_Y); - UI_SetNextWidth(UI_Pixels(250, 1)); - UI_SetNextHeight(UI_ChildrenSum(MenuT, 1)); - ui_box *Dropdown = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawDropShadow | - UI_BoxFlag_Clip | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY, - "Workspace Dropdown"); - - UI_Parent(Dropdown) - UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) - UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) - UI_CornerRadius(2) - UI_Size(UI_Percent(1, 1), UI_Pixels(25, 1)) - { - if(Workspace->Menu == W_ToolbarMenu_Panel) - { - if(W_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked) - { - W_IssueCommand(W_Command_SplitPanelHorizontal); - Workspace->Menu = W_ToolbarMenu_None; - } - if(W_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked) - { - W_IssueCommand(W_Command_SplitPanelVertical); - Workspace->Menu = W_ToolbarMenu_None; - } - } - else if(Workspace->Menu == W_ToolbarMenu_View) - { - workspace_view_kind ViewKind = W_ViewKind_None; - if(W_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked) {ViewKind = W_ViewKind_Startup;} - if(W_BuildMenuItem(FontIcon_Pencil, "File Lister", "Ctrl + O").Clicked) {ViewKind = W_ViewKind_FileLister;} - if(W_BuildMenuItem(FontIcon_Users, "Navigation Editor", "").Clicked) {ViewKind = W_ViewKind_NavEditor;} - if(W_BuildMenuItem(FontIcon_Terminal, "Scene View", "").Clicked) {ViewKind = W_ViewKind_SceneView;} - if(W_BuildMenuItem(FontIcon_Wrench, "Settings", "Ctrl + Shift + P").Clicked) {ViewKind = W_ViewKind_Settings;} - - if(ViewKind != W_ViewKind_None) - { - W_IssueCommand(W_Command_OpenView, ViewKind); - Workspace->Menu = W_ToolbarMenu_None; - } - } - else if(Workspace->Menu == W_ToolbarMenu_Window) - - if(W_BuildMenuItem(FontIcon_WindowMaximize, "ToggleFullscreen", "Alt + Enter").Clicked) - { - Platform.ToggleFullscreen(); - Workspace->Menu = W_ToolbarMenu_None; - } - } - - AC_AnimateValueDirect(1, 0.1, &Workspace->MenuT); - - // sixten: Unless the mouse press was captured, we close the menu. - if(Platform_KeyPress(Workspace->EventList, Key_MouseLeft)) - { - Workspace->Menu = W_ToolbarMenu_None; - } - } + workspace *Workspace = W_GetState(); + + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHeight(UI_Pixels(30, 1)); + UI_SetNextBackgroundColor(Theme_BackgroundColor); + + ui_box *ToolbarBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, + "Workspace Toolbar"); + + UI_Parent(ToolbarBox) + { + W_BuildToolbarButton("Panel", W_ToolbarMenu_Panel); + W_BuildToolbarButton("View", W_ToolbarMenu_View); + W_BuildToolbarButton("Window", W_ToolbarMenu_Window); + } + + if(Workspace->Menu != W_ToolbarMenu_None) + { + r32 MenuT = Workspace->MenuT; + + UI_SetNextTooltip(); + UI_SetNextFixedX(Workspace->MenuP.x); + UI_SetNextFixedY(Workspace->MenuP.y); + UI_SetNextLayoutAxis(Axis2_Y); + UI_SetNextWidth(UI_Pixels(250, 1)); + UI_SetNextHeight(UI_ChildrenSum(MenuT, 1)); + ui_box *Dropdown = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawDropShadow | + UI_BoxFlag_Clip | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, + "Workspace Dropdown"); + + UI_Parent(Dropdown) + UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) + UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) + UI_CornerRadius(2) + UI_Size(UI_Percent(1, 1), UI_Pixels(25, 1)) + { + if(Workspace->Menu == W_ToolbarMenu_Panel) + { + if(W_BuildMenuItem(FontIcon_ResizeHorizontal, "Split Horizontal", "Ctrl + P").Clicked) + { + W_IssueCommand(W_Command_SplitPanelHorizontal); + Workspace->Menu = W_ToolbarMenu_None; + } + if(W_BuildMenuItem(FontIcon_ResizeVertical, "Split Vertical", "Ctrl + L").Clicked) + { + W_IssueCommand(W_Command_SplitPanelVertical); + Workspace->Menu = W_ToolbarMenu_None; + } + } + else if(Workspace->Menu == W_ToolbarMenu_View) + { + workspace_view_kind ViewKind = W_ViewKind_None; + if(W_BuildMenuItem(FontIcon_None, "Welcome", "").Clicked) {ViewKind = W_ViewKind_Startup;} + if(W_BuildMenuItem(FontIcon_FolderOpen, "File Lister", "Ctrl + O").Clicked) {ViewKind = W_ViewKind_FileLister;} + if(W_BuildMenuItem(FontIcon_Terminal, "Scene View", "").Clicked) {ViewKind = W_ViewKind_SceneView;} + if(W_BuildMenuItem(FontIcon_Wrench, "Settings", "Ctrl + Shift + P").Clicked) {ViewKind = W_ViewKind_Settings;} + + if(ViewKind != W_ViewKind_None) + { + W_IssueCommand(W_Command_OpenView, ViewKind); + Workspace->Menu = W_ToolbarMenu_None; + } + } + else if(Workspace->Menu == W_ToolbarMenu_Window) + + if(W_BuildMenuItem(FontIcon_WindowMaximize, "ToggleFullscreen", "Alt + Enter").Clicked) + { + Platform.ToggleFullscreen(); + Workspace->Menu = W_ToolbarMenu_None; + } + } + + AC_AnimateValueDirect(1, 0.1, &Workspace->MenuT); + + // sixten: Unless the mouse press was captured, we close the menu. + if(Platform_KeyPress(Workspace->EventList, Key_MouseLeft)) + { + Workspace->Menu = W_ToolbarMenu_None; + } + } } //- sixten: Panels static workspace_panel *W_CreateNewPanel(workspace_panel *Parent) { - workspace_panel *Result = 0; - - workspace *Workspace = W_GetState(); - - if(DLLIsEmpty(Workspace->FirstFreePanel)) - { - Result = PushStruct(Workspace->PanelArena, workspace_panel); - } - else - { - Result = Workspace->FirstFreePanel; - DLLRemove(Workspace->FirstFreePanel, Workspace->LastFreePanel, Result); - - *Result = {}; - } - - Result->Parent = Parent; - - return(Result); + workspace_panel *Result = 0; + + workspace *Workspace = W_GetState(); + + if(DLLIsEmpty(Workspace->FirstFreePanel)) + { + Result = PushStruct(Workspace->PanelArena, workspace_panel); + } + else + { + Result = Workspace->FirstFreePanel; + DLLRemove(Workspace->FirstFreePanel, Workspace->LastFreePanel, Result); + + *Result = {}; + } + + Result->Parent = Parent; + + return(Result); } static void W_DeletePanel(workspace_panel *Panel) { - workspace *Workspace = W_GetState(); - - if(Workspace->CurrentPanel == Panel) - { - Workspace->CurrentPanel = 0; - } - - *Panel = {}; - DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, Panel); + workspace *Workspace = W_GetState(); + + if(Workspace->CurrentPanel == Panel) + { + Workspace->CurrentPanel = 0; + } + + *Panel = {}; + DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, Panel); } static void W_PanelViewPush(workspace_panel_view_list *List, workspace_panel_view *PanelView) { - DLLInsertLast(List->First, List->Last, PanelView); - List->Count += 1; + DLLInsertLast(List->First, List->Last, PanelView); + List->Count += 1; } static void W_PanelViewRemove(workspace_panel_view_list *List, workspace_panel_view *PanelView) { - DLLRemove(List->First, List->Last, PanelView); - List->Count -= 1; + DLLRemove(List->First, List->Last, PanelView); + List->Count -= 1; } static workspace_panel_view *W_CreatePanelView(void) { - workspace *Workspace = W_GetState(); - workspace_panel_view *PanelView = Workspace->PanelViewFreeList.First; - if(PanelView) - { - W_PanelViewRemove(&Workspace->PanelViewFreeList, PanelView); - } - else - { - PanelView = PushStruct(Workspace->PanelViewArena, workspace_panel_view); - } - return(PanelView); + workspace *Workspace = W_GetState(); + workspace_panel_view *PanelView = Workspace->PanelViewFreeList.First; + if(PanelView) + { + W_PanelViewRemove(&Workspace->PanelViewFreeList, PanelView); + } + else + { + PanelView = PushStruct(Workspace->PanelViewArena, workspace_panel_view); + } + return(PanelView); } static void W_DeletePanelView(workspace_panel_view *PanelView) { - workspace *Workspace = W_GetState(); - ZeroStruct(PanelView); - W_PanelViewPush(&Workspace->PanelViewFreeList, PanelView); + workspace *Workspace = W_GetState(); + ZeroStruct(PanelView); + W_PanelViewPush(&Workspace->PanelViewFreeList, PanelView); } static void W_SplitPanel(workspace_panel *Panel, axis2 Axis) { - workspace *Workspace = W_GetState(); - - if(Panel) - { - workspace_panel *Parent = Panel->Parent; - - if(Parent && (Parent->SplitAxis == Axis)) - { - 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 = W_CreateNewPanel(Panel); - NewPanel->FirstView = Panel->FirstView; - NewPanel->LastView = Panel->LastView; - - // sixten: Update the parents of the children. - for(workspace_view *Child = NewPanel->FirstView; - Child != 0; - Child = Child->Next) - { - Child->Parent = NewPanel; - } - - NewPanel->CurrentView = Panel->CurrentView; - NewPanel->PercentOfParent = 0.5; - DLLInsertLast(Panel->First, Panel->Last, NewPanel); - - NewPanel = W_CreateNewPanel(Panel); - NewPanel->PercentOfParent = 0.5; - DLLInsertLast(Panel->First, Panel->Last, NewPanel); - - Panel->FirstView = 0; - Panel->LastView = 0; - Panel->CurrentView = 0; - Panel->SplitAxis = Axis; - - if(Workspace->CurrentPanel == Panel) - { - Workspace->CurrentPanel = Panel->First; - } - } - } + workspace *Workspace = W_GetState(); + + if(Panel) + { + workspace_panel *Parent = Panel->Parent; + + if(Parent && (Parent->SplitAxis == Axis)) + { + 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 = W_CreateNewPanel(Panel); + NewPanel->FirstView = Panel->FirstView; + NewPanel->LastView = Panel->LastView; + + // sixten: Update the parents of the children. + for(workspace_view *Child = NewPanel->FirstView; + Child != 0; + Child = Child->Next) + { + Child->Parent = NewPanel; + } + + NewPanel->CurrentView = Panel->CurrentView; + NewPanel->PercentOfParent = 0.5; + DLLInsertLast(Panel->First, Panel->Last, NewPanel); + + NewPanel = W_CreateNewPanel(Panel); + NewPanel->PercentOfParent = 0.5; + DLLInsertLast(Panel->First, Panel->Last, NewPanel); + + Panel->FirstView = 0; + Panel->LastView = 0; + Panel->CurrentView = 0; + Panel->SplitAxis = Axis; + + if(Workspace->CurrentPanel == Panel) + { + Workspace->CurrentPanel = Panel->First; + } + } + } } static b32 W_ViewIsDragged(workspace_view *View) { - workspace *Workspace = W_GetState(); - b32 Result = (Workspace->DragPayloadState != W_DragPayload_Inactive && Workspace->DragPayload.View == View) && - UI_GetState()->DragStartP != UI_MouseP(); - return(Result); + workspace *Workspace = W_GetState(); + b32 Result = (Workspace->DragPayloadState != W_DragPayload_Inactive && Workspace->DragPayload.View == View) && + UI_GetState()->DragStartP != UI_MouseP(); + return(Result); } inline void W_BeginDrag(workspace_drag_payload *Payload) { - workspace *Workspace = W_GetState(); - - // sixten(no longer a 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 == W_DragPayload_Inactive); - // sixten(from the future): I have concluded that this should never be an issue. - - Workspace->DragPayload = *Payload; - Workspace->DragPayloadState = W_DragPayload_Active; + workspace *Workspace = W_GetState(); + + // sixten(no longer a 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 == W_DragPayload_Inactive); + // sixten(from the future): I have concluded that this should never be an issue. + + Workspace->DragPayload = *Payload; + Workspace->DragPayloadState = W_DragPayload_Active; } inline b32 W_GetDragPayload(workspace_drag_payload *Dest) { - workspace *Workspace = W_GetState(); - - b32 Result = (Workspace->DragPayloadState != W_DragPayload_Inactive); - *Dest = Workspace->DragPayload; - return(Result); + workspace *Workspace = W_GetState(); + + b32 Result = (Workspace->DragPayloadState != W_DragPayload_Inactive); + *Dest = Workspace->DragPayload; + return(Result); } static void W_BuildTabItem(workspace_panel *Panel, workspace_view *View) { - workspace *Workspace = W_GetState(); - - b32 ViewIsCurrent = (Panel->CurrentView == View); - b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); - - string Name = W_GetViewName(UI_FrameArena(), View); - - b32 Open = !(W_ViewIsDragged(View) && Workspace->DragPayloadState != W_DragPayload_Released); - r32 OpenT = AC_AnimateValueF(Open, 0.0f, 0.3f, "Workspace Tab Item %p", View); - r32 SpaceT = AC_AnimateValueF(Open, 1.0f, 0.3f, "Workspace Tab Item Space %p", View); - - UI_Size(UI_ChildrenSum(SpaceT, 1), UI_Percent(1, 1)) UI_Column() - { - UI_Spacer(UI_Percent(1-OpenT, 1)); - - v4 BackgroundColor = ViewIsCurrent ? (PanelIsCurrent ? Theme_HighlightBorderColor : Theme_BorderColor) : ColorFromHex(0x353738FF); - - UI_SetNextWidth(UI_ChildrenSum(1, 1)); - UI_SetNextHeight(UI_Percent(1, 1)); - UI_SetNextBackgroundColor(BackgroundColor); - UI_SetNextBorderColor(LinearBlend(UI_TopBackgroundColor(), Color_Grey, 0.5)); - UI_SetNextLayoutAxis(Axis2_X); - UI_SetNextCornerRadius(0.0); - - ui_box *TabBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawDropShadow | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation | - UI_BoxFlag_Clip | - UI_BoxFlag_Clickable, - "Workspace Panel Tab Item %S#%p", Name, View); - - UI_Parent(TabBox) UI_Padding(UI_Pixels(5, 1)) - { - UI_Size(UI_TextContent(1, 1), UI_Percent(1, 1)) UI_Label(Name); - UI_Spacer(UI_Pixels(5, 1)); - - // sixten: Build close button - { - 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); - - ui_signal Signal = UI_SignalFromBox(CloseBox); - if(Signal.Clicked) - { - W_IssueCommand(W_Command_CloseView, PointerToU64(View)); - } - } - } - - ui_signal Signal = UI_SignalFromBox(TabBox); - if(Signal.Clicked) - { - Workspace->CurrentPanel = Panel; - Panel->CurrentView = View; - } - - // sixten: Handle dragging the view. - if(Signal.Dragging) - { - if(Signal.Pressed) - { - workspace_drag_payload Payload = {}; - Payload.View = View; - Payload.Key = TabBox->Key; - - W_BeginDrag(&Payload); - } - } - } + workspace *Workspace = W_GetState(); + + b32 ViewIsCurrent = (Panel->CurrentView == View); + b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); + + string Name = W_NameFromView(UI_FrameArena(), View); + + b32 Open = !(W_ViewIsDragged(View) && Workspace->DragPayloadState != W_DragPayload_Released); + r32 OpenT = AC_AnimateValueF(Open, 0.0f, 0.3f, "Workspace Tab Item %p", View); + r32 SpaceT = AC_AnimateValueF(Open, 1.0f, 0.3f, "Workspace Tab Item Space %p", View); + + UI_Size(UI_ChildrenSum(SpaceT, 1), UI_Percent(1, 1)) UI_Column() + { + UI_Spacer(UI_Percent(1-OpenT, 1)); + + v4 BackgroundColor = ViewIsCurrent ? (PanelIsCurrent ? Theme_HighlightBorderColor : Theme_BorderColor) : ColorFromHex(0x353738FF); + + UI_SetNextWidth(UI_ChildrenSum(1, 1)); + UI_SetNextHeight(UI_Percent(1, 1)); + UI_SetNextBackgroundColor(BackgroundColor); + UI_SetNextBorderColor(LinearBlend(UI_TopBackgroundColor(), Color_Grey, 0.5)); + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextCornerRadius(0.0); + + ui_box *TabBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawDropShadow | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation | + UI_BoxFlag_Clip | + UI_BoxFlag_Clickable, + "Workspace Panel Tab Item %S#%p", Name, View); + + UI_Parent(TabBox) UI_Padding(UI_Pixels(5, 1)) + { + UI_Size(UI_TextContent(1, 1), UI_Percent(1, 1)) UI_Label(Name); + UI_Spacer(UI_Pixels(5, 1)); + + // sixten: Build close button + { + 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); + + ui_signal Signal = UI_SignalFromBox(CloseBox); + if(Signal.Clicked) + { + W_IssueCommand(W_Command_CloseView, PointerToU64(View)); + } + } + } + + ui_signal Signal = UI_SignalFromBox(TabBox); + if(Signal.Clicked) + { + Workspace->CurrentPanel = Panel; + Panel->CurrentView = View; + } + + // sixten: Handle dragging the view. + if(Signal.Dragging) + { + if(Signal.Pressed) + { + workspace_drag_payload Payload = {}; + Payload.View = View; + Payload.Key = TabBox->Key; + + W_BeginDrag(&Payload); + } + } + } } static void W_BuildPanelHeader(workspace_panel *Panel) { - workspace *Workspace = W_GetState(); - - UI_SetNextLayoutAxis(Axis2_X); - UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); - UI_SetNextCornerRadius(0); - UI_SetNextSize(UI_Percent(1, 1), UI_Pixels(30, 1)); - - UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "Workspace Panel Header")) - { - for(workspace_view *View = Panel->FirstView; - View != 0; - View = View->Next) - { - W_BuildTabItem(Panel, View); - } - - // sixten: Panel Close Button - if(Panel != Workspace->RootPanel) - { - UI_Spacer(UI_Percent(1, 0)); - - UI_SetNextSize(UI_Pixels(30, 1), UI_Pixels(30, 1)); - UI_SetNextFont(Font_Icons); - UI_SetNextCornerRadius(4); - UI_SetNextHoverCursor(PlatformCursor_Hand); - - ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation | - UI_BoxFlag_DrawText | - UI_BoxFlag_Clickable, - "%U", FontIcon_Cancel); - - ui_signal Signal = UI_SignalFromBox(CloseBox); - if(Signal.Clicked) - { - W_IssueCommand(W_Command_ClosePanel, PointerToU64(Panel)); - } - } - - UI_Spacer(UI_Pixels(2, 1)); - } + workspace *Workspace = W_GetState(); + + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextBackgroundColor(ColorFromHex(0x252728FF)); + UI_SetNextCornerRadius(0); + UI_SetNextSize(UI_Percent(1, 1), UI_Pixels(30, 1)); + + UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "Workspace Panel Header")) + { + for(workspace_view *View = Panel->FirstView; + View != 0; + View = View->Next) + { + W_BuildTabItem(Panel, View); + } + + // sixten: Panel Close Button + if(Panel != Workspace->RootPanel) + { + UI_Spacer(UI_Percent(1, 0)); + + UI_SetNextSize(UI_Pixels(30, 1), UI_Pixels(30, 1)); + UI_SetNextFont(Font_Icons); + UI_SetNextCornerRadius(4); + UI_SetNextHoverCursor(PlatformCursor_Hand); + + ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation | + UI_BoxFlag_DrawText | + UI_BoxFlag_Clickable, + "%U", FontIcon_Cancel); + + ui_signal Signal = UI_SignalFromBox(CloseBox); + if(Signal.Clicked) + { + W_IssueCommand(W_Command_ClosePanel, PointerToU64(Panel)); + } + } + + UI_Spacer(UI_Pixels(2, 1)); + } } static void W_BuildPanel(workspace_panel *Panel) { - workspace *Workspace = W_GetState(); - - // sixten: Fill remaining percent of parent. - workspace_panel *Parent = Panel->Parent; - if(Parent && Panel != Parent->First) - { - r32 TotalOfParent = 0; - for(workspace_panel *Child = Parent->First; - Child != 0; - Child = Child->Next) - { - if(Child != Panel) - { - TotalOfParent += Child->PercentOfParent; - } - } - - Panel->PercentOfParent = 1.0 - TotalOfParent; - } - - ui_box *PanelBox = UI_MakeBoxF(0, "Workspace Panel %p", Panel); - UI_Parent(PanelBox) - { - if(DLLIsEmpty(Panel->First)) - { - W_BuildPanelHeader(Panel); - - // sixten: Main body - { - b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); - - UI_PushSize(UI_Percent(1, 0), UI_Percent(1, 0)); - - 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); - - ui_box *BodyBox = UI_MakeBoxF(UI_BoxFlag_DrawBorder | - UI_BoxFlag_DrawBackground | - UI_BoxFlag_Clip | - UI_BoxFlag_Clickable, - "Workspace Panel Body"); - UI_Parent(BodyBox) - { - if(Panel->FirstView) - { - if(!Panel->CurrentView) - { - Panel->CurrentView = Panel->FirstView; - } - } - else - { - UI_Column() UI_Padding(UI_Percent(1, 0)) - UI_Height(UI_ChildrenSum(1, 1)) UI_Row() UI_Padding(UI_Percent(1, 0)) - { - UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) - { - UI_LabelF("- empty -"); - } - } - } - - if(Panel->CurrentView) - { - ui_key CurrentActive = UI_ActiveKey(); - - W_BuildView(Panel->CurrentView); - - if(!AreEqual(CurrentActive, UI_ActiveKey())) - { - Workspace->CurrentPanel = Panel; - } - } - - // sixten: Draw dragged view overlay. - { - workspace_drag_payload Payload; - b32 DragActive = W_GetDragPayload(&Payload); - - b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) && - InRange(BodyBox->Rect, UI_GetState()->MouseP)); - - 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 = W_DragPayload_Inactive; - - // sixten(TODO): Pull out the code below into separate functions. - - // sixten: Move view - workspace_view *View = Payload.View; - { - workspace_panel *OldParent = View->Parent; - b32 ViewWasCurrent = ((OldParent->CurrentView == View) && - (Workspace->CurrentPanel == OldParent)); - - // sixten: Detatch view - { - Assert(OldParent); - - if(OldParent->CurrentView == View) - { - OldParent->CurrentView = 0; - } - DLLRemove(OldParent->FirstView, OldParent->LastView, View); - } - - View->Parent = Panel; - DLLInsertLast(Panel->FirstView, Panel->LastView, View); - - if(ViewWasCurrent) - { - Workspace->CurrentPanel = Panel; - Panel->CurrentView = View; - } - } - } - - 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; - - UI_SetNextBackgroundColor(OverlayColor); - UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); - - UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY, - "Workspace Panel Drag Hover"); - } - } - - ui_signal Signal = UI_SignalFromBox(BodyBox); - if(Signal.Pressed) - { - Workspace->CurrentPanel = Panel; - } - - UI_PopSize(); - } - } - else - { - UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); - UI_SetNextLayoutAxis(Panel->SplitAxis); - - UI_Parent(UI_MakeBoxF(0, "")) - { - s32 ChildCount = 0; - for(workspace_panel *Child = Panel->First; - Child != 0; - Child = Child->Next) - { - ++ChildCount; - } - - v2 PanelDim = DimOfRange(PanelBox->Rect); - - r32 PaddingSize = 5; - r32 PaddedSpace = (ChildCount - 1)*PaddingSize; - r32 PercentPaddedSpace = PaddedSpace / PanelDim.E[Panel->SplitAxis]; - r32 SizeScalar = 1.0 - PercentPaddedSpace; - - for(workspace_panel *Child = Panel->First; - Child != 0; - Child = Child->Next) - { - UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0)); - UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); - W_BuildPanel(Child); - - // sixten: Create drag area - if(Child->Next) - { - UI_SetNextAxisSize(Panel->SplitAxis, UI_Pixels(PaddingSize, 1)); - UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); - ui_box *DragBox = UI_MakeBoxF(UI_BoxFlag_Clickable, "Workspace Panel Drag %p", Child); - - ui_signal Signal = UI_SignalFromBox(DragBox); - if(Signal.Hovering || Signal.Dragging) - { - Platform.SetCursor((Panel->SplitAxis == Axis2_X) ? - PlatformCursor_ArrowHorizontal : - PlatformCursor_ArrowVertical); - } - - if(Signal.Dragging) - { - if(Signal.Pressed) - { - UI_StoreDragR32(Child->PercentOfParent); - } - - r32 Delta = Signal.DragDelta.E[Panel->SplitAxis]/PanelDim.E[Panel->SplitAxis]; - - r32 StartOffset = UI_GetDragR32(); - r32 EndOffset = StartOffset + Delta; - - Child->PercentOfParent = Clamp(EndOffset, 0.05, 0.95); - } - } - } - } - } - } + workspace *Workspace = W_GetState(); + + // sixten: Fill remaining percent of parent. + workspace_panel *Parent = Panel->Parent; + if(Parent && Panel != Parent->First) + { + r32 TotalOfParent = 0; + for(workspace_panel *Child = Parent->First; + Child != 0; + Child = Child->Next) + { + if(Child != Panel) + { + TotalOfParent += Child->PercentOfParent; + } + } + + Panel->PercentOfParent = 1.0 - TotalOfParent; + } + + ui_box *PanelBox = UI_MakeBoxF(0, "Workspace Panel %p", Panel); + UI_Parent(PanelBox) + { + if(DLLIsEmpty(Panel->First)) + { + W_BuildPanelHeader(Panel); + + // sixten: Main body + { + b32 PanelIsCurrent = (Workspace->CurrentPanel == Panel); + + UI_PushSize(UI_Percent(1, 0), UI_Percent(1, 0)); + + 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); + + ui_box *BodyBox = UI_MakeBoxF(UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_Clip | + UI_BoxFlag_Clickable, + "Workspace Panel Body"); + UI_Parent(BodyBox) + { + if(Panel->FirstView) + { + if(!Panel->CurrentView) + { + Panel->CurrentView = Panel->FirstView; + } + } + else + { + UI_Column() UI_Padding(UI_Percent(1, 0)) + UI_Height(UI_ChildrenSum(1, 1)) UI_Row() UI_Padding(UI_Percent(1, 0)) + { + UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) + { + UI_LabelF("- empty -"); + } + } + } + + if(Panel->CurrentView) + { + ui_key CurrentActive = UI_ActiveKey(); + + W_BuildView(Panel->CurrentView); + + if(!AreEqual(CurrentActive, UI_ActiveKey())) + { + Workspace->CurrentPanel = Panel; + } + } + + // sixten: Draw dragged view overlay. + { + workspace_drag_payload Payload; + b32 DragActive = W_GetDragPayload(&Payload); + + b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) && + InRange(BodyBox->Rect, UI_GetState()->MouseP)); + + 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 = W_DragPayload_Inactive; + + // sixten(TODO): Pull out the code below into separate functions. + + // sixten: Move view + workspace_view *View = Payload.View; + { + workspace_panel *OldParent = View->Parent; + b32 ViewWasCurrent = ((OldParent->CurrentView == View) && + (Workspace->CurrentPanel == OldParent)); + + // sixten: Detatch view + { + Assert(OldParent); + + if(OldParent->CurrentView == View) + { + OldParent->CurrentView = 0; + } + DLLRemove(OldParent->FirstView, OldParent->LastView, View); + } + + View->Parent = Panel; + DLLInsertLast(Panel->FirstView, Panel->LastView, View); + + if(ViewWasCurrent) + { + Workspace->CurrentPanel = Panel; + Panel->CurrentView = View; + } + } + } + + 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; + + UI_SetNextBackgroundColor(OverlayColor); + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1)); + + UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, + "Workspace Panel Drag Hover"); + } + } + + ui_signal Signal = UI_SignalFromBox(BodyBox); + if(Signal.Pressed) + { + Workspace->CurrentPanel = Panel; + } + + UI_PopSize(); + } + } + else + { + UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); + UI_SetNextLayoutAxis(Panel->SplitAxis); + + UI_Parent(UI_MakeBoxF(0, "")) + { + s32 ChildCount = 0; + for(workspace_panel *Child = Panel->First; + Child != 0; + Child = Child->Next) + { + ++ChildCount; + } + + v2 PanelDim = DimOfRange(PanelBox->Rect); + + r32 PaddingSize = 5; + r32 PaddedSpace = (ChildCount - 1)*PaddingSize; + r32 PercentPaddedSpace = PaddedSpace / PanelDim.E[Panel->SplitAxis]; + r32 SizeScalar = 1.0 - PercentPaddedSpace; + + for(workspace_panel *Child = Panel->First; + Child != 0; + Child = Child->Next) + { + UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0)); + UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); + W_BuildPanel(Child); + + // sixten: Create drag area + if(Child->Next) + { + UI_SetNextAxisSize(Panel->SplitAxis, UI_Pixels(PaddingSize, 1)); + UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0)); + ui_box *DragBox = UI_MakeBoxF(UI_BoxFlag_Clickable, "Workspace Panel Drag %p", Child); + + ui_signal Signal = UI_SignalFromBox(DragBox); + if(Signal.Hovering || Signal.Dragging) + { + Platform.SetCursor((Panel->SplitAxis == Axis2_X) ? + PlatformCursor_ArrowHorizontal : + PlatformCursor_ArrowVertical); + } + + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragR32(Child->PercentOfParent); + } + + r32 Delta = Signal.DragDelta.E[Panel->SplitAxis]/PanelDim.E[Panel->SplitAxis]; + + r32 StartOffset = UI_GetDragR32(); + r32 EndOffset = StartOffset + Delta; + + Child->PercentOfParent = Clamp(EndOffset, 0.05, 0.95); + } + } + } + } + } + } } static void W_BuildDragPayload() { - workspace *Workspace = W_GetState(); - - workspace_drag_payload Payload; - if(W_GetDragPayload(&Payload) && W_ViewIsDragged(Payload.View)) - { - if(Workspace->DragPayloadState == W_DragPayload_Released) - { - Workspace->DragPayloadState = W_DragPayload_Inactive; - } - - if(AreEqual(Payload.Key, UI_ActiveKey())) - { - workspace_view *DraggedView = Payload.View; - UI_SetNextCornerRadius(4); - UI_TooltipLabel(W_GetViewName(UI_FrameArena(), DraggedView), UI_MouseP()); - } - else - { - if(Workspace->DragPayloadState == W_DragPayload_Active) - { - Workspace->DragPayloadState = W_DragPayload_Released; - } - } - } + workspace *Workspace = W_GetState(); + + workspace_drag_payload Payload; + if(W_GetDragPayload(&Payload) && W_ViewIsDragged(Payload.View)) + { + if(Workspace->DragPayloadState == W_DragPayload_Released) + { + Workspace->DragPayloadState = W_DragPayload_Inactive; + } + + if(AreEqual(Payload.Key, UI_ActiveKey())) + { + workspace_view *DraggedView = Payload.View; + UI_SetNextCornerRadius(4); + UI_TooltipLabel(W_NameFromView(UI_FrameArena(), DraggedView), UI_MouseP()); + } + else + { + if(Workspace->DragPayloadState == W_DragPayload_Active) + { + Workspace->DragPayloadState = W_DragPayload_Released; + } + } + } } //- sixten: Workspace static void W_Init(workspace *Workspace) { - W_SetState(Workspace); - - Workspace->CommandArena = ArenaAlloc(Kilobytes(4), true); - Workspace->CommandDataArena = ArenaAlloc(Kilobytes(4), true); - Workspace->PanelArena = ArenaAlloc(Kilobytes(4), true); - - Workspace->RootPanel = Workspace->CurrentPanel = W_CreateNewPanel(0); - - if(DEBUG_DebugSettings->ShowWelcomeMessage) - { - W_CreateNewView(W_ViewKind_Startup, Workspace->RootPanel); - } - - // sixten: build text editor / scene view layout - if(0) - { - W_CreateNewView(W_ViewKind_TextEditor, Workspace->RootPanel); - W_SplitPanel(Workspace->RootPanel, Axis2_X); - W_CreateNewView(W_ViewKind_SceneView, Workspace->RootPanel->Last); - } - - W_CreateNewView(W_ViewKind_NavEditor, Workspace->RootPanel); + W_SetState(Workspace); + + Workspace->CommandArena = ArenaAlloc(Kilobytes(4), true); + Workspace->CommandDataArena = ArenaAlloc(Kilobytes(4), true); + Workspace->PanelArena = ArenaAlloc(Kilobytes(4), true); + + Workspace->RootPanel = Workspace->CurrentPanel = W_CreateNewPanel(0); + + if(DEBUG_DebugSettings->ShowWelcomeMessage) + { + W_CreateNewView(W_ViewKind_Startup, Workspace->RootPanel); + } } static void W_Update(workspace *Workspace, vn_render_commands *RenderCommands, vn_input *Input, glyph_atlas *GlyphAtlas) { - Workspace->Input = Input; - Workspace->EventList = Input->EventList; - - W_SetState(Workspace); - - // sixten: Process the keybinds and commands. - W_ProcessKeyBinds(); - W_ProcessCommands(); - - // sixten: Make sure that a view/panel is always selected. - if(!Workspace->CurrentPanel) - { - Workspace->CurrentPanel = Workspace->RootPanel; - while(Workspace->CurrentPanel->First != 0) - { - Workspace->CurrentPanel = Workspace->CurrentPanel->First; - } - } - - // sixten: Build the UI. - { - W_BuildDragPayload(); - - W_BuildToolbar(); - UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0)); - W_BuildPanel(Workspace->RootPanel); - } + Workspace->Input = Input; + Workspace->EventList = Input->EventList; + + W_SetState(Workspace); + + // sixten: Process the keybinds and commands. + W_ProcessKeyBinds(); + W_ProcessCommands(); + + // sixten: Make sure that a view/panel is always selected. + if(!Workspace->CurrentPanel) + { + Workspace->CurrentPanel = Workspace->RootPanel; + while(Workspace->CurrentPanel->First != 0) + { + Workspace->CurrentPanel = Workspace->CurrentPanel->First; + } + } + + // sixten: Build the UI. + { + W_BuildDragPayload(); + + W_BuildToolbar(); + UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 0)); + W_BuildPanel(Workspace->RootPanel); + } } \ No newline at end of file diff --git a/code/vn_workspace_commands.cpp b/code/vn_workspace_commands.cpp index 61aa3ce..9a76a58 100644 --- a/code/vn_workspace_commands.cpp +++ b/code/vn_workspace_commands.cpp @@ -1,173 +1,198 @@ WORKSPACE_COMMAND(W_Command_SplitPanelHorizontal) { - workspace *Workspace = W_GetState(); - W_SplitPanel(Workspace->CurrentPanel, Axis2_X); + workspace *Workspace = W_GetState(); + W_SplitPanel(Workspace->CurrentPanel, Axis2_X); } WORKSPACE_COMMAND(W_Command_SplitPanelVertical) { - workspace *Workspace = W_GetState(); - W_SplitPanel(Workspace->CurrentPanel, Axis2_Y); + workspace *Workspace = W_GetState(); + W_SplitPanel(Workspace->CurrentPanel, Axis2_Y); } WORKSPACE_COMMAND(W_Command_ClosePanel) { - workspace *Workspace = W_GetState(); - - workspace_panel *Panel = (workspace_panel *)Argument; - if(!Panel) - { - Panel = Workspace->CurrentPanel; - } - - workspace_panel *Parent = Panel->Parent; - - Assert(Parent); - Assert(Panel != Workspace->RootPanel); - - DLLRemove(Parent->First, Parent->Last, Panel); - - b32 OneChildRemains = (Parent->First == Parent->Last); - if(OneChildRemains) - { - workspace_panel *Child = Parent->First; - Assert(DLLIsEmpty(Parent->FirstView)); - - Parent->FirstView = Child->FirstView; - Parent->LastView = Child->LastView; - Parent->First = Child->First; - Parent->Last = Child->Last; - Parent->SplitAxis = Child->SplitAxis; - - // sixten: Update the parents of the children. - for(workspace_view *View = Parent->FirstView; - View != 0; - View = View->Next) - { - View->Parent = Parent; - } - for(workspace_panel *ParentChild = Parent->First; - ParentChild != 0; - ParentChild = ParentChild->Next) - { - ParentChild->Parent = Parent; - } - - DLLRemove(Parent->First, Parent->Last, Child); - W_DeletePanel(Child); - } - else - { - s32 ChildCount = 0; - for(workspace_panel *Child = Parent->First; - Child != 0; - Child = Child->Next) - { - ++ChildCount; - } - - r32 ToAppend = Panel->PercentOfParent / ChildCount; - for(workspace_panel *Child = Parent->First; - Child != 0; - Child = Child->Next) - { - Child->PercentOfParent += ToAppend; - } - } - - // sixten: Delete all child views. - workspace_view *NextChild = 0; - for(workspace_view *Child = Panel->FirstView; - Child != 0; - Child = NextChild) - { - NextChild = Child->Next; - W_DestroyView(Child); - } - - W_DeletePanel(Panel); + workspace *Workspace = W_GetState(); + + workspace_panel *Panel = (workspace_panel *)Argument; + if(!Panel) + { + Panel = Workspace->CurrentPanel; + } + + workspace_panel *Parent = Panel->Parent; + + Assert(Parent); + Assert(Panel != Workspace->RootPanel); + + DLLRemove(Parent->First, Parent->Last, Panel); + + b32 OneChildRemains = (Parent->First == Parent->Last); + if(OneChildRemains) + { + workspace_panel *Child = Parent->First; + Assert(DLLIsEmpty(Parent->FirstView)); + + Parent->FirstView = Child->FirstView; + Parent->LastView = Child->LastView; + Parent->First = Child->First; + Parent->Last = Child->Last; + Parent->SplitAxis = Child->SplitAxis; + Parent->CurrentView = Child->CurrentView; + + // sixten: Update the parents of the children. + for(workspace_view *View = Parent->FirstView; + View != 0; + View = View->Next) + { + View->Parent = Parent; + } + for(workspace_panel *ParentChild = Parent->First; + ParentChild != 0; + ParentChild = ParentChild->Next) + { + ParentChild->Parent = Parent; + } + + DLLRemove(Parent->First, Parent->Last, Child); + W_DeletePanel(Child); + } + else + { + s32 ChildCount = 0; + for(workspace_panel *Child = Parent->First; + Child != 0; + Child = Child->Next) + { + ++ChildCount; + } + + r32 ToAppend = Panel->PercentOfParent / ChildCount; + for(workspace_panel *Child = Parent->First; + Child != 0; + Child = Child->Next) + { + Child->PercentOfParent += ToAppend; + } + } + + // sixten: Delete all child views. + workspace_view *NextChild = 0; + for(workspace_view *Child = Panel->FirstView; + Child != 0; + Child = NextChild) + { + NextChild = Child->Next; + W_DestroyView(Child); + } + + W_DeletePanel(Panel); } WORKSPACE_COMMAND(W_Command_OpenView) { - workspace *Workspace = W_GetState(); - W_CreateNewView((workspace_view_kind)Argument, Workspace->CurrentPanel); + workspace *Workspace = W_GetState(); + W_CreateNewView((workspace_view_kind)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); + 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); } WORKSPACE_COMMAND(W_Command_OpenFile) { - temporary_memory Scratch = GetScratch(); - - workspace *Workspace = W_GetState(); - workspace_file_lister_action *Action = (workspace_file_lister_action *)U64ToPointer(Argument); - - string Filepath = PushFormat(Scratch.Arena, "%S/%S", Action->Path, Action->Name); - - platform_file_handle File = Platform.OpenFile(Filepath, PlatformAccess_Read); - if(File.IsValid) - { - // sixten: retrieve file extension - string FileExtension = Substring(Action->Name, Range1S64(LastIndexOf(Action->Name, '.')+1, Action->Name.Count)); - if(AreEqual(FileExtension, StrLit("vns"))) - { - s64 FileSize = Platform.GetFileSize(File); - string ReplaceString = MakeString(PushArrayNoClear(Scratch.Arena, u8, FileSize+1), FileSize); - ReplaceString.Data[FileSize] = 0; - Platform.ReadFile(File, ReplaceString.Data, 0, FileSize); - - ReplaceString = RemoveAll(Scratch.Arena, ReplaceString, '\r'); - - workspace_view *View = W_CreateNewView(W_ViewKind_TextEditor, Workspace->CurrentPanel); - workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; - - MutableStringReplaceRange(&Editor->Text, ReplaceString, Range1S64(0, 0)); - W_TextEditorApplyChanges(Editor); - Editor->FileName = PushString(View->Arena, Action->Name); - Editor->FilePath = PushString(View->Arena, Action->Path); - } - else if(AreEqual(FileExtension, StrLit("vnn"))) - { - s64 FileSize = Platform.GetFileSize(File); - string Contents = MakeString(PushArrayNoClear(Scratch.Arena, u8, FileSize), FileSize); - Platform.ReadFile(File, Contents.Data, 0, Contents.Count); - - workspace_view *View = W_CreateNewView(W_ViewKind_NavEditor, Workspace->CurrentPanel); - W_SetupNavEditor(View, Contents); - } - else - { - workspace_view *View = W_CreateNewView(W_ViewKind_Error, Workspace->CurrentPanel); - workspace_view_error *Error = (workspace_view_error *)View->Data; - Error->Message = PushFormat(View->Arena, "Unknown file extension: %S", FileExtension); - } - - Platform.CloseFile(File); - } - else - { - // sixten: what happens now?? - } - - ReleaseScratch(Scratch); + temp Scratch = GetScratch(); + + workspace *Workspace = W_GetState(); + workspace_file_lister_action *Action = (workspace_file_lister_action *)U64ToPointer(Argument); + + string Filepath = PushFormat(Scratch.Arena, "%S/%S", Action->Path, Action->Name); + + platform_file_handle File = Platform.OpenFile(Filepath, PlatformAccess_Read); + if(File.IsValid) + { + // sixten: retrieve file extension + string FileExtension = Substring(Action->Name, Range1S64(LastIndexOf(Action->Name, '.')+1, Action->Name.Count)); + if(AreEqual(FileExtension, StrLit("vns"))) + { + s64 FileSize = Platform.GetFileSize(File); + string ReplaceString = MakeString(PushArrayNoClear(Scratch.Arena, u8, FileSize+1), FileSize); + ReplaceString.Data[FileSize] = 0; + Platform.ReadFile(File, ReplaceString.Data, 0, FileSize); + + ReplaceString = RemoveAll(Scratch.Arena, ReplaceString, '\r'); + + workspace_view *View = W_CreateNewView(W_ViewKind_TextEditor, Workspace->CurrentPanel); + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + + MutableStringReplaceRange(&Editor->Text, ReplaceString, Range1S64(0, 0)); + W_TextEditorApplyChanges(Editor); + Editor->FileName = PushString(View->Arena, Action->Name); + Editor->FilePath = PushString(View->Arena, Action->Path); + } + else if(AreEqual(FileExtension, StrLit("vnn"))) + { + s64 FileSize = Platform.GetFileSize(File); + string Contents = MakeString(PushArrayNoClear(Scratch.Arena, u8, FileSize), FileSize); + Platform.ReadFile(File, Contents.Data, 0, Contents.Count); + + workspace_view *View = W_CreateNewView(W_ViewKind_NavEditor, Workspace->CurrentPanel); + W_NavEditorSetup(View, Action->Path, Action->Name, Contents); + } + else if(AreEqual(FileExtension, StrLit("png")) || AreEqual(FileExtension, StrLit("jpg"))) + { + s64 FileSize = Platform.GetFileSize(File); + string Contents = MakeString(PushArrayNoClear(Scratch.Arena, u8, FileSize), FileSize); + Platform.ReadFile(File, Contents.Data, 0, Contents.Count); + workspace_view *View = W_CreateNewView(W_ViewKind_ImageViewer, Workspace->CurrentPanel); + b32 CouldOpenImage = W_ImageViewerSetup(View, Action->Name, Contents); + if(!CouldOpenImage) + { + //- sixten: destroy the view and show an error message instead + workspace_panel *Panel = View->Parent; + + DLLRemove(Panel->FirstView, Panel->LastView, View); + if(Panel->CurrentView == View) + { + Panel->CurrentView = Panel->FirstView; + } + W_DestroyView(View); + + View = W_CreateNewView(W_ViewKind_Error, Workspace->CurrentPanel); + workspace_view_error *Error = (workspace_view_error *)View->Data; + Error->Message = PushFormat(View->Arena, "Unknown to open file: %S", Filepath); + } + } + else + { + workspace_view *View = W_CreateNewView(W_ViewKind_Error, Workspace->CurrentPanel); + workspace_view_error *Error = (workspace_view_error *)View->Data; + Error->Message = PushFormat(View->Arena, "Unknown file extension: %S", FileExtension); + } + + Platform.CloseFile(File); + } + else + { + // sixten: what happens now?? + } + + ReleaseScratch(Scratch); } #if VN_INTERNAL WORKSPACE_COMMAND(W_Command_ToggleRenderUIDebugRects) { - DEBUG_DebugSettings->RenderUIDebugRects = !DEBUG_DebugSettings->RenderUIDebugRects; + DEBUG_DebugSettings->RenderUIDebugRects = !DEBUG_DebugSettings->RenderUIDebugRects; } #endif diff --git a/code/vn_workspace_file_lister.cpp b/code/vn_workspace_file_lister.cpp index 05c1057..67daea4 100644 --- a/code/vn_workspace_file_lister.cpp +++ b/code/vn_workspace_file_lister.cpp @@ -1,246 +1,227 @@ static workspace_file_lister_action *W_FileListerActionCopy(arena *Arena, workspace_file_lister_action *Action) { - workspace_file_lister_action *Result = PushStruct(Arena, workspace_file_lister_action); - Result->HasRequestedFile = Action->HasRequestedFile; - Result->Path = PushString(Arena, Action->Path); - Result->Name = PushString(Arena, Action->Name); - return(Result); + workspace_file_lister_action *Result = PushStruct(Arena, workspace_file_lister_action); + Result->HasRequestedFile = Action->HasRequestedFile; + Result->Path = PushString(Arena, Action->Path); + Result->Name = PushString(Arena, Action->Name); + return(Result); } UI_CUSTOM_DRAW_CALLBACK(W_FileListerInputCallback) { - workspace_view_file_lister *Lister = (workspace_view_file_lister *)Data; - s64 ClampedCursor = Clamp(0, Box->String.Count, Lister->InputEditState.Cursor); - s64 ClampedMark = Clamp(0, Box->String.Count, Lister->InputEditState.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); - } + workspace_view_file_lister *Lister = (workspace_view_file_lister *)Data; + s64 ClampedCursor = Clamp(0, Box->String.Count, Lister->InputEditState.Cursor); + s64 ClampedMark = Clamp(0, Box->String.Count, Lister->InputEditState.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 W_BuildFileListerItem(string Text, u32 Icon, b32 Selected, b32 EnterPressed) { - b32 Result = false; - UI_SetNextLayoutAxis(Axis2_X); - UI_SetNextHoverCursor(PlatformCursor_Hand); - r32 SelectedT = AC_AnimateValueF(Selected, 0, 0.3f, "Lister Item %S", Text); - UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Theme_HighlightBorderColor, SelectedT*0.5)); - UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, SelectedT)); - ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBorder | - UI_BoxFlag_DrawBackground | - UI_BoxFlag_Clickable | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation| - UI_BoxFlag_DrawDropShadow, - "File Lister %S", Text); - UI_Parent(Container) - { - UI_Width(UI_Em(2, 1)) UI_Font(Font_Icons) UI_LabelF("%U", Icon); - UI_Width(UI_TextContent(0, 1)) UI_Label(Text); - } - - ui_signal Signal = UI_SignalFromBox(Container); - if(Signal.Clicked || Selected&&EnterPressed) - { - Result = true; - } - return(Result); + b32 Result = false; + UI_SetNextLayoutAxis(Axis2_X); + UI_SetNextHoverCursor(PlatformCursor_Hand); + r32 SelectedT = AC_AnimateValueF(Selected, 0, 0.3f, "Lister Item %S", Text); + UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Theme_HighlightBorderColor, SelectedT*0.5)); + UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, SelectedT)); + ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBorder | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_Clickable | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation| + UI_BoxFlag_DrawDropShadow, + "File Lister %S", Text); + UI_Parent(Container) + { + UI_Width(UI_Em(2, 1)) UI_Font(Font_Icons) UI_LabelF("%U", Icon); + UI_Width(UI_TextContent(0, 1)) UI_Label(Text); + } + + ui_signal Signal = UI_SignalFromBox(Container); + if(Signal.Clicked || Selected&&EnterPressed) + { + Result = true; + } + return(Result); } static workspace_file_lister_action W_BuildFileLister(workspace_view *View) { - workspace_view_file_lister *Lister = (workspace_view_file_lister *)View->Data; - - workspace_file_lister_action ListerAction = {}; - temporary_memory Scratch = GetScratch(); - UI_Size(UI_Percent(1, 1), UI_Percent(1, 1)) - { - - b32 EnterPressed = Platform_KeyPress(UI_EventList(), Key_Return); - s32 SelectedItemDelta = 0; - - s32 ListerCount = 0; - - //- sixten: filename input field - if(W_ViewIsCurrent(View)) - { - for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next) - { - if((Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) && (Event->Codepoint != '/' && Event->Codepoint != '\\')) - { - text_action Action = SingleLineTextActionFromEvent(Event); - if(IsValid(&Action)) - { - text_op Op = TextOpFromAction(Scratch.Arena, MakeString(Lister->Input, Lister->InputUsed), &Lister->InputEditState, &Action); - if(Op.NewCursor >= 0 && Op.NewMark >= 0) - { - string Left = MakeString(Lister->Input, Op.Range.Min); - string Right = MakeString(Lister->Input + Op.Range.Max, Lister->InputUsed - Op.Range.Max); - - u64 NewStringSize = Left.Count + Right.Count + Op.ReplaceString.Count; - char *NewString = PushArray(Scratch.Arena, char, NewStringSize); - Copy(NewString, Left.Data, Left.Count); - Copy(NewString + Left.Count, Op.ReplaceString.Data, Op.ReplaceString.Count); - Copy(NewString + Left.Count + Op.ReplaceString.Count, Right.Data, Right.Count); - Lister->InputUsed = Minimum(ArrayCount(Lister->Input), NewStringSize); - Copy(Lister->Input, NewString, Lister->InputUsed); - - Lister->InputEditState.Cursor = Minimum(Op.NewCursor, Lister->InputUsed); - Lister->InputEditState.Mark = Minimum(Op.NewMark, Lister->InputUsed); - Platform_ConsumeEvent(UI_EventList(), Event); - } - } - } - - if(Event->Type == PlatformEvent_Press && (Event->Key == Key_Up || Event->Key == Key_Down)) - { - SelectedItemDelta = Event->Key == Key_Up ? -1 : 1; - } - } - } - - //- sixten: build navbar - UI_Height(UI_Em(2, 1)) UI_Row(UI_BoxFlag_DrawBorder) - { - UI_SetNextBackgroundColor(ColorFromHex(0x2D5790FF)); - UI_SetNextWidth(UI_TextContent(20, 1)); - UI_MakeBox(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawText, Lister->Path); - - UI_SetNextWidth(UI_TextContent(15, 1)); - ui_box *InputBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Text Lister Lister Input")); - UI_EquipBoxText(InputBox, MakeString(Lister->Input, Lister->InputUsed)); - UI_EquipBoxCustomDrawCallback(InputBox, W_FileListerInputCallback, Lister); - - UI_Padding(UI_Percent(1, 0)); - - UI_SetNextWidth(UI_TextContent(20, 1)); - if(UI_ButtonF("Open/Create").Clicked || EnterPressed) - { - ListerAction.HasRequestedFile = true; - ListerAction.Name = MakeString(Lister->Input, Lister->InputUsed); - ListerAction.Path = Lister->Path; - } - } - - UI_Scroll(0, &Lister->Scroll) - { - UI_Height(UI_Em(2, 1)) - { - //- sixten: display "parent directory button" - s64 LastSlash = LastIndexOf(Lister->Path, '/'); - if(LastSlash != -1) - { - b32 BackspacePressed = Lister->InputUsed == 0 && Platform_KeyPress(UI_EventList(), Key_Backspace); - if(W_BuildFileListerItem(StrLit("Parent Directory"), FontIcon_Reply, Lister->SelectedItem == ListerCount, EnterPressed) || - BackspacePressed) - { - Lister->Path = Prefix(Lister->Path, LastSlash); - Lister->InputUsed = 0; - Lister->SelectedItem = -1; - } - ListerCount += 1; - } - platform_file_info FileInfo; - platform_file_iter *FileIter; - - string Name = MakeString(Lister->Input, Lister->InputUsed); - string FullPath = PushFormat(Scratch.Arena, "%S/%S", Lister->Path, Name); - - //- sixten: display directories - { - FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); - for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) - { - if(FileInfo.IsDirectory) - { - if(W_BuildFileListerItem(FileInfo.Name, FontIcon_Folder, Lister->SelectedItem == ListerCount, EnterPressed)) - { - Lister->Path = PushFormat(View->Arena, "%S/%S", Lister->Path, FileInfo.Name); - Lister->InputUsed = 0; - Lister->SelectedItem = -1; - ListerAction.HasRequestedFile = false; // sixten: if we have selected a folder, but typed a name we want to open the folder. - } - ListerCount += 1; - } - } - } - Platform.EndFileIter(FileIter); - - //- sixten: display files - { - FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); - for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) - { - if(!FileInfo.IsDirectory) - { - if(W_BuildFileListerItem(FileInfo.Name, FontIcon_Document, Lister->SelectedItem == ListerCount, EnterPressed)) - { - ListerAction.HasRequestedFile = true; - Lister->SelectedItem = -1; - ListerAction.Name = PushString(View->Arena, FileInfo.Name); - ListerAction.Path = Lister->Path; - } - ListerCount += 1; - } - } - } - - //- sixten: update selected item - if(SelectedItemDelta != 0) - { - Lister->SelectedItem += SelectedItemDelta; - if(Lister->SelectedItem < 0) - { - Lister->SelectedItem += ListerCount; - } - else if(Lister->SelectedItem >= ListerCount) - { - Lister->SelectedItem -= ListerCount; - } - } - - Platform.EndFileIter(FileIter); - } - } - } - ReleaseScratch(Scratch); - return(ListerAction); + workspace_file_lister_action ListerAction = {}; + + workspace_view_file_lister *Lister = (workspace_view_file_lister *)View->Data; + + b32 EnterPressed = Platform_KeyPress(UI_EventList(), Key_Return); + s32 SelectedItemDelta = 0; + s32 ListerCount = 0; + s64 LastSlash = LastIndexOf(Lister->Path, '/'); + b32 RequestedParentDirectory = false; + + temp Scratch = GetScratch(); + UI_Size(UI_Percent(1, 1), UI_Percent(1, 1)) + { + //- sixten: filename input field + if(W_ViewIsCurrent(View)) + { + for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press && (Event->Key == Key_Up || Event->Key == Key_Down)) + { + SelectedItemDelta = Event->Key == Key_Up ? -1 : 1; + } + if(Event->Type == PlatformEvent_Press && Event->Key == Key_Backspace && Lister->InputEditState.Cursor == 0) + { + RequestedParentDirectory = true; + } + } + } + + //- sixten: build navbar + UI_Height(UI_Em(2, 1)) UI_Row(UI_BoxFlag_DrawBorder) + { + + UI_SetNextBackgroundColor(ColorFromHex(0x2D5790FF)); + UI_SetNextWidth(UI_TextContent(20, 1)); + UI_MakeBox(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawText, Lister->Path); + + UI_Width(UI_Percent(1, 0)) + UI_LineEdit(&Lister->InputEditState, ArrayCount(Lister->Input), Lister->Input, &Lister->InputUsed, StrLit("File Lister Text Input"), W_ViewIsCurrent(View)); + + UI_SetNextWidth(UI_TextContent(20, 1)); + if(UI_ButtonF("Open/Create").Clicked || EnterPressed) + { + ListerAction.HasRequestedFile = true; + ListerAction.Name = MakeString(Lister->Input, Lister->InputUsed); + ListerAction.Path = Lister->Path; + } + } + + UI_Scroll(0, &Lister->Scroll) + { + UI_Height(UI_Em(2, 1)) + { + //- sixten: display "parent directory button" + if(LastSlash != -1) + { + if(W_BuildFileListerItem(StrLit("Parent Directory"), FontIcon_Reply, Lister->SelectedItem == ListerCount, EnterPressed)) + { + RequestedParentDirectory = true; + ListerAction.HasRequestedFile = false; // sixten: if we have selected a folder, but typed a name we want to open the folder. + } + ListerCount += 1; + } + platform_file_info FileInfo; + platform_file_iter *FileIter; + + string Name = MakeString(Lister->Input, Lister->InputUsed); + string FullPath = PushFormat(Scratch.Arena, "%S/%S", Lister->Path, Name); + + //- sixten: display directories + { + FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); + for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) + { + if(FileInfo.IsDirectory) + { + if(W_BuildFileListerItem(FileInfo.Name, FontIcon_Folder, Lister->SelectedItem == ListerCount, EnterPressed)) + { + Lister->Path = PushFormat(View->Arena, "%S/%S", Lister->Path, FileInfo.Name); + Lister->InputUsed = 0; + Lister->SelectedItem = -1; + ListerAction.HasRequestedFile = false; // sixten: if we have selected a folder, but typed a name we want to open the folder. + } + ListerCount += 1; + } + } + } + Platform.EndFileIter(FileIter); + + //- sixten: display files + { + FileIter = Platform.BeginFileIter(Scratch.Arena, FullPath); + for(;Platform.AdvanceFileIter(Scratch.Arena, FileIter, &FileInfo);) + { + if(!FileInfo.IsDirectory) + { + if(W_BuildFileListerItem(FileInfo.Name, FontIcon_Document, Lister->SelectedItem == ListerCount, EnterPressed)) + { + ListerAction.HasRequestedFile = true; + Lister->SelectedItem = -1; + ListerAction.Name = PushString(View->Arena, FileInfo.Name); + ListerAction.Path = Lister->Path; + } + ListerCount += 1; + } + } + } + + //- sixten: update selected item + if(SelectedItemDelta != 0) + { + Lister->SelectedItem += SelectedItemDelta; + if(Lister->SelectedItem < 0) + { + Lister->SelectedItem = ListerCount - 1; + } + else if(Lister->SelectedItem >= ListerCount) + { + Lister->SelectedItem -= ListerCount; + } + } + + Platform.EndFileIter(FileIter); + } + } + } + + //- sixten: goto parent directory + if(RequestedParentDirectory && LastSlash != -1) + { + Lister->Path = Prefix(Lister->Path, LastSlash); + Lister->InputUsed = 0; + Lister->SelectedItem = -1; + } + + ReleaseScratch(Scratch); + return(ListerAction); } diff --git a/code/vn_workspace_file_lister.h b/code/vn_workspace_file_lister.h index b6918c3..ba705cc 100644 --- a/code/vn_workspace_file_lister.h +++ b/code/vn_workspace_file_lister.h @@ -3,19 +3,19 @@ struct workspace_file_lister_action { - b32 HasRequestedFile; - string Path; - string Name; + b32 HasRequestedFile; + string Path; + string Name; }; struct workspace_view_file_lister { - string Path; - r32 Scroll; - u8 Input[256]; - s32 InputUsed; - text_edit_state InputEditState; - s32 SelectedItem; + string Path; + r32 Scroll; + u8 Input[256]; + s64 InputUsed; + text_edit_state InputEditState; + s32 SelectedItem; }; static workspace_file_lister_action *W_FileListerActionCopy(arena *Arena, workspace_file_lister_action *Action); diff --git a/code/vn_workspace_nav_editor.cpp b/code/vn_workspace_nav_editor.cpp index b510e37..b44e772 100644 --- a/code/vn_workspace_nav_editor.cpp +++ b/code/vn_workspace_nav_editor.cpp @@ -1,160 +1,442 @@ UI_CUSTOM_DRAW_CALLBACK(BuildNavViewDrawCallback) { - scene_view *SceneView = (scene_view *)Data; - SV_DrawBackground(SceneView, Box, Group); + scene_view *SceneView = (scene_view *)Data; + SV_DrawBackground(SceneView, Box, Group); +} + +static void W_NavEditorSerializeItems(workspace_view_nav_editor *Editor) +{ + temp Scratch = GetScratch(); + + //- sixten: find total size + u64 TotalSize = sizeof(u16); // item count + for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next) + { + string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]); + + TotalSize += 1; TotalSize += sizeof(u16); TotalSize += AssetName.Count; // texture id + TotalSize += 1; TotalSize += sizeof(r32); // scale + TotalSize += 1; TotalSize += sizeof(v2_r32); // origin + TotalSize += 1; TotalSize += sizeof(v2_r32); // p + TotalSize += 1; TotalSize += sizeof(u16); TotalSize += Node->Item.HoverText.Count; // hover text + TotalSize += 1; TotalSize += sizeof(u16); TotalSize += sizeof(u8); TotalSize += Node->Item.Action.Content.Count; // action + TotalSize += 1; // end of item + } + + u8 *FileData = PushArray(Scratch.Arena, u8, TotalSize); + u8 *Data = FileData; + + *(u16 *)Data = Editor->Items.Count; + Data += sizeof(u16); + +#define WriteByte(Op) do { *Data++ = Op; } while(0) +#define WriteField(Op, type, Member) do { *Data++ = Op; *(type *)Data = Member; Data += sizeof(type); } while(0) +#define WriteString(String) do { *(u16 *)Data = (u16)String.Count; Data += sizeof(u16); Copy(Data, String.Data, String.Count); Data += String.Count; } while(0) + + for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next) + { + scene_nav_item *Item = &Node->Item; + string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]); + WriteByte(S_NavItemOp_TextureID); WriteString(AssetName); + WriteField(S_NavItemOp_Scale, r32, Item->Scale); + WriteField(S_NavItemOp_Origin, v2_r32, Item->Origin); + WriteField(S_NavItemOp_P, v2_r32, Item->P); + WriteByte(S_NavItemOp_HoverText); WriteString(Item->HoverText); + WriteByte(S_NavItemOp_Action); WriteByte(Item->Action.Kind); WriteString(Item->Action.Content); + WriteByte(S_NavItemOp_None); + } +#undef WriteByte +#undef WriteField +#undef WriteString + + platform_file_handle File = Platform.OpenFile(PushFormat(Scratch.Arena, "%S/%S", Editor->FilePath, Editor->FileName), PlatformAccess_Write); + if(File.IsValid) + { + Platform.WriteFile(File, FileData, 0, TotalSize); + Platform.CloseFile(File); + } + ReleaseScratch(Scratch); +} + +static workspace_string_chunk *W_StringChunkAlloc(arena *Arena, workspace_string_chunk_list *FreeList) +{ + workspace_string_chunk *Result = FreeList->First; + if(Result) + { + DLLRemove(FreeList->First, FreeList->Last, Result); + *Result = {}; + } + else + { + Result = PushStruct(Arena, workspace_string_chunk); + } + + return(Result); +} + +static void W_StringChunkRelease(workspace_string_chunk_list *FreeList, workspace_string_chunk *Chunk) +{ + DLLInsertLast(FreeList->First, FreeList->Last, Chunk); } static scene_nav_item_node *W_SceneNavItemNodeAlloc(arena *Arena, workspace_view_nav_editor *Editor) { - scene_nav_item_node *Result = Editor->FirstFree; - if(Result) - { - DLLRemove(Editor->FirstFree, Editor->LastFree, Result); - *Result = {}; - } - else - { - Result = PushStruct(Arena, scene_nav_item_node); - } - - DLLInsertLast(Editor->Items.First, Editor->Items.Last, Result); - Editor->Items.Count += 1; - return(Result); + scene_nav_item_node *Result = Editor->FirstFree; + if(Result) + { + DLLRemove(Editor->FirstFree, Editor->LastFree, Result); + *Result = {}; + } + else + { + Result = PushStruct(Arena, scene_nav_item_node); + } + + Result->HoverTextStringChunk = W_StringChunkAlloc(Arena, &Editor->FreeStrings); + Result->ActionStringChunk = W_StringChunkAlloc(Arena, &Editor->FreeStrings); + + Result->Item.HoverText.Data = Result->HoverTextStringChunk->Data; + Result->Item.Action.Content.Data = Result->ActionStringChunk->Data; + + DLLInsertLast(Editor->Items.First, Editor->Items.Last, Result); + Editor->Items.Count += 1; + return(Result); } static void W_SceneNavItemNodeRelease(workspace_view_nav_editor *Editor, scene_nav_item_node *Node) { - DLLInsertLast(Editor->FirstFree, Editor->LastFree, Node); - Editor->Items.Count -= 1; + W_StringChunkRelease(&Editor->FreeStrings, Node->HoverTextStringChunk); + W_StringChunkRelease(&Editor->FreeStrings, Node->ActionStringChunk); + DLLRemove(Editor->Items.First, Editor->Items.Last, Node); + DLLInsertLast(Editor->FirstFree, Editor->LastFree, Node); + Editor->Items.Count -= 1; } -static void W_SetupNavEditor(workspace_view *View, string FileContents) +static void W_NavEditorInspectNode(workspace_view_nav_editor *Editor, scene_nav_item_node *Node) { - //- sixten(TODO): deserialize data + Editor->SelectedItem = Node; + Editor->TextureDropdownOpen = false; + Editor->NavActionDropdownOpen = false; +} + +static void W_NavEditorSetup(workspace_view *View, string FilePath, string FileName, string FileContents) +{ + workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data; + + //- sixten: setup file info + Editor->FileName = PushString(View->Arena, FileName); + Editor->FilePath = PushString(View->Arena, FilePath); + + //- sixten: deserialize data + if(FileContents.Count != 0) + { + u8 *DataBegin = FileContents.Data; + u8 *Byte = DataBegin; + + u16 ItemCount = *(u16 *)Byte; + Byte += 2; + + //- sixten: parse items + for(u64 ItemIndex = 0; ItemIndex < ItemCount; ItemIndex += 1) + { + scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor); + scene_nav_item *Item = &Node->Item; + for(;;) + { + switch(*Byte++) + { + case S_NavItemOp_TextureID: + { + string AssetName; + AssetName.Count = *(u16 *)Byte; + Byte += sizeof(u16); + AssetName.Data = Byte; + Byte += AssetName.Count; + + Item->TextureID = AssetID_Error; + for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1) + { + if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName)) + { + Item->TextureID = AssetIndex; + break; + } + } + } goto Next; + case S_NavItemOp_Scale: + { + Item->Scale = *(r32 *)Byte; + Byte += sizeof(r32); + } goto Next; + case S_NavItemOp_Origin: + { + Item->Origin = *(v2_r32 *)Byte; + Byte += sizeof(v2_r32); + } goto Next; + case S_NavItemOp_P: + { + Item->P = *(v2_r32 *)Byte; + Byte += sizeof(v2_r32); + } goto Next; + case S_NavItemOp_HoverText: + { + Item->HoverText.Count = *(u16 *)Byte; + Byte += 2; + Assert(Item->HoverText.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *)); + + Copy(Item->HoverText.Data, Byte, Item->HoverText.Count); + Byte += Item->HoverText.Count; + } goto Next; + case S_NavItemOp_Action: + { + Item->Action.Kind = (scene_nav_action_kind)*Byte; + Byte += 1; + + Item->Action.Content.Count = *(u16 *)Byte; + Byte += 2; + Assert(Item->Action.Content.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *)); + + Copy(Item->Action.Content.Data, Byte, Item->Action.Content.Count); + Byte += Item->Action.Content.Count; + } goto Next; + } + + //- sixten: no op found, assume item done + break; + + Next:; + } + } + } } static void W_BuildNavEditor(workspace_view *View) { - workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data; - scene_view *SceneView = SV_GetState(); - - UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) - UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) - UI_WidthFill UI_Height(UI_Em(2.0f, 1.0f)) - UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Workspace Nav Editor Toolbar")) - { - UI_Width(UI_TextContent(10, 1.0f)) UI_Font(Font_Icons) if(UI_ButtonF("%U", FontIcon_UserPlus).Clicked) - { - scene_nav_item_node *NewNode = W_SceneNavItemNodeAlloc(View->Arena, Editor); - NewNode->Item = G_DefaultSceneNavItem; - } - } - - UI_Row() - { - //- sixten: build inspector panel - UI_Width(UI_Percent(0.2f, 1.0f)) UI_HeightFill - UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder, StrLit("Workspace Nav Editor Inspector"))) - { - scene_nav_item_node *SelectedItem = Editor->SelectedItem; - if(SelectedItem) - { - } - else UI_WidthFill - { - UI_LabelF("No item selected."); - } - } - - //- sixten: build nav view - UI_Row() - { - //- sixten: calculate nav view size - r32 VerticalPad = 0; - r32 HorizontalPad = 0; - { - ui_box *ParentBox = UI_TopParent(); - v2_r32 ParentDim = DimOfRange(ParentBox->Rect); - - r32 WidthOverHeight = ParentDim.x / ParentDim.y; - r32 TargetRatio = 16.0f / 9.0f; - if(WidthOverHeight > TargetRatio) - { - VerticalPad = ParentDim.x - ParentDim.y*TargetRatio; - } - else - { - HorizontalPad = ParentDim.y - ParentDim.x/TargetRatio; - } - } - //- sixten: do the building of the nav view - UI_Padding(UI_Pixels(VerticalPad/2, 1)) - { - UI_Column() UI_Padding(UI_Pixels(HorizontalPad/2, 1)) - { - UI_SetNextWidth(UI_Percent(1, 0)); - UI_SetNextHeight(UI_Percent(1, 0)); - UI_SetNextLayoutAxis(Axis2_Y); - - ui_box *Box = UI_MakeBox(UI_BoxFlag_Clip, StrLit("Nav View")); - UI_EquipBoxCustomDrawCallback(Box, BuildNavViewDrawCallback, SceneView); - - v2_r32 BoxDim = UI_CalculateBoxDim(Box); - r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); - - //- sixten: build all nav items - s32 ItemIndex = 0; - for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next, ItemIndex += 1) - { - scene_nav_item *Item = &Node->Item; - - //- sixten: calculate item position - r32 AppliedScale = GlobalScale*Item->Scale; - render_handle Texture = TextureFromAssetID(Item->TextureID); - v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); - v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin); - v2_r32 Dim = TextureDim*AppliedScale; - v2_r32 OriginP = TextureOrigin*AppliedScale; - - //- sixten: build the item - scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info); - Data->Item = Item; - - UI_SetNextHoverCursor(PlatformCursor_Hand); - UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1)); - ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, - "View Item Box %i", ItemIndex); - UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data); - - //- sixten: handle signals - { - ui_signal Signal = UI_SignalFromBox(ItemBox); - Data->Signal = Signal; - - //- sixten: inspect pressed item - if(Signal.Pressed) - { - Editor->SelectedItem = Node; - } - - //- sixten: handle dragging - if(Signal.Dragging) - { - if(Signal.Pressed) - { - UI_StoreDragV2(Item->P); - } - - Item->P = UI_GetDragV2() + Signal.DragDelta/BoxDim*2; - } - } - - //- sixten: apply the calculated position - v2_r32 OffsetP = BoxDim*(V2R32(1, 1) + Item->P)*0.5f; - ItemBox->FixedP = (OffsetP-OriginP); - } - } - } - } - } + workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data; + scene_view *SceneView = SV_GetState(); + + UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) + UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) + UI_WidthFill UI_Height(UI_Em(2.0f, 1.0f)) + UI_LayoutAxis(Axis2_X) + UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Workspace Nav Editor Toolbar")) + { + UI_Width(UI_Em(2.0f, 1.0f)) UI_Font(Font_Icons) UI_CornerRadius(4.0f) + { + //- sixten: create nav item button + if(UI_ButtonF("%U", FontIcon_UserPlus).Clicked) + { + scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor); + + //- sixten: apply default options + Node->Item.Scale = 0.01f; + Node->Item.Origin = V2R32(0.5, 1.0f); + Node->Item.P = V2R32(0.0, 0.0f); + Node->Item.TextureID = AssetID_ArthurNormal; + } + + //- sixten: create save nav items button + if(UI_ButtonF("%U", FontIcon_Floppy).Clicked) + { + W_NavEditorSerializeItems(Editor); + } + + //- sixten: create force reload + if(UI_ButtonF("%U", FontIcon_ArrowsCW).Clicked) + { + SV_LoadNavItems(); + } + } + } + + UI_Row() + { + //- sixten: build inspector panel + scene_nav_item_node *SelectedItem = Editor->SelectedItem; + UI_SetNextLayoutAxis(Axis2_X); + UI_Width(UI_Em(24.0f*AC_AnimateValueF(SelectedItem != 0, 0, 0.3f, "Workspace Nav Editor Inspector %p", Editor), 1.0f)) UI_HeightFill + UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder, StrLit("Workspace Nav Editor Inspector"))) + UI_Padding(UI_Em(1, 1)) UI_Width(UI_Percent(1.0f, 0.0f)) UI_Column() + { + if(SelectedItem) + { + UI_CornerRadius(4.0f) UI_Width(UI_Percent(1, 1)) UI_Height(UI_TextContent(15, 1)) + { + //- sixten: inspect position + UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position X:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.x);} + UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position Y:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.y);} + + //- sixten: inspect scale + UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) + { + UI_LabelF("Scale:"); + UI_Font(Font_Monospace) + { + UI_SetNextHoverCursor(PlatformCursor_ArrowHorizontal); + ui_box *ScaleBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, "%f##Scale", SelectedItem->Item.Scale); + + ui_signal ScaleSignal = UI_SignalFromBox(ScaleBox); + if(ScaleSignal.Dragging) + { + if(ScaleSignal.Pressed) + { + UI_StoreDragR32(SelectedItem->Item.Scale); + } + + SelectedItem->Item.Scale = UI_GetDragR32() + ScaleSignal.DragDelta.x*0.001f; + } + } + } + + //- sixten: inspect texture + UI_Row(0, StrLit("Texture")) UI_Width(UI_Percent(0.5f, 1.0f)) + { + UI_LabelF("Texture:"); + UI_DropdownSelection(AssetNameLUT, ArrayCount(AssetNameLUT), &Editor->TextureDropdownOpen, &SelectedItem->Item.TextureID); + } + + //- sixten: inspect nav action kind + UI_Row(0, StrLit("Nav Action")) UI_Width(UI_Percent(0.5f, 1.0f)) + { + UI_LabelF("Nav Action Type:"); + char *Actions[] = { "None", "Trigger Proc", "Change Scene" }; + UI_DropdownSelection(Actions, ArrayCount(Actions), &Editor->NavActionDropdownOpen, (s32 *)&SelectedItem->Item.Action); + } + + //- sixten: inspect nav action contents + UI_Row(0, StrLit("Nav Action String")) UI_Width(UI_Percent(0.5f, 1.0f)) + { + UI_LabelF("Nav Action Contents:"); + ui_signal Signal = UI_LineEdit(&Editor->NavActionStringEditState, ArrayCount(SelectedItem->ActionStringChunk->Data), SelectedItem->ActionStringChunk->Data, &SelectedItem->Item.Action.Content.Count, StrLit("Nav Action Text"), Editor->ActiveTextThing == 1); + if(AreEqual(UI_ActiveKey(), Signal.Box->Key)) + { + Editor->ActiveTextThing = 1; + } + else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 1) + { + Editor->ActiveTextThing = 0; + } + } + + //- sixten: inspect hover text + UI_Row(0, StrLit("Hover Text")) UI_Width(UI_Percent(0.5f, 1.0f)) + { + UI_LabelF("Hover Text:"); + ui_signal Signal = UI_LineEdit(&Editor->HoverTextEditState, ArrayCount(SelectedItem->HoverTextStringChunk->Data), SelectedItem->HoverTextStringChunk->Data, &SelectedItem->Item.HoverText.Count, StrLit("Hover Text Input"), Editor->ActiveTextThing == 2); + if(AreEqual(UI_ActiveKey(), Signal.Box->Key)) + { + Editor->ActiveTextThing = 2; + } + else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 2) + { + Editor->ActiveTextThing = 0; + } + } + + UI_Spacer(UI_TextContent(15, 1)); + + UI_Row() UI_FillPadding + { + UI_Width(UI_TextContent(15, 1)) if(UI_ButtonF("Remove Item").Pressed) + { + W_SceneNavItemNodeRelease(Editor, SelectedItem); + Editor->SelectedItem = 0; + } + } + } + } + } + + //- sixten: build nav view + UI_Width(UI_Percent(1, 0)) UI_Height(UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip, "Workspace View Nav Editor Thing")) + { + //- sixten: calculate nav view size + v2_r32 ParentDim = DimOfRange(UI_TopParent()->Rect); + v2_r32 BoxDim = ParentDim; + + r32 TargetRatio = 16.0f/9.0f; + r32 ActualRatio = ParentDim.x/ParentDim.y; + + if(ActualRatio>TargetRatio) + { + BoxDim.x = BoxDim.y*TargetRatio; + } + else + { + BoxDim.y = BoxDim.x/TargetRatio; + } + + UI_SetNextWidth(UI_Pixels(BoxDim.x, 1)); + UI_SetNextHeight(UI_Pixels(BoxDim.y, 1)); + UI_SetNextLayoutAxis(Axis2_Y); + UI_SetNextFixedP((ParentDim-BoxDim)*0.5f); + ui_box *Box = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_Clickable, "Nav View %p", View); + UI_EquipBoxCustomDrawCallback(Box, BuildNavViewDrawCallback, SceneView); + + r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim); + + //- sixten: build all nav items + s32 ItemIndex = 0; + UI_Parent(Box) for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next, ItemIndex += 1) + { + scene_nav_item *Item = &Node->Item; + + //- sixten: calculate item position + r32 AppliedScale = GlobalScale*Item->Scale; + render_handle Texture = TextureFromAssetID(Item->TextureID); + v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture)); + v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin); + v2_r32 Dim = TextureDim*AppliedScale; + v2_r32 OriginP = TextureOrigin*AppliedScale; + + //- sixten: build the item + scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info); + Data->Item = Item; + + UI_SetNextHoverCursor(PlatformCursor_Hand); + UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1)); + ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|((Node==Editor->SelectedItem)?UI_BoxFlag_DrawBorder:0), + "View Item Box %i", ItemIndex); + UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data); + + if(Node == Editor->SelectedItem) + { + // sixten(TODO): this: + //ui_box *OriginBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, "View Item Origin Box %i", ItemIndex); + } + //- sixten: handle signals + { + ui_signal Signal = UI_SignalFromBox(ItemBox); + Data->Signal = Signal; + + //- sixten: inspect pressed item + if(Signal.Clicked && Signal.DragDelta == V2R32(0, 0)) + { + W_NavEditorInspectNode(Editor, Node); + } + + //- sixten: handle dragging + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragV2(Item->P); + } + + Item->P = UI_GetDragV2() + Signal.DragDelta/BoxDim*2; + } + } + + //- sixten: apply the calculated position + v2_r32 OffsetP = BoxDim*(V2R32(1, 1) + Item->P)*0.5f; + ItemBox->FixedP = (OffsetP-OriginP); + } + + ui_signal BackgroundSignal = UI_SignalFromBox(Box); + if(BackgroundSignal.Pressed) + { + W_NavEditorInspectNode(Editor, 0); + } + } + } } \ No newline at end of file diff --git a/code/vn_workspace_nav_editor.h b/code/vn_workspace_nav_editor.h index 2c6476a..faa0926 100644 --- a/code/vn_workspace_nav_editor.h +++ b/code/vn_workspace_nav_editor.h @@ -3,35 +3,68 @@ #ifndef VN_WORKSPACE_NAV_EDITOR_H #define VN_WORKSPACE_NAV_EDITOR_H +#define W_STRING_CHUNK_SIZE 128 + +struct workspace_string_chunk +{ + workspace_string_chunk *Next; + workspace_string_chunk *Prev; + u8 Data[W_STRING_CHUNK_SIZE - 2*sizeof(workspace_string_chunk *)]; +}; + +struct workspace_string_chunk_list +{ + workspace_string_chunk *First; + workspace_string_chunk *Last; +}; + struct scene_nav_item_node { - scene_nav_item_node *Next; - scene_nav_item_node *Prev; - - scene_nav_item Item; + scene_nav_item_node *Next; + scene_nav_item_node *Prev; + + workspace_string_chunk *HoverTextStringChunk; + workspace_string_chunk *ActionStringChunk; + scene_nav_item Item; }; struct scene_nav_item_list { - scene_nav_item_node *First; - scene_nav_item_node *Last; - u64 Count; + scene_nav_item_node *First; + scene_nav_item_node *Last; + u64 Count; }; struct workspace_view_nav_editor { - //- sixten: items - scene_nav_item_list Items; - scene_nav_item_node *FirstFree; - scene_nav_item_node *LastFree; - - scene_nav_item_node *SelectedItem; + //- sixten: items + scene_nav_item_list Items; + scene_nav_item_node *FirstFree; + scene_nav_item_node *LastFree; + + //- sixten: strings + workspace_string_chunk_list FreeStrings; + + //- sixten: inspect info + scene_nav_item_node *SelectedItem; + b32 TextureDropdownOpen; + b32 NavActionDropdownOpen; + text_edit_state NavActionStringEditState; + text_edit_state HoverTextEditState; + s32 ActiveTextThing; + + //- sixten: file info + string FileName; + string FilePath; }; +static void W_NavEditorSerializeItems(workspace_view_nav_editor *Editor); + static scene_nav_item_node *W_SceneNavItemNodeAlloc(arena *Arena, workspace_view_nav_editor *Editor); static void W_SceneNavItemNodeRelease(workspace_view_nav_editor *Editor, scene_nav_item_node *Node); -static void W_SetupNavEditor(workspace_view *View, string FileContents); +static void W_NavEditorInspectNode(scene_nav_item_node *Node); +static void W_NavEditorSetup(workspace_view *View, string FilePath, string FileName, string FileContents); static void W_BuildNavEditor(workspace_view *View); #endif //VN_WORKSPACE_NAV_EDITOR_H diff --git a/code/vn_workspace_text_editor.cpp b/code/vn_workspace_text_editor.cpp index f7f3618..aecc19a 100644 --- a/code/vn_workspace_text_editor.cpp +++ b/code/vn_workspace_text_editor.cpp @@ -3,38 +3,38 @@ static mutable_string MutableStringAllocate(u64 Size) { - mutable_string Result = {}; - Result.Arena = ArenaAlloc(Size); - ArenaSetAlign(Result.Arena, 1); - Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0LL); - return(Result); + mutable_string Result = {}; + Result.Arena = ArenaAlloc(Size); + ArenaSetAlign(Result.Arena, 1); + Result.String = MakeString(PushArray(Result.Arena, u8, 1), 0LL); + return(Result); } static void MutableStringRelease(mutable_string *String) { - ArenaRelease(String->Arena); + ArenaRelease(String->Arena); } static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceString, range1_s64 Range) { - Range = Intersection(Range, Range1S64(0, MutString->String.Count)); - - s64 NewCount = MutString->String.Count+ReplaceString.Count-DimOfRange(Range); - if(NewCount > MutString->String.Count) - { - s64 ToAllocate = NewCount-MutString->String.Count; - PushArrayNoClear(MutString->Arena, u8, ToAllocate); - } - else if(NewCount < MutString->String.Count) - { - ArenaPopTo(MutString->Arena, sizeof(arena)+NewCount+1); - } - - Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-Range.Min-DimOfRange(Range)); - Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count); - - MutString->String.Count = NewCount; - MutString->String.Data[NewCount] = 0; + Range = Intersection(Range, Range1S64(0, MutString->String.Count)); + + s64 NewCount = MutString->String.Count+ReplaceString.Count-DimOfRange(Range); + if(NewCount > MutString->String.Count) + { + s64 ToAllocate = NewCount-MutString->String.Count; + PushArrayNoClear(MutString->Arena, u8, ToAllocate); + } + else if(NewCount < MutString->String.Count) + { + ArenaPopTo(MutString->Arena, sizeof(arena)+NewCount+1); + } + + Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-Range.Min-DimOfRange(Range)); + Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count); + + MutString->String.Count = NewCount; + MutString->String.Data[NewCount] = 0; } //////////////////////////////// @@ -42,21 +42,21 @@ static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceS static history_entry HistoryEntry(arena *Arena, string ReplaceString, range1_s64 Range) { - // sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory - history_entry Entry = {}; - Entry.ReplaceString = PushString(Arena, ReplaceString); - Entry.Range = Range; - return(Entry); + // sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory + history_entry Entry = {}; + Entry.ReplaceString = PushString(Arena, ReplaceString); + Entry.Range = Range; + return(Entry); } static void AppendToHistory(arena *Arena, history_list *List, history_entry Forward, history_entry Backward) { - // sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory - history_node *Node = PushStructNoClear(Arena, history_node); - Node->Forward = Forward; - Node->Backward = Backward; - - SenDLLInsertLast(&List->Sentinel, Node); + // sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory + history_node *Node = PushStructNoClear(Arena, history_node); + Node->Forward = Forward; + Node->Backward = Backward; + + SenDLLInsertLast(&List->Sentinel, Node); } //////////////////////////////// @@ -64,73 +64,73 @@ static void AppendToHistory(arena *Arena, history_list *List, history_entry Forw static workspace_text_data W_TextDataFromString(arena *Arena, string Text) { - temporary_memory Scratch = GetScratch(&Arena, 1); - - //- sixten: clear the target arena - ArenaClear(Arena); - - //- sixten: tokenize the text - tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text); - token_array Tokens = TokenizeResult.Tokens; - - //- sixten: gather all line ranges - range1_s64_list Lines = {}; - { - u8 *TextBegin = Text.Data; - u8 *TextEnd = TextBegin + Text.Count; - range1_s64 Range = Range1S64(0, 0); - for(u8 *Char = TextBegin; Char <= TextEnd; Char += 1) - { - Range.Max += 1; - - //- sixten: push line range on newline and EOF - if(Char == TextEnd || *Char == '\n') - { - Range1S64ListPush(Scratch.Arena, &Lines, Range); - Range = Range1S64(Range.Max, Range.Max); - } - } - } - - //- sixten: fill & return - workspace_text_data Result = {}; - { - Result.Tokens = Tokens; - Result.Lines = Range1S64ArrayFromList(Arena, &Lines);; - } - ReleaseScratch(Scratch); - return(Result); + temp Scratch = GetScratch(&Arena, 1); + + //- sixten: clear the target arena + ArenaClear(Arena); + + //- sixten: tokenize the text + tokenize_result TokenizeResult = T_TokenizeFromText(Arena, Text); + token_array Tokens = TokenizeResult.Tokens; + + //- sixten: gather all line ranges + range1_s64_list Lines = {}; + { + u8 *TextBegin = Text.Data; + u8 *TextEnd = TextBegin + Text.Count; + range1_s64 Range = Range1S64(0, 0); + for(u8 *Char = TextBegin; Char <= TextEnd; Char += 1) + { + Range.Max += 1; + + //- sixten: push line range on newline and EOF + if(Char == TextEnd || *Char == '\n') + { + Range1S64ListPush(Scratch.Arena, &Lines, Range); + Range = Range1S64(Range.Max, Range.Max); + } + } + } + + //- sixten: fill & return + workspace_text_data Result = {}; + { + Result.Tokens = Tokens; + Result.Lines = Range1S64ArrayFromList(Arena, &Lines);; + } + ReleaseScratch(Scratch); + return(Result); } static void W_TextEditorApplyChanges(workspace_view_text_editor *Editor) { - 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); - } + 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 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); + temp 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); } //////////////////////////////// @@ -138,600 +138,600 @@ static void W_SaveTextEditorToFile(workspace_view_text_editor *Editor) static UI_CUSTOM_DRAW_CALLBACK(W_TextEditorDrawCallback) { - temporary_memory Scratch = GetScratch(); - workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data; - - //- sixten: get dimensions & scroll offset from container - ui_box *ContainerBox = Editor->ContainerBox; - range2_r32 ParentRect = ContainerBox->Rect; - v2 ParentDim = DimOfRange(ParentRect); - v2 Offset = Box->Parent->Offset; - - //- sixten: rendering properties - r32 FontSize = Editor->FontSize; - r32 LineHeight = FontSize + 4.0f; - - //- sixten: calculate the dimensions of the glyphs - glyph *Glyph = GetGlyph(Atlas, Font_Monospace, 'A', FontSize, 0); - r32 GlyphAdvance = Glyph->Advance; - - //- sixten: find the text point - text_point CursorTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); - text_point MarkTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Mark); - - //- sixten: get the line count - range1_s64_array *Lines = &Editor->Lines; - s64 LineCount = Lines->Count; - - //- sixten: calculate the text dim - Editor->TextDim = V2(1900, LineCount*LineHeight); - - //- sixten: calculate the line margin dim - s32 LineMarginDigitsRequired = 6; - v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y); - - //- sixten: tokenize text - tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Editor->Text.String); - token_array Tokens = TokenizeResult.Tokens; - token *TokensBegin = Tokens.Tokens; - token *TokensEnd = TokensBegin + Tokens.Count; - - //- sixten: find the first visible token - token *VisibleTokensBegin = TokensBegin; - s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount); - for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1) - { - if(VisibleTokensBegin->Kind == TokenKind_Newline) - { - LinesFound += 1; - } - } - - //- sixten: find the last visible token - token *VisibleTokensEnd = VisibleTokensBegin; - s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine); - for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1) - { - if(VisibleTokensEnd->Kind == TokenKind_Newline) - { - LinesFound += 1; - } - } - - //- sixten: draw line numbers & line highlights - { - //- sixten: draw the background - v4 LineMarginColor = ColorFromHex(0x10203080); - range2_r32 LineMarginBox = Range2R32(ParentRect.Min, ParentRect.Min+LineMarginDim); - PushQuad(Group, LineMarginBox, LineMarginColor, LineMarginColor, LineMarginColor, LineMarginColor, 0, 0, 0); - - //- sixten: draw the numbers - v2_r32 LineOffset = Box->Rect.Min; - for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) - { - r32 LineY = LineOffset.y + LineIndex*LineHeight; - PushTextF(Group, Atlas, Font_Monospace, V2(0, LineY), FontSize, Color_Grey, "%*.i", LineMarginDigitsRequired, LineIndex+1); - - if(LineIndex + 1 == CursorTextP.Line) - { - v4_r32 LineHighlightColor = ColorFromHex(0x10204080); - range2_r32 LineHighlightBox = Range2R32(V2(LineMarginBox.Max.x, LineY), V2(Box->Rect.Max.x, LineY+LineHeight)); - PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 0, 0, 0); - } - } - } - - //- sixten: render tokens - v2 BaseTokenP = Box->Rect.Min+V2(LineMarginDim.x, TopMostLine*LineHeight); - v2 TokenP = BaseTokenP; - for(token *Token = VisibleTokensBegin; Token < VisibleTokensEnd; Token += 1) - { - string TokenString = T_StringFromToken(Editor->Text.String, *Token); - - //- sixten: get color & font from token - font_id Font = Font_Monospace; - v4 Color = Color_Magenta; - 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_KeywordsBegin && Token->Kind < TokenKind_KeywordsEnd) - { - if(Token->Kind == TokenKind_True || Token->Kind == TokenKind_False) - { - Color = ColorFromHex(0xffa900ff); - } - else - { - Color = ColorFromHex(0xf0c674ff); - } - } - else if(Token->Kind == TokenKind_Identifier) - { - 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))) - { - if(Token->Kind == TokenKind_Comment) - { - //- sixten: advance to newline and push text - // sixten(TODO): proper multiline comment rendering. - u8 *TextBegin = TokenString.Data; - u8 *TextEnd = TextBegin+TokenString.Count; - u8 *Marker = TextBegin; - for(u8 *Byte = TextBegin; Byte <= TextEnd; Byte += 1) - { - if(*Byte == '\n' || Byte == TextEnd) - { - PushText(Group, Atlas, Font, TokenP, FontSize, Color, MakeString(Marker, Byte-Marker)); - Marker = Byte+1; - - if(*Byte == '\n' && Byte != TextEnd) - { - TokenP.x = BaseTokenP.x; - TokenP.y += LineHeight; - } - } - } - } - else - { - 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 - { - if(Token->Kind == TokenKind_Newline) - { - TokenP.x = BaseTokenP.x; - TokenP.y += LineHeight; - } - else - { - u8 *StringBegin = TokenString.Data; - u8 *StringEnd = StringBegin + TokenString.Count; - for(u8 *Char = StringBegin; Char < StringEnd; Char += 1) - { - if(*Char == ' ' || *Char == '\t') - { - TokenP.x += GlyphAdvance; - } - } - } - } - } - - //- sixten: render cursor - { - s64 LineIndex = CursorTextP.Line-1; - string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); - s64 ColumnIndex = CursorTextP.Column-1; - s64 ColumnOffset = UTF8OffsetFromIndex(Line, ColumnIndex); - - v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+ColumnOffset*GlyphAdvance, LineIndex*LineHeight); - 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); - PushQuad(Group, CursorRect, CursorColor, CursorColor, CursorColor, CursorColor, 2, 0.4, 0); - } - - //- sixten: render the selection - { - text_range Selection = TextRange(CursorTextP, MarkTextP); - range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line); - for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) - { - if(Contains(LineRange, LineIndex + 1)) - { - range1_s64 ColumnRange = Lines->Ranges[LineIndex]; - range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange)); - if(LineIndex+1 == LineRange.Min && LineIndex+1 == LineRange.Max) - { - NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min); - } - else if(LineIndex+1 == LineRange.Min) - { - NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange)); - } - else if(LineIndex+1 == LineRange.Max) - { - NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min); - } - - string Line = Substring(Editor->Text.String, ColumnRange); - range1_s64 ColumnOffsetRange = Range1S64(UTF8OffsetFromIndex(Line, NormalizedColumnRange.Min), - UTF8OffsetFromIndex(Line, NormalizedColumnRange.Max)); - - r32 LineY = LineIndex*LineHeight; - v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C); - range2_r32 LineHighlightBox = Range2R32(Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY), - Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight)); - PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0); - } - } - } - ReleaseScratch(Scratch); + temp Scratch = GetScratch(); + workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data; + + //- sixten: get dimensions & scroll offset from container + ui_box *ContainerBox = Editor->ContainerBox; + range2_r32 ParentRect = ContainerBox->Rect; + v2 ParentDim = DimOfRange(ParentRect); + v2 Offset = Box->Parent->Offset; + + //- sixten: rendering properties + r32 FontSize = Editor->FontSize; + r32 LineHeight = FontSize + 4.0f; + + //- sixten: calculate the dimensions of the glyphs + glyph *Glyph = GetGlyph(Atlas, Font_Monospace, 'A', FontSize, 0); + r32 GlyphAdvance = Glyph->Advance; + + //- sixten: find the text point + text_point CursorTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); + text_point MarkTextP = TextPointFromOffset(Editor->Text.String, Editor->EditState.Mark); + + //- sixten: get the line count + range1_s64_array *Lines = &Editor->Lines; + s64 LineCount = Lines->Count; + + //- sixten: calculate the text dim + Editor->TextDim = V2(1900, LineCount*LineHeight); + + //- sixten: calculate the line margin dim + s32 LineMarginDigitsRequired = 6; + v2_r32 LineMarginDim = V2((LineMarginDigitsRequired)*GlyphAdvance, ParentRect.Max.y - ParentRect.Min.y); + + //- sixten: tokenize text + tokenize_result TokenizeResult = T_TokenizeFromText(Scratch.Arena, Editor->Text.String); + token_array Tokens = TokenizeResult.Tokens; + token *TokensBegin = Tokens.Tokens; + token *TokensEnd = TokensBegin + Tokens.Count; + + //- sixten: find the first visible token + token *VisibleTokensBegin = TokensBegin; + s64 TopMostLine = Min((s64)Floor(-Offset.y / LineHeight), LineCount); + for(s64 LinesFound = 0; LinesFound < TopMostLine && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1) + { + if(VisibleTokensBegin->Kind == TokenKind_Newline) + { + LinesFound += 1; + } + } + + //- sixten: find the last visible token + token *VisibleTokensEnd = VisibleTokensBegin; + s64 LinesOnScreen = Min((s64)Floor(ParentDim.y / LineHeight)+1, LineCount-TopMostLine); + for(s64 LinesFound = 0; LinesFound < LinesOnScreen && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1) + { + if(VisibleTokensEnd->Kind == TokenKind_Newline) + { + LinesFound += 1; + } + } + + //- sixten: draw line numbers & line highlights + { + //- sixten: draw the background + v4 LineMarginColor = ColorFromHex(0x10203080); + range2_r32 LineMarginBox = Range2R32(ParentRect.Min, ParentRect.Min+LineMarginDim); + PushQuad(Group, LineMarginBox, LineMarginColor, LineMarginColor, LineMarginColor, LineMarginColor, 0, 0, 0); + + //- sixten: draw the numbers + v2_r32 LineOffset = Box->Rect.Min; + for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) + { + r32 LineY = LineOffset.y + LineIndex*LineHeight; + PushTextF(Group, Atlas, Font_Monospace, V2(0, LineY), FontSize, Color_Grey, "%*.i", LineMarginDigitsRequired, LineIndex+1); + + if(LineIndex + 1 == CursorTextP.Line) + { + v4_r32 LineHighlightColor = ColorFromHex(0x10204080); + range2_r32 LineHighlightBox = Range2R32(V2(LineMarginBox.Max.x, LineY), V2(Box->Rect.Max.x, LineY+LineHeight)); + PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 0, 0, 0); + } + } + } + + //- sixten: render tokens + v2 BaseTokenP = Box->Rect.Min+V2(LineMarginDim.x, TopMostLine*LineHeight); + v2 TokenP = BaseTokenP; + for(token *Token = VisibleTokensBegin; Token < VisibleTokensEnd; Token += 1) + { + string TokenString = T_StringFromToken(Editor->Text.String, *Token); + + //- sixten: get color & font from token + font_id Font = Font_Monospace; + v4 Color = Color_Magenta; + 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_KeywordsBegin && Token->Kind < TokenKind_KeywordsEnd) + { + if(Token->Kind == TokenKind_True || Token->Kind == TokenKind_False) + { + Color = ColorFromHex(0xffa900ff); + } + else + { + Color = ColorFromHex(0xf0c674ff); + } + } + else if(Token->Kind == TokenKind_Identifier) + { + 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))) + { + if(Token->Kind == TokenKind_Comment) + { + //- sixten: advance to newline and push text + // sixten(TODO): proper multiline comment rendering. + u8 *TextBegin = TokenString.Data; + u8 *TextEnd = TextBegin+TokenString.Count; + u8 *Marker = TextBegin; + for(u8 *Byte = TextBegin; Byte <= TextEnd; Byte += 1) + { + if(*Byte == '\n' || Byte == TextEnd) + { + PushText(Group, Atlas, Font, TokenP, FontSize, Color, MakeString(Marker, Byte-Marker)); + Marker = Byte+1; + + if(*Byte == '\n' && Byte != TextEnd) + { + TokenP.x = BaseTokenP.x; + TokenP.y += LineHeight; + } + } + } + } + else + { + 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 + { + if(Token->Kind == TokenKind_Newline) + { + TokenP.x = BaseTokenP.x; + TokenP.y += LineHeight; + } + else + { + u8 *StringBegin = TokenString.Data; + u8 *StringEnd = StringBegin + TokenString.Count; + for(u8 *Char = StringBegin; Char < StringEnd; Char += 1) + { + if(*Char == ' ' || *Char == '\t') + { + TokenP.x += GlyphAdvance; + } + } + } + } + } + + //- sixten: render cursor + { + s64 LineIndex = CursorTextP.Line-1; + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); + s64 ColumnIndex = CursorTextP.Column-1; + s64 ColumnOffset = UTF8OffsetFromIndex(Line, ColumnIndex); + + v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+ColumnOffset*GlyphAdvance, LineIndex*LineHeight); + 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); + PushQuad(Group, CursorRect, CursorColor, CursorColor, CursorColor, CursorColor, 2, 0.4, 0); + } + + //- sixten: render the selection + { + text_range Selection = TextRange(CursorTextP, MarkTextP); + range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line); + for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1) + { + if(Contains(LineRange, LineIndex + 1)) + { + range1_s64 ColumnRange = Lines->Ranges[LineIndex]; + range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange)); + if(LineIndex+1 == LineRange.Min && LineIndex+1 == LineRange.Max) + { + NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min); + } + else if(LineIndex+1 == LineRange.Min) + { + NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange)); + } + else if(LineIndex+1 == LineRange.Max) + { + NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min); + } + + string Line = Substring(Editor->Text.String, ColumnRange); + range1_s64 ColumnOffsetRange = Range1S64(UTF8OffsetFromIndex(Line, NormalizedColumnRange.Min), + UTF8OffsetFromIndex(Line, NormalizedColumnRange.Max)); + + r32 LineY = LineIndex*LineHeight; + v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C); + range2_r32 LineHighlightBox = Range2R32(Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY), + Box->Rect.Min+V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight)); + PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0); + } + } + } + ReleaseScratch(Scratch); } static b32 W_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event) { - b32 CursorHasBeenModified = false; - temporary_memory Scratch = GetScratch(); - text_action Action = MultiLineTextActionFromEvent(Event); - if(IsValid(&Action)) - { - text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1); - - if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString)) - { - //- sixten: append to the history - { - history_list *List = &Editor->History; - - //- sixten: remove the pre-existing history if needed - if(List->Sentinel.Prev != List->At) - { - // sixten(TODO): instead of just removing the links to the old memory, find some way to manage it. - List->Sentinel.Prev->Next = List->At; - List->Sentinel.Prev = List->At; - } - - range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark); - AppendToHistory(Editor->HistoryArena, List, - HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range), - HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count))); - - List->At = List->Sentinel.Prev; - } - //- sixten: apply the text action - string ReplaceString = RemoveAll(Scratch.Arena, Op.ReplaceString, '\r'); - MutableStringReplaceRange(&Editor->Text, ReplaceString, Op.Range); - W_TextEditorApplyChanges(Editor); - } - - CursorHasBeenModified = true; - Editor->EditState.Cursor = Op.NewCursor; - Editor->EditState.Mark = Op.NewMark; - } - ReleaseScratch(Scratch); - return(CursorHasBeenModified); + b32 CursorHasBeenModified = false; + temp Scratch = GetScratch(); + text_action Action = MultiLineTextActionFromEvent(Event); + if(IsValid(&Action)) + { + text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1); + + if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString)) + { + //- sixten: append to the history + { + history_list *List = &Editor->History; + + //- sixten: remove the pre-existing history if needed + if(List->Sentinel.Prev != List->At) + { + // sixten(TODO): instead of just removing the links to the old memory, find some way to manage it. + List->Sentinel.Prev->Next = List->At; + List->Sentinel.Prev = List->At; + } + + range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark); + AppendToHistory(Editor->HistoryArena, List, + HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range), + HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count))); + + List->At = List->Sentinel.Prev; + } + //- sixten: apply the text action + string ReplaceString = RemoveAll(Scratch.Arena, Op.ReplaceString, '\r'); + MutableStringReplaceRange(&Editor->Text, ReplaceString, Op.Range); + W_TextEditorApplyChanges(Editor); + } + + CursorHasBeenModified = true; + Editor->EditState.Cursor = Op.NewCursor; + Editor->EditState.Mark = Op.NewMark; + } + ReleaseScratch(Scratch); + return(CursorHasBeenModified); } 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); - } - } + 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 = 13.0f; - r32 LineHeight = FontSize + 4.0f; - - //- sixten: calculate the dimensions of the glyphs - glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0); - r32 GlyphAdvance = Glyph->Advance; - - //- sixten: calculate the line margin dim - s32 LineMarginDigitsRequired = 6; - r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance; - - { - //- sixten: build & handle the text editor - ui_box *EditorBox = 0; - 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 - Editor->ContainerBox = UI_TopParent()->Parent->Parent; - - 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); - 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(W_ViewIsCurrent(View)) - { - //- sixten: handle history - { - history_list *List = &Editor->History; - - //- sixten: undo - if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl)) - { - history_node *Node = List->At; - if(Node != &List->Sentinel) - { - //- sixten: get entry & apply - history_entry Entry = Node->Backward; - MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - W_TextEditorApplyChanges(Editor); - Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; - CursorHasBeenModified = true; - - List->At = Node->Prev; - } - } - - //- sixten: redo - if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl)) - { - history_node *Node = List->At->Next; - if(Node != &List->Sentinel) - { - //- sixten: get entry & apply - history_entry Entry = Node->Forward; - MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); - W_TextEditorApplyChanges(Editor); - Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; - CursorHasBeenModified = true; - - List->At = Node; - } - } - } - - //- 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)) - { - Editor->EditState.Mark = 0; - Editor->EditState.Cursor = Editor->Text.String.Count; - } - - //- sixten: keyboard input -> text op - for(platform_event *Event = UI_EventList()->First; - Event != 0; - Event = Event->Next) - { - if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) - { - //- sixten: auto-indent - s64 Indent = 0; - if(Event->Codepoint == '\n') - { - string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[Editor->LastTextPoint.Line-1]); - if(Line.Data[Line.Count-1] == '\n') - { - Line.Count -= 1; - } - - for(u8 *Data = Line.Data; *Data == '\t' && Data < Line.Data+Line.Count; Data += 1) - { - Indent += 1; - } - } - - //- sixten: auto close bracket - 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); - } - - //- 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 - { - platform_event FakeTab = {}; - FakeTab.Codepoint = '\t'; - FakeTab.Type = PlatformEvent_Text; - - for(s64 IndentIndex = 0; IndentIndex < Indent; IndentIndex += 1) - { - CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeTab); - } - } - } - } - } - - //- sixten: right-click dropdown - { - if(Editor->DropdownActive) - { - UI_Tooltip - { - UI_SetNextFixedP(Editor->DropdownP); - UI_SetNextWidth(UI_Em(20, 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 | - UI_BoxFlag_Clip | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY, StrLit("Text Editor Dropdown"))) - UI_Width(UI_Percent(1, 1)) UI_Height(UI_Em(1.66, 1)) - UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) - UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) - UI_CornerRadius(2) - { - // sixten(TODO): MAKE THESE BUTTONS ACTUALLY DO STUFF!!! - 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; - } - } - } - } - } - - ui_signal Signal = UI_SignalFromBox(EditorBox); - - if(Signal.Pressed || (Signal.PressedRight && (Editor->EditState.Cursor == Editor->EditState.Mark))) - { - //- sixten: translate mouse position to text point - v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); - s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); - - string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); - s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); - s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); - - text_point Point = {LineIndex + 1, ColumnIndex + 1}; - Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); - } - - if(Signal.Dragging) - { - //- sixten: translate mouse position to text point - v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); - s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); - string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); - s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); - s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); - - text_point Point = {LineIndex + 1, ColumnIndex + 1}; - Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); - - CursorHasBeenModified = true; - - Editor->DropdownActive = false; - } - - if(Signal.PressedRight) - { - Editor->DropdownActive = true; - Editor->DropdownP = UI_MouseP(); - Editor->DropdownTransition = 0; - } - - //- sixten: update eventual text point extents - if(CursorHasBeenModified) - { - text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); - if(Editor->LastTextPoint.Line == Point.Line) - { - Editor->LastTextPoint = Point; - } - else - { - Editor->LastTextPoint.Line = Point.Line; - Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column); - } - } - } - ReleaseScratch(Scratch); + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + + temp Scratch = GetScratch(); + + //- sixten: rendering properties + r32 FontSize = Editor->FontSize = 13.0f; + r32 LineHeight = FontSize + 4.0f; + + //- sixten: calculate the dimensions of the glyphs + glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0); + r32 GlyphAdvance = Glyph->Advance; + + //- sixten: calculate the line margin dim + s32 LineMarginDigitsRequired = 6; + r32 LineMarginWidth = (LineMarginDigitsRequired)*GlyphAdvance; + + { + //- sixten: build & handle the text editor + ui_box *EditorBox = 0; + 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 + Editor->ContainerBox = UI_TopParent()->Parent->Parent; + + 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); + 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(W_ViewIsCurrent(View)) + { + //- sixten: handle history + { + history_list *List = &Editor->History; + + //- sixten: undo + if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl)) + { + history_node *Node = List->At; + if(Node != &List->Sentinel) + { + //- sixten: get entry & apply + history_entry Entry = Node->Backward; + MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); + W_TextEditorApplyChanges(Editor); + Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; + CursorHasBeenModified = true; + + List->At = Node->Prev; + } + } + + //- sixten: redo + if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl)) + { + history_node *Node = List->At->Next; + if(Node != &List->Sentinel) + { + //- sixten: get entry & apply + history_entry Entry = Node->Forward; + MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range); + W_TextEditorApplyChanges(Editor); + Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count; + CursorHasBeenModified = true; + + List->At = Node; + } + } + } + + //- 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)) + { + Editor->EditState.Mark = 0; + Editor->EditState.Cursor = Editor->Text.String.Count; + } + + //- sixten: keyboard input -> text op + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) + { + //- sixten: auto-indent + s64 Indent = 0; + if(Event->Codepoint == '\n') + { + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[Editor->LastTextPoint.Line-1]); + if(Line.Data[Line.Count-1] == '\n') + { + Line.Count -= 1; + } + + for(u8 *Data = Line.Data; *Data == '\t' && Data < Line.Data+Line.Count; Data += 1) + { + Indent += 1; + } + } + + //- sixten: auto close bracket + 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); + } + + //- 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 + { + platform_event FakeTab = {}; + FakeTab.Codepoint = '\t'; + FakeTab.Type = PlatformEvent_Text; + + for(s64 IndentIndex = 0; IndentIndex < Indent; IndentIndex += 1) + { + CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeTab); + } + } + } + } + } + + //- sixten: right-click dropdown + { + if(Editor->DropdownActive) + { + UI_Tooltip + { + UI_SetNextFixedP(Editor->DropdownP); + UI_SetNextWidth(UI_Em(20, 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 | + UI_BoxFlag_Clip | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, StrLit("Text Editor Dropdown"))) + UI_Width(UI_Percent(1, 1)) UI_Height(UI_Em(1.66, 1)) + UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1)) + UI_BorderColor(V4(0.45, 0.45, 0.45, 1)) + UI_CornerRadius(2) + { + // sixten(TODO): MAKE THESE BUTTONS ACTUALLY DO STUFF!!! + 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; + } + } + } + } + } + + ui_signal Signal = UI_SignalFromBox(EditorBox); + + if(Signal.Pressed || (Signal.PressedRight && (Editor->EditState.Cursor == Editor->EditState.Mark))) + { + //- sixten: translate mouse position to text point + v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); + s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); + + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); + s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); + s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); + + text_point Point = {LineIndex + 1, ColumnIndex + 1}; + Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); + } + + if(Signal.Dragging) + { + //- sixten: translate mouse position to text point + v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0); + s64 LineIndex = Clamp((s64)(MouseOffset.y / LineHeight), 0, Editor->Lines.Count); + string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]); + s64 ColumnOffset = (s64)(MouseOffset.x / GlyphAdvance); + s64 ColumnIndex = UTF8IndexFromOffset(Line, ColumnOffset); + + text_point Point = {LineIndex + 1, ColumnIndex + 1}; + Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point); + + CursorHasBeenModified = true; + + Editor->DropdownActive = false; + } + + if(Signal.PressedRight) + { + Editor->DropdownActive = true; + Editor->DropdownP = UI_MouseP(); + Editor->DropdownTransition = 0; + } + + //- sixten: update eventual text point extents + if(CursorHasBeenModified) + { + text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor); + if(Editor->LastTextPoint.Line == Point.Line) + { + Editor->LastTextPoint = Point; + } + else + { + Editor->LastTextPoint.Line = Point.Line; + Editor->LastTextPoint.Column = Max(Editor->LastTextPoint.Column, Point.Column); + } + } + } + ReleaseScratch(Scratch); } \ No newline at end of file diff --git a/code/vn_workspace_view.cpp b/code/vn_workspace_view.cpp index 166dad4..e2dbc01 100644 --- a/code/vn_workspace_view.cpp +++ b/code/vn_workspace_view.cpp @@ -1,435 +1,559 @@ //- sixten: Views inline workspace_view *W_CreateNewView(workspace_view_kind Kind, workspace_panel *Parent) { - arena *Arena = ArenaAlloc(Kilobytes(4), true); - workspace_view *View = PushStruct(Arena, workspace_view); - View->Arena = Arena; - View->Kind = Kind; - View->Parent = Parent; - - switch(View->Kind) - { - case W_ViewKind_Settings: - { - View->Data = PushStruct(View->Arena, workspace_view_settings); - } break; - - case W_ViewKind_FileLister: - { - workspace_view_file_lister *Lister = PushStruct(View->Arena, workspace_view_file_lister); - View->Data = Lister; - - Lister->SelectedItem = -1; - } break; - - case W_ViewKind_TextEditor: - { - View->Data = PushStruct(View->Arena, workspace_view_text_editor); - - workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; - Editor->ProcessingArena = ArenaAlloc(Gigabytes(1)); - Editor->Text = MutableStringAllocate(Gigabytes(1)); - Editor->HistoryArena = ArenaAlloc(Gigabytes(1)); - - SenDLLInit(&Editor->History.Sentinel); - Editor->History.At = &Editor->History.Sentinel; - Editor->SavePoint = Editor->History.At; - - workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String); - Editor->Tokens = TextData.Tokens; - Editor->Lines = TextData.Lines; - } break; - - case W_ViewKind_NavEditor: - { - View->Data = PushStruct(View->Arena, workspace_view_nav_editor); - } break; - - case W_ViewKind_Error: - { - View->Data = PushStruct(View->Arena, workspace_view_error); - } break; - - default: break; - } - - DLLInsertLast(Parent->FirstView, Parent->LastView, View); - - if(View->Parent) - { - Parent->CurrentView = View; - } - - return(View); + arena *Arena = ArenaAlloc(Kilobytes(4), true); + workspace_view *View = PushStruct(Arena, workspace_view); + View->Arena = Arena; + View->Kind = Kind; + View->Parent = Parent; + + switch(View->Kind) + { + case W_ViewKind_Settings: + { + View->Data = PushStruct(View->Arena, workspace_view_settings); + } break; + + case W_ViewKind_FileLister: + { + workspace_view_file_lister *Lister = PushStruct(View->Arena, workspace_view_file_lister); + View->Data = Lister; + Lister->Path = StrLit("data"); + + Lister->SelectedItem = -1; + } break; + + case W_ViewKind_TextEditor: + { + View->Data = PushStruct(View->Arena, workspace_view_text_editor); + + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + Editor->ProcessingArena = ArenaAlloc(Gigabytes(1)); + Editor->Text = MutableStringAllocate(Gigabytes(1)); + Editor->HistoryArena = ArenaAlloc(Gigabytes(1)); + + SenDLLInit(&Editor->History.Sentinel); + Editor->History.At = &Editor->History.Sentinel; + Editor->SavePoint = Editor->History.At; + + workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String); + Editor->Tokens = TextData.Tokens; + Editor->Lines = TextData.Lines; + } break; + + case W_ViewKind_NavEditor: + { + View->Data = PushStruct(View->Arena, workspace_view_nav_editor); + } break; + + case W_ViewKind_ImageViewer: + { + View->Data = PushStruct(View->Arena, workspace_view_image_viewer); + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)View->Data; + Viewer->Scale = 1.0f; + //- sixten(TODO): write an image viewer + // beign with loading the image from the file lister load thingy + // show it on build + // release the handle when done (and add a way to remove texture ig) + } break; + + case W_ViewKind_Error: + { + View->Data = PushStruct(View->Arena, workspace_view_error); + } break; + + default: break; + } + + DLLInsertLast(Parent->FirstView, Parent->LastView, View); + + if(View->Parent) + { + Parent->CurrentView = View; + } + + return(View); } inline void W_DestroyView(workspace_view *View) { - switch(View->Kind) - { - case W_ViewKind_TextEditor: - { - workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; - ArenaRelease(Editor->ProcessingArena); - MutableStringRelease(&Editor->Text); - ArenaRelease(Editor->HistoryArena); - } break; - - default: break; - } - - // sixten(NOTE): This function does not ensure that the view is not being used anywhere else. - ArenaRelease(View->Arena); + switch(View->Kind) + { + case W_ViewKind_TextEditor: + { + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + ArenaRelease(Editor->ProcessingArena); + MutableStringRelease(&Editor->Text); + ArenaRelease(Editor->HistoryArena); + } break; + + case W_ViewKind_ImageViewer: + { + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)View->Data; + GlobalRenderCommands->DeallocateTexture(Viewer->Texture); + } break; + + default: break; + } + + // sixten(NOTE): This function does not ensure that the view is not being used anywhere else. + ArenaRelease(View->Arena); } inline b32 W_ViewIsCurrent(workspace_view *View) { - workspace *Workspace = W_GetState(); - - b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View); - return(Result); + workspace *Workspace = W_GetState(); + + b32 Result = (Workspace->CurrentPanel && Workspace->CurrentPanel->CurrentView == View); + return(Result); } -inline string W_GetViewName(arena *Arena, workspace_view *View) +inline string W_NameFromView(arena *Arena, workspace_view *View) { - string Result = StrLit("Unnamed view"); - switch(View->Kind) - { - case W_ViewKind_Startup: { Result = StrLit("Welcome"); } break; - case W_ViewKind_Settings: { Result = StrLit("Settings"); } break; - case W_ViewKind_TextEditor: - { - workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; - if(AreEqual(Editor->FileName, StrLit(""))) - { - Result = StrLit("Open File"); - } - else - { - //if(Editor->History.At == &Editor->History.Sentinel) - if(Editor->History.At == Editor->SavePoint) - { - Result = PushString(Arena, Editor->FileName); - } - else - { - Result = PushFormat(Arena, "* %S", Editor->FileName); - } - } - } break; - case W_ViewKind_SceneView: { Result = StrLit("Scene View"); } break; - case W_ViewKind_NavEditor: { Result = StrLit("Navigation Editor"); } break; - case W_ViewKind_FileLister: { Result = StrLit("File Lister"); } break; - case W_ViewKind_Error: { Result = StrLit("Error"); } break; - default: {} break; - } - - return(Result); + string Result = StrLit("Unnamed view"); + switch(View->Kind) + { + case W_ViewKind_Startup: { Result = StrLit("Welcome"); } break; + case W_ViewKind_Settings: { Result = StrLit("Settings"); } break; + case W_ViewKind_TextEditor: + { + workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data; + if(AreEqual(Editor->FileName, StrLit(""))) + { + Result = StrLit("Open File"); + } + else + { + if(Editor->History.At == Editor->SavePoint) + { + Result = PushString(Arena, Editor->FileName); + } + else + { + Result = PushFormat(Arena, "* %S", Editor->FileName); + } + } + } break; + case W_ViewKind_SceneView: { Result = StrLit("Scene View"); } break; + case W_ViewKind_NavEditor: { Result = StrLit("Navigation Editor"); } break; + case W_ViewKind_FileLister: { Result = StrLit("File Lister"); } break; + case W_ViewKind_ImageViewer: + { + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)View->Data; + Result = PushString(Arena, Viewer->FileName); + } break; + case W_ViewKind_Error: { Result = StrLit("Error"); } break; + default: {} break; + } + + return(Result); +} + +static b32 W_ImageViewerSetup(workspace_view *View, string Name, string Contents) +{ + b32 Result = false; + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)View->Data; + Viewer->Texture = EmptyRenderHandle(); + if(Contents.Count && Contents.Data) + { + s32 Width, Height, BPP; + u8 *TextureData = stbi_load_from_memory(Contents.Data, Contents.Count, &Width, &Height, &BPP, 0); + if(TextureData) + { + render_texture_format TextureFormat = Render_TextureFormat_Invalid; + switch(BPP) + { + InvalidDefaultCase; + case 1: { TextureFormat = Render_TextureFormat_R8; } break; + case 3: { TextureFormat = Render_TextureFormat_RGB8; } break; + case 4: { TextureFormat = Render_TextureFormat_RGBA8; } break; + } + + Viewer->Texture = GlobalRenderCommands->AllocateTexture(V2S32(Width, Height), TextureFormat, true, TextureData); + + Result = true; + + stbi_image_free(TextureData); + } + } + Viewer->FileName = PushString(View->Arena, Name); + + return(Result); } //- sixten: Builder code static void W_BuildSettingsTabButton(workspace_view_settings *Settings, char *Name, workspace_settings_category Category) { - b32 IsSelected = (Settings->Category == Category); - - v4 Color = LinearBlend(Theme_TextColor, Theme_HighlightBorderColor, AC_AnimateValueF(IsSelected, IsSelected, 0.3, "Workspace Settings %s %p", Name, Settings)); - - UI_SetNextFont(Font_Bold); - UI_SetNextHeight(UI_TextContent(0, 1)); - UI_SetNextTextColor(Color); - - ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawText | - UI_BoxFlag_Clickable, - Name); - - ui_signal Signal = UI_SignalFromBox(Box); - if(Signal.Hovering) - { - Platform.SetCursor(PlatformCursor_Hand); - } - if(Signal.Pressed) - { - Settings->Category = Category; - } + b32 IsSelected = (Settings->Category == Category); + + v4 Color = LinearBlend(Theme_TextColor, Theme_HighlightBorderColor, AC_AnimateValueF(IsSelected, IsSelected, 0.3, "Workspace Settings %s %p", Name, Settings)); + + UI_SetNextFont(Font_Bold); + UI_SetNextHeight(UI_TextContent(0, 1)); + UI_SetNextTextColor(Color); + + ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawText | + UI_BoxFlag_Clickable, + Name); + + ui_signal Signal = UI_SignalFromBox(Box); + if(Signal.Hovering) + { + Platform.SetCursor(PlatformCursor_Hand); + } + if(Signal.Pressed) + { + Settings->Category = Category; + } } static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 *Open, s32 *Selected) { - b32 Result = false; - - UI_SetNextLayoutAxis(Axis2_X); - UI_Parent(UI_MakeBoxF(0, "")) - { - UI_LabelF("Refresh Rate:"); - UI_Spacer(UI_Pixels(10, 1)); - - b32 ActiveInDropdown = false; - - UI_SetNextWidth(UI_Pixels(200, 1)); - UI_SetNextCornerRadius(4); - UI_SetNextLayoutAxis(Axis2_X); - ui_box *DropdownBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawBorder | - UI_BoxFlag_HotAnimation | - UI_BoxFlag_ActiveAnimation | - UI_BoxFlag_Clickable, - "Dropdown"); - UI_Parent(DropdownBox) - { - UI_Width(UI_Percent(1, 0)) UI_LabelF(Alternatives[*Selected]); - UI_BackgroundColor(Theme_BorderColor) UI_Width(UI_Pixels(1, 1)) UI_MakeBoxF(UI_BoxFlag_DrawBackground, ""); - UI_Width(UI_Pixels(25, 1)) UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_DownDir); - } - - ui_signal DropdownSignal = UI_SignalFromBox(DropdownBox); - if(DropdownSignal.Pressed) - { - *Open = !(*Open); - } - - if(AreEqual(UI_ActiveKey(), DropdownBox->Key)) - { - ActiveInDropdown = true; - } - - r32 OpenTransition = AC_AnimateValueF(*Open, 0, 0.175, "UI Dropdown %p%p", Alternatives, Open); - - if(OpenTransition > 0.1) - { - UI_Tooltip - { - UI_SetNextFixedP(V2(DropdownBox->Rect.Min.x, DropdownBox->Rect.Max.y)); - UI_SetNextCornerRadius(4); - UI_SetNextWidth(UI_Pixels(200, 1)); - UI_SetNextHeight(UI_ChildrenSum(OpenTransition, 1)); - UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip | - UI_BoxFlag_DrawDropShadow | - UI_BoxFlag_FloatingX | - UI_BoxFlag_FloatingY, "Dropdown Contents")) - { - UI_PushWidth(UI_Percent(1, 1)); - - for(s64 Index = 0; - Index < Round(AlternativeCount*OpenTransition); - ++Index) - { - ui_signal ButtonSignal = UI_ButtonF(Alternatives[Index]); - if(AreEqual(UI_ActiveKey(), ButtonSignal.Box->Key)) - { - ActiveInDropdown = true; - } - - if(ButtonSignal.Pressed) - { - *Selected = Index; - Result = true; - } - } - - UI_PopWidth(); - } - } - } - - if(!ActiveInDropdown && !AreEqual(UI_ActiveKey(), UI_EmptyKey())) - { - *Open = false; - } - } - - return(Result); + b32 Result = false; + b32 ActiveInDropdown = false; + + UI_SetNextLayoutAxis(Axis2_X); + ui_box *DropdownBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_HotAnimation | + UI_BoxFlag_ActiveAnimation | + UI_BoxFlag_Clickable, + "Dropdown"); + UI_Parent(DropdownBox) + { + UI_Width(UI_Percent(1, 0)) UI_LabelF(Alternatives[*Selected]); + UI_BackgroundColor(Theme_BorderColor) UI_Width(UI_Pixels(1, 1)) UI_MakeBoxF(UI_BoxFlag_DrawBackground, ""); + UI_Width(UI_Pixels(25, 1)) UI_Font(Font_Icons) UI_LabelF("%U", *Open?FontIcon_DownDir:FontIcon_RightDir); + } + + ui_signal DropdownSignal = UI_SignalFromBox(DropdownBox); + if(DropdownSignal.Pressed) + { + *Open = !(*Open); + } + + if(AreEqual(UI_ActiveKey(), DropdownBox->Key)) + { + ActiveInDropdown = true; + } + + r32 OpenTransition = AC_AnimateValueF(*Open, 0, 0.175, "UI Dropdown %p%p", Alternatives, Open); + + if(OpenTransition > 0.1) + { + UI_Tooltip + { + UI_SetNextFixedP(V2(DropdownBox->Rect.Min.x, DropdownBox->Rect.Max.y)); + UI_SetNextCornerRadius(4); + UI_SetNextWidth(UI_Pixels(200, 1)); + UI_SetNextHeight(UI_ChildrenSum(OpenTransition, 1)); + UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip | + UI_BoxFlag_DrawDropShadow | + UI_BoxFlag_FloatingX | + UI_BoxFlag_FloatingY, "Dropdown Contents %p%p", Alternatives, Open)) + { + UI_PushWidth(UI_Percent(1, 1)); + + for(s64 Index = 0; + Index < Round(AlternativeCount*OpenTransition); + ++Index) + { + ui_signal ButtonSignal = UI_ButtonF(Alternatives[Index]); + if(AreEqual(UI_ActiveKey(), ButtonSignal.Box->Key)) + { + ActiveInDropdown = true; + } + + if(ButtonSignal.Pressed) + { + *Selected = Index; + Result = true; + } + } + + UI_PopWidth(); + } + } + } + + if(!ActiveInDropdown && !AreEqual(UI_ActiveKey(), UI_EmptyKey())) + { + *Open = false; + } + + return(Result); +} + + +UI_CUSTOM_DRAW_CALLBACK(W_ImageViewerUICallback) +{ + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)Data; + v2_r32 ImageDim = ConvertV2ToR32(DimFromTexture(Viewer->Texture)); + v2_r32 ScaleImageDim = ImageDim*Viewer->Scale; + + v4 Color = Color_White; + range2_r32 Dest; + Dest.Min = (Box->Rect.Max+Box->Rect.Min)*0.5-ScaleImageDim*0.5+Viewer->Offset*Viewer->Scale; + Dest.Max = Dest.Min+ScaleImageDim; + range2_r32 Source = Range2R32(V2R32(0, 0), ImageDim); + PushTexturedQuad(Group, Dest, Source, Color, Color, Color, Color, 0, 0, 0, Viewer->Texture); +} + +static void W_BuildImageViewer(workspace_view *View) +{ + workspace_view_image_viewer *Viewer = (workspace_view_image_viewer *)View->Data; + + //- sixten: find image dim + v2_r32 ImageDim = ConvertV2ToR32(DimFromTexture(Viewer->Texture)); + + UI_WidthFill UI_HeightFill + { + ui_box *ImageBox = UI_MakeBoxF(UI_BoxFlag_DrawBorder|UI_BoxFlag_Clickable, "Workspace View Image Viewer %p", View); + UI_EquipBoxCustomDrawCallback(ImageBox, W_ImageViewerUICallback, Viewer); + + //- sixten: handle scrolling + if(AreEqual(UI_HotKey(), ImageBox->Key)) + { + for(platform_event *Event = UI_EventList()->First; + Event != 0; + Event = Event->Next) + { + if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0) + { + Viewer->Scale *= Pow(1.10f, Event->Scroll.y); + Platform_ConsumeEvent(UI_EventList(), Event); + } + } + } + + ui_signal Signal = UI_SignalFromBox(ImageBox); + if(Signal.Dragging) + { + if(Signal.Pressed) + { + UI_StoreDragV2(Viewer->Offset); + } + v2_r32 StartOffset = UI_GetDragV2(); + v2_r32 EndOffset = StartOffset+Signal.DragDelta*(1.0f/Viewer->Scale); + + Viewer->Offset = EndOffset; + } + } + UI_WidthFill UI_Height(UI_Em(3, 1)) + UI_Row(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder) + { + UI_Width(UI_TextContent(15, 1)) + { + UI_Label(Viewer->FileName); + UI_LabelF("Width: %i Height: %i", (int)ImageDim.x, (int)ImageDim.y); + } + UI_Spacer(UI_Percent(1, 0)); + } } static void W_BuildSceneView(workspace_view *View) { - workspace *Workspace = W_GetState(); - SV_BuildSceneView(Workspace->Input); + workspace *Workspace = W_GetState(); + SV_BuildSceneView(Workspace->Input); } static void W_BuildSettings(workspace_view *View) { - workspace_view_settings *Settings = (workspace_view_settings *)View->Data; - workspace *Workspace = W_GetState(); - - UI_Height(UI_ChildrenSum(1, 1)) - UI_Column() UI_Padding(UI_Pixels(50, 0)) - UI_Row() UI_Padding(UI_Pixels(50, 0)) - { - UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) - UI_Font(Font_Bold) UI_FontSize(36) - UI_LabelF("Settings"); - } - - UI_LayoutAxis(Axis2_X) - UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) - UI_Parent(UI_MakeBoxF(0, "")) - { - UI_Width(UI_Pixels(300, 1)) - UI_Parent(UI_MakeBoxF(0, "Navigation")) - { - UI_Row() UI_Padding(UI_Pixels(50, 1)) - UI_Width(UI_Percent(1, 0)) UI_Column() UI_Padding(UI_Percent(1, 0)) - UI_Height(UI_ChildrenSum(1, 1)) UI_LayoutAxis(Axis2_Y) UI_Parent(UI_MakeBoxF(0, "")) - { - W_BuildSettingsTabButton(Settings, "All", W_Settings_All); - UI_Spacer(UI_Pixels(30, 1)); - W_BuildSettingsTabButton(Settings, "General", W_Settings_General); - UI_Spacer(UI_Pixels(30, 1)); - W_BuildSettingsTabButton(Settings, "Developer", W_Settings_Developer); - - UI_Spacer(UI_Pixels(150, 1)); - } - } - - UI_CornerRadius(5) - UI_Width(UI_Pixels(1.25, 1)) - UI_BackgroundColor(Color_Grey) - UI_MakeBoxF(UI_BoxFlag_DrawBackground, "Separator"); - - UI_Padding(UI_Pixels(70, 0)) - UI_LayoutAxis(Axis2_Y) - UI_Width(UI_Percent(1, 0)) - UI_Parent(UI_MakeBoxF(0, "Tab")) - UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) - { - UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); - UI_Scroll(0, &Settings->GlobalScroll, UI_BoxFlag_Clip) - { - workspace_settings_category Category = Settings->Category; - if(!Category || (Category == W_Settings_General)) - { - UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("General"); - - char *Alternatives[] = {"15 Hz", "30 Hz", "60 Hz", "120 Hz", "144 Hz", "Uncapped", "V-Sync"}; - s64 AlternativeMapping[] = {15, 30, 60, 120, 144, -1, 0}; - - s32 DropdownSelected; - FindIndexOfElement(DropdownSelected, AlternativeMapping, 0, Workspace->Input->RefreshRate); - - if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives), - &Settings->GeneralDropdownOpen, &DropdownSelected)) - { - Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected]; - Settings->GeneralDropdownOpen = false; - } - - UI_Spacer(UI_Pixels(50, 1)); - } - - if(!Category || (Category == W_Settings_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)); + workspace_view_settings *Settings = (workspace_view_settings *)View->Data; + workspace *Workspace = W_GetState(); + + UI_Height(UI_ChildrenSum(1, 1)) + UI_Column() UI_Padding(UI_Pixels(50, 0)) + UI_Row() UI_Padding(UI_Pixels(50, 0)) + { + UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) + UI_Font(Font_Bold) UI_FontSize(36) + UI_LabelF("Settings"); + } + + UI_LayoutAxis(Axis2_X) + UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) + UI_Parent(UI_MakeBoxF(0, "")) + { + UI_Width(UI_Pixels(300, 1)) + UI_Parent(UI_MakeBoxF(0, "Navigation")) + { + UI_Row() UI_Padding(UI_Pixels(50, 1)) + UI_Width(UI_Percent(1, 0)) UI_Column() UI_Padding(UI_Percent(1, 0)) + UI_Height(UI_ChildrenSum(1, 1)) UI_LayoutAxis(Axis2_Y) UI_Parent(UI_MakeBoxF(0, "")) + { + W_BuildSettingsTabButton(Settings, "All", W_Settings_All); + UI_Spacer(UI_Pixels(30, 1)); + W_BuildSettingsTabButton(Settings, "General", W_Settings_General); + UI_Spacer(UI_Pixels(30, 1)); + W_BuildSettingsTabButton(Settings, "Developer", W_Settings_Developer); + + UI_Spacer(UI_Pixels(150, 1)); + } + } + + UI_CornerRadius(5) + UI_Width(UI_Pixels(1.25, 1)) + UI_BackgroundColor(Color_Grey) + UI_MakeBoxF(UI_BoxFlag_DrawBackground, "Separator"); + + UI_Padding(UI_Pixels(70, 0)) + UI_LayoutAxis(Axis2_Y) + UI_Width(UI_Percent(1, 0)) + UI_Parent(UI_MakeBoxF(0, "Tab")) + UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) + { + UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); + UI_Scroll(0, &Settings->GlobalScroll, UI_BoxFlag_Clip) + { + workspace_settings_category Category = Settings->Category; + if(!Category || (Category == W_Settings_General)) + { + UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("General"); + + char *Alternatives[] = {"15 Hz", "30 Hz", "60 Hz", "120 Hz", "144 Hz", "Uncapped", "V-Sync"}; + s64 AlternativeMapping[] = {15, 30, 60, 120, 144, -1, 0}; + + s32 DropdownSelected; + FindIndexOfElement(DropdownSelected, AlternativeMapping, 0, Workspace->Input->RefreshRate); + + UI_Row() + { + UI_LabelF("Refresh Rate:"); + UI_Spacer(UI_Pixels(10, 1)); + + UI_SetNextWidth(UI_Pixels(200, 1)); + UI_SetNextCornerRadius(4); + if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives), + &Settings->GeneralDropdownOpen, &DropdownSelected)) + { + Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected]; + Settings->GeneralDropdownOpen = false; + } + } + + UI_Spacer(UI_Pixels(50, 1)); + } + + if(!Category || (Category == W_Settings_Developer)) + { + UI_Font(Font_Bold) UI_FontSize(36) UI_LabelF("Developer"); + UI_Checkbox(&DEBUG_DebugSettings->RenderUIDebugRects, StrLit("Render UI Debug Rects")); + UI_Spacer(UI_Pixels(5, 1)); + UI_Checkbox(&DEBUG_DebugSettings->RenderFPSCounter, StrLit("Render FPS Counter")); + UI_Spacer(UI_Pixels(5, 1)); + UI_Checkbox(&DEBUG_DebugSettings->ListHotAndActive, StrLit("List Hot & Active")); + + UI_Spacer(UI_Pixels(50, 1)); + } + } + } + } + + UI_Spacer(UI_Pixels(50, 1)); } static void W_BuildView(workspace_view *View) { - r32 ViewHighlightTransition = - AC_AnimateValueF(W_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View); - UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, ViewHighlightTransition)); - UI_PushBackgroundColor(Theme_BackgroundColor); - UI_SetNextCornerRadius(3); - - ui_box *ViewBox = UI_MakeBoxF(UI_BoxFlag_Clickable | - UI_BoxFlag_DrawBackground | - UI_BoxFlag_DrawBorder | - UI_BoxFlag_Clip, - "Workspace View %p", View); - - UI_Parent(ViewBox) - UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) - { - switch(View->Kind) - { - case W_ViewKind_None: {} break; - - case W_ViewKind_Startup: - { - UI_Row() UI_Padding(UI_Pixels(50, 0)) - UI_Width(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) - { - UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) - { - UI_Font(Font_Bold) UI_FontSize(36) - UI_LabelF("Welcome to VN"); - UI_TextColor(Theme_BorderColor) UI_LabelF("An impractical way to make a game"); - - UI_Spacer(UI_Percent(1, 0)); - - UI_Checkbox(&DEBUG_DebugSettings->ShowWelcomeMessage, StrLit("Show this message on startup")); - } - } - } break; - - case W_ViewKind_Settings: - { - W_BuildSettings(View); - } break; - - case W_ViewKind_TextEditor: - { - W_BuildTextEditor(View); - } break; - - case W_ViewKind_SceneView: - { - W_BuildSceneView(View); - } break; - case W_ViewKind_NavEditor: - { - W_BuildNavEditor(View); - } break; - case W_ViewKind_FileLister: - { - workspace_file_lister_action Action = W_BuildFileLister(View); - if(Action.HasRequestedFile) - { - workspace_file_lister_action *ActionOnDataArena = W_FileListerActionCopy(W_GetState()->CommandDataArena, &Action); - W_IssueCommand(W_Command_OpenFile, PointerToU64(ActionOnDataArena)); - - W_IssueCommand(W_Command_CloseView, PointerToU64(View)); - } - } break; - - case W_ViewKind_Error: - { - workspace_view_error *Error = (workspace_view_error *)View->Data; - UI_WidthFill UI_HeightFill - UI_Column() - { - UI_Label(Error->Message); - - UI_Height(UI_Em(2, 1)) UI_Row() - { - UI_Spacer(UI_Percent(1, 0)); - - UI_Width(UI_TextContent(15, 1)) UI_CornerRadius(4.0f) - if(UI_Button(StrLit("Close View")).Clicked) - { - W_IssueCommand(W_Command_CloseView, PointerToU64(View)); - } - UI_Spacer(UI_Em(1, 1)); - } - UI_Spacer(UI_Em(1, 1)); - } - } break; - } - } - - UI_PopBackgroundColor(); - - UI_SignalFromBox(ViewBox); + r32 ViewHighlightTransition = + AC_AnimateValueF(W_ViewIsCurrent(View), 0, 0.25, "Workspace View Highlight %p", View); + UI_SetNextBorderColor(LinearBlend(Theme_BorderColor, Theme_HighlightBorderColor, ViewHighlightTransition)); + UI_PushBackgroundColor(Theme_BackgroundColor); + UI_SetNextCornerRadius(3); + + ui_box *ViewBox = UI_MakeBoxF(UI_BoxFlag_Clickable | + UI_BoxFlag_DrawBackground | + UI_BoxFlag_DrawBorder | + UI_BoxFlag_Clip, + "Workspace View %p", View); + + UI_Parent(ViewBox) + UI_Size(UI_Percent(1, 0), UI_Percent(1, 0)) + { + switch(View->Kind) + { + case W_ViewKind_None: {} break; + + case W_ViewKind_Startup: + { + UI_Row() UI_Padding(UI_Pixels(50, 0)) + UI_Width(UI_ChildrenSum(1, 1)) UI_Column() UI_Padding(UI_Pixels(50, 0)) + { + UI_Size(UI_TextContent(0, 1), UI_TextContent(10, 1)) + { + UI_Font(Font_Bold) UI_FontSize(36) + UI_LabelF("Welcome to VN"); + UI_TextColor(Theme_BorderColor) UI_LabelF("An impractical way to make a game"); + + UI_Spacer(UI_Percent(1, 0)); + + UI_Checkbox(&DEBUG_DebugSettings->ShowWelcomeMessage, StrLit("Show this message on startup")); + } + } + } break; + + case W_ViewKind_Settings: + { + W_BuildSettings(View); + } break; + + case W_ViewKind_ImageViewer: + { + W_BuildImageViewer(View); + } break; + + case W_ViewKind_TextEditor: + { + W_BuildTextEditor(View); + } break; + + case W_ViewKind_SceneView: + { + W_BuildSceneView(View); + } break; + case W_ViewKind_NavEditor: + { + W_BuildNavEditor(View); + } break; + case W_ViewKind_FileLister: + { + workspace_file_lister_action Action = W_BuildFileLister(View); + if(Action.HasRequestedFile) + { + workspace_file_lister_action *ActionOnDataArena = W_FileListerActionCopy(W_GetState()->CommandDataArena, &Action); + W_IssueCommand(W_Command_OpenFile, PointerToU64(ActionOnDataArena)); + + W_IssueCommand(W_Command_CloseView, PointerToU64(View)); + } + } break; + + case W_ViewKind_Error: + { + workspace_view_error *Error = (workspace_view_error *)View->Data; + UI_WidthFill UI_HeightFill + UI_Column() + { + UI_Label(Error->Message); + + UI_Height(UI_Em(2, 1)) UI_Row() + { + UI_Spacer(UI_Percent(1, 0)); + + UI_Width(UI_TextContent(15, 1)) UI_CornerRadius(4.0f) + if(UI_Button(StrLit("Close View")).Clicked) + { + W_IssueCommand(W_Command_CloseView, PointerToU64(View)); + } + UI_Spacer(UI_Em(1, 1)); + } + UI_Spacer(UI_Em(1, 1)); + } + } break; + } + } + + UI_PopBackgroundColor(); + + UI_SignalFromBox(ViewBox); } diff --git a/code/vn_workspace_view.h b/code/vn_workspace_view.h index 89a1a08..753d68d 100644 --- a/code/vn_workspace_view.h +++ b/code/vn_workspace_view.h @@ -8,47 +8,56 @@ struct workspace_view { - arena *Arena; - - workspace_panel *Parent; - workspace_view *Next; - workspace_view *Prev; - - enum workspace_view_kind Kind; - void *Data; + arena *Arena; + + workspace_panel *Parent; + workspace_view *Next; + workspace_view *Prev; + + enum workspace_view_kind Kind; + void *Data; }; enum workspace_view_kind { - W_ViewKind_None, - W_ViewKind_Startup, - W_ViewKind_Settings, - W_ViewKind_FileLister, - W_ViewKind_TextEditor, - W_ViewKind_SceneView, - W_ViewKind_NavEditor, - W_ViewKind_Error, + W_ViewKind_None, + W_ViewKind_Startup, + W_ViewKind_Settings, + W_ViewKind_FileLister, + W_ViewKind_TextEditor, + W_ViewKind_SceneView, + W_ViewKind_NavEditor, + W_ViewKind_ImageViewer, + W_ViewKind_Error, }; struct workspace_view_error { - string Message; + string Message; }; enum workspace_settings_category { - W_Settings_All, - W_Settings_General, - W_Settings_Developer, + W_Settings_All, + W_Settings_General, + W_Settings_Developer, }; struct workspace_view_settings { - workspace_settings_category Category; - r32 GlobalScroll; - - // sixten: General - b32 GeneralDropdownOpen; + workspace_settings_category Category; + r32 GlobalScroll; + + // sixten: General + b32 GeneralDropdownOpen; +}; + +struct workspace_view_image_viewer +{ + string FileName; + render_handle Texture; + r32 Scale; + v2_r32 Offset; }; //////////////////////////////// @@ -58,11 +67,18 @@ struct workspace_view_settings inline workspace_view *W_CreateNewView(workspace_view_kind Type, workspace_panel *Parent); inline void W_DestroyView(workspace_view *View); inline b32 W_ViewIsCurrent(workspace_view *View); -inline string W_GetViewName(arena *Arena, workspace_view *View); +inline string W_NameFromView(arena *Arena, workspace_view *View); //- sixten: Builder code 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); +//- sixten: View Implementations +static b32 W_ImageViewerSetup(workspace_view *View, string Name, string Contents); +static void W_BuildImageViewer(workspace_view *View); +static void W_BuildSceneView(workspace_view *View); +static void W_BuildSettings(workspace_view *View); + + #endif //VN_WORKSPACE_VIEW_H diff --git a/code/win32_main.cpp b/code/win32_main.cpp index be58e7d..81142db 100644 --- a/code/win32_main.cpp +++ b/code/win32_main.cpp @@ -17,849 +17,849 @@ global WINDOWPLACEMENT Global_WindowPosition = {sizeof(Global_WindowPosition)};; static void Win32_PlatformError(char *Message, bool IsFatal) { - MessageBoxA(0, Message, "vn - Platform Error", MB_OK|(IsFatal?MB_ICONSTOP:MB_ICONEXCLAMATION)); - - if(IsFatal) - { - ExitProcess((UINT)-1); - } + MessageBoxA(0, Message, "vn - Platform Error", MB_OK|(IsFatal?MB_ICONSTOP:MB_ICONEXCLAMATION)); + + if(IsFatal) + { + ExitProcess((UINT)-1); + } } static PLATFORM_SHOW_MESSAGE(Win32_ShowMessage) { - DWORD Flags = MB_OK; - switch(Type) - { - case Platform_Message_Info: { Flags |= MB_ICONINFORMATION; } break; - case Platform_Message_Warning: { Flags |= MB_ICONWARNING; } break; - case Platform_Message_Error: { Flags |= MB_ICONEXCLAMATION; } break; - case Platform_Message_Fatal: { Flags |= MB_ICONSTOP; } break; - - InvalidDefaultCase; - } - - // sixten(NOTE): Check for null-termination. - Assert(Message.Data[Message.Count] == 0); - - MessageBoxA(0, (char *)Message.Data, "vn - A message from the developer", Flags); - - if(Type == Platform_Message_Fatal) - { - ExitProcess((UINT)-1); - } + DWORD Flags = MB_OK; + switch(Type) + { + case Platform_Message_Info: { Flags |= MB_ICONINFORMATION; } break; + case Platform_Message_Warning: { Flags |= MB_ICONWARNING; } break; + case Platform_Message_Error: { Flags |= MB_ICONEXCLAMATION; } break; + case Platform_Message_Fatal: { Flags |= MB_ICONSTOP; } break; + + InvalidDefaultCase; + } + + // sixten(NOTE): Check for null-termination. + Assert(Message.Data[Message.Count] == 0); + + MessageBoxA(0, (char *)Message.Data, "vn - A message from the developer", Flags); + + if(Type == Platform_Message_Fatal) + { + ExitProcess((UINT)-1); + } } static u64 Win32_GetPageSize(void) { - SYSTEM_INFO Info; - GetSystemInfo(&Info); - return(Info.dwPageSize); + SYSTEM_INFO Info; + GetSystemInfo(&Info); + return(Info.dwPageSize); } static PLATFORM_RESERVE(Win32_Reserve) { - u64 GigabyteAlignedSize = Size+Gigabytes(1)-1; - GigabyteAlignedSize -= GigabyteAlignedSize%Gigabytes(1); - void *Result = VirtualAlloc(0, GigabyteAlignedSize, MEM_RESERVE, PAGE_NOACCESS); - return(Result); + u64 GigabyteAlignedSize = Size+Gigabytes(1)-1; + GigabyteAlignedSize -= GigabyteAlignedSize%Gigabytes(1); + void *Result = VirtualAlloc(0, GigabyteAlignedSize, MEM_RESERVE, PAGE_NOACCESS); + return(Result); } static PLATFORM_RELEASE(Win32_Release) { - VirtualFree(Pointer, 0, MEM_RELEASE); + VirtualFree(Pointer, 0, MEM_RELEASE); } static PLATFORM_COMMIT(Win32_Commit) { - u64 PageAlignedSize = Size+Win32_GetPageSize()-1; - PageAlignedSize -= PageAlignedSize%Win32_GetPageSize(); - VirtualAlloc(Pointer, PageAlignedSize, MEM_COMMIT, PAGE_READWRITE); + u64 PageAlignedSize = Size+Win32_GetPageSize()-1; + PageAlignedSize -= PageAlignedSize%Win32_GetPageSize(); + VirtualAlloc(Pointer, PageAlignedSize, MEM_COMMIT, PAGE_READWRITE); } static PLATFORM_DECOMMIT(Win32_Decommit) { - VirtualFree(Pointer, Size, MEM_DECOMMIT); + VirtualFree(Pointer, Size, MEM_DECOMMIT); } static PLATFORM_ALLOCATE(Win32_Allocate) { - void *Result = VirtualAlloc(0, Size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); - return(Result); + void *Result = VirtualAlloc(0, Size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); + return(Result); } static PLATFORM_DEALLOCATE(Win32_Deallocate) { - VirtualFree(Pointer, 0, MEM_DECOMMIT|MEM_RELEASE); + VirtualFree(Pointer, 0, MEM_DECOMMIT|MEM_RELEASE); } static PLATFORM_OPEN_FILE(Win32_OpenFile) { - DWORD DesiredAccess = 0; - if(FileAccess & PlatformAccess_Read) - { - DesiredAccess |= GENERIC_READ; - } - if(FileAccess & PlatformAccess_Write) - { - DesiredAccess |= GENERIC_WRITE; - } - - DWORD CreationAttributes = 0; - if(FileAccess & PlatformAccess_Read) - { - CreationAttributes = OPEN_EXISTING; - } - if(FileAccess & PlatformAccess_Write) - { - CreationAttributes = CREATE_ALWAYS; - } - - temporary_memory Scratch = GetScratch(0, 0); - - string FullPath = PushFormat(Scratch.Arena, "%S\\%S", Global_Win32State.ContentsPath, Path); - HANDLE File = CreateFileA((char *)FullPath.Data, DesiredAccess, 0, 0, CreationAttributes, 0, 0); - - ReleaseScratch(Scratch); - - platform_file_handle Result = {}; - Result.Platform = (u64)File; - Result.IsValid = (File != INVALID_HANDLE_VALUE); - - return(Result); + DWORD DesiredAccess = 0; + if(FileAccess & PlatformAccess_Read) + { + DesiredAccess |= GENERIC_READ; + } + if(FileAccess & PlatformAccess_Write) + { + DesiredAccess |= GENERIC_WRITE; + } + + DWORD CreationAttributes = 0; + if(FileAccess & PlatformAccess_Read) + { + CreationAttributes = OPEN_EXISTING; + } + if(FileAccess & PlatformAccess_Write) + { + CreationAttributes = CREATE_ALWAYS; + } + + temp Scratch = GetScratch(0, 0); + + string FullPath = PushFormat(Scratch.Arena, "%S\\%S", Global_Win32State.ContentsPath, Path); + HANDLE File = CreateFileA((char *)FullPath.Data, DesiredAccess, 0, 0, CreationAttributes, 0, 0); + + ReleaseScratch(Scratch); + + platform_file_handle Result = {}; + Result.Platform = (u64)File; + Result.IsValid = (File != INVALID_HANDLE_VALUE); + + return(Result); } static PLATFORM_CLOSE_FILE(Win32_CloseFile) { - HANDLE File = (HANDLE)Handle.Platform; - if(File != INVALID_HANDLE_VALUE) - { - CloseHandle(File); - } + HANDLE File = (HANDLE)Handle.Platform; + if(File != INVALID_HANDLE_VALUE) + { + CloseHandle(File); + } } static PLATFORM_READ_FILE(Win32_ReadFile) { - HANDLE File = (HANDLE)Handle.Platform; - if(File != INVALID_HANDLE_VALUE) - { - DWORD BytesRead; - ReadFile(File, Dest, Size, &BytesRead, 0); - - Assert(BytesRead == Size); - } + HANDLE File = (HANDLE)Handle.Platform; + if(File != INVALID_HANDLE_VALUE) + { + DWORD BytesRead; + ReadFile(File, Dest, Size, &BytesRead, 0); + + //Assert(BytesRead == Size); + } } static PLATFORM_WRITE_FILE(Win32_WriteFile) { - HANDLE File = (HANDLE)Handle.Platform; - if(File != INVALID_HANDLE_VALUE) - { - DWORD BytesWritten; - WriteFile(File, Source, Size, &BytesWritten, 0); - - Assert(BytesWritten == Size); - } + HANDLE File = (HANDLE)Handle.Platform; + if(File != INVALID_HANDLE_VALUE) + { + DWORD BytesWritten; + WriteFile(File, Source, Size, &BytesWritten, 0); + + Assert(BytesWritten == Size); + } } static PLATFORM_GET_FILE_SIZE(Win32_GetFileSize) { - u64 Result = 0; - - HANDLE File = (HANDLE)Handle.Platform; - if(File != INVALID_HANDLE_VALUE) - { - LARGE_INTEGER FileSize; - GetFileSizeEx(File, &FileSize); - - Result = FileSize.QuadPart; - } - - return(Result); + u64 Result = 0; + + HANDLE File = (HANDLE)Handle.Platform; + if(File != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER FileSize; + GetFileSizeEx(File, &FileSize); + + Result = FileSize.QuadPart; + } + + return(Result); } static PLATFORM_SET_CURSOR(Win32_SetCursor) { - Global_Win32State.Cursor = Cursor; + Global_Win32State.Cursor = Cursor; } static PLATFORM_BEGIN_FILE_ITER(Win32_BeginFileIter) { - win32_file_find_data *FileFindData = PushStruct(Arena, win32_file_find_data); - temporary_memory Scratch = GetScratch(&Arena, 1); - Path = PushFormat(Scratch.Arena, "%S/%S*", Global_Win32State.ContentsPath, Path); - string16 Path16 = String16FromString8(Scratch.Arena, Path); - FileFindData->Handle = FindFirstFileW((WCHAR *)Path16.Data, &FileFindData->FindData); - ReleaseScratch(Scratch); - platform_file_iter *Iter = (platform_file_iter *)FileFindData; - return(Iter); + win32_file_find_data *FileFindData = PushStruct(Arena, win32_file_find_data); + temp Scratch = GetScratch(&Arena, 1); + Path = PushFormat(Scratch.Arena, "%S/%S*", Global_Win32State.ContentsPath, Path); + string16 Path16 = String16FromString8(Scratch.Arena, Path); + FileFindData->Handle = FindFirstFileW((WCHAR *)Path16.Data, &FileFindData->FindData); + ReleaseScratch(Scratch); + platform_file_iter *Iter = (platform_file_iter *)FileFindData; + return(Iter); } static PLATFORM_ADVANCE_FILE_ITER(Win32_AdvanceFileIter) { - b32 Result = false; - win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; - WIN32_FIND_DATAW FindData = {}; - s64 InvalidCount = 0; - for(;;) - { - if(FileFindData->FindFirstReturned) - { - Result = FindNextFileW(FileFindData->Handle, &FindData); - } - else - { - Result = (FileFindData->Handle != 0 && FileFindData->Handle != INVALID_HANDLE_VALUE); - FindData = FileFindData->FindData; - FileFindData->FindFirstReturned = true; - } - - b32 NameIsInvalid = (FindData.cFileName[0] == '.' && (FindData.cFileName[1] == 0 || FindData.cFileName[1] == '.')); - if(NameIsInvalid && InvalidCount < 10) - { - InvalidCount += 1; - continue; - } - - break; - } - - if(Result != 0) - { - string16 Name16 = {}; - Name16.Data = (u16 *)FindData.cFileName; - for(u16 Index = 0; Index < MAX_PATH; Index += 1) - { - if(FindData.cFileName[Index] == 0) - { - break; - } - Name16.Count += 1; - } - - *OutInfo = {}; - OutInfo->Name = String8FromString16(Arena, Name16); - OutInfo->IsDirectory = FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY; - } - return(Result); + b32 Result = false; + win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; + WIN32_FIND_DATAW FindData = {}; + s64 InvalidCount = 0; + for(;;) + { + if(FileFindData->FindFirstReturned) + { + Result = FindNextFileW(FileFindData->Handle, &FindData); + } + else + { + Result = (FileFindData->Handle != 0 && FileFindData->Handle != INVALID_HANDLE_VALUE); + FindData = FileFindData->FindData; + FileFindData->FindFirstReturned = true; + } + + b32 NameIsInvalid = (FindData.cFileName[0] == '.' && (FindData.cFileName[1] == 0 || FindData.cFileName[1] == '.')); + if(NameIsInvalid && InvalidCount < 10) + { + InvalidCount += 1; + continue; + } + + break; + } + + if(Result != 0) + { + string16 Name16 = {}; + Name16.Data = (u16 *)FindData.cFileName; + for(u16 Index = 0; Index < MAX_PATH; Index += 1) + { + if(FindData.cFileName[Index] == 0) + { + break; + } + Name16.Count += 1; + } + + *OutInfo = {}; + OutInfo->Name = String8FromString16(Arena, Name16); + OutInfo->IsDirectory = FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY; + } + return(Result); } static PLATFORM_END_FILE_ITER(Win32_EndFileIter) { - win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; - FindClose(FileFindData->Handle); + win32_file_find_data *FileFindData = (win32_file_find_data *)Iter; + FindClose(FileFindData->Handle); } inline u64 Win32_GetWallClock(void) { - LARGE_INTEGER Query; - QueryPerformanceCounter(&Query); - - u64 Result = Query.QuadPart; - return(Result); + LARGE_INTEGER Query; + QueryPerformanceCounter(&Query); + + u64 Result = Query.QuadPart; + return(Result); } inline r64 Win32_GetSecondsElapsed(u64 Start, u64 End) { - u64 Elapsed = End - Start; - - r64 Result = (r64)Elapsed/(r64)Global_Win32State.PerformanceFrequency; - return(Result); + u64 Elapsed = End - Start; + + r64 Result = (r64)Elapsed/(r64)Global_Win32State.PerformanceFrequency; + return(Result); } inline FILETIME Win32_GetLastWriteTime(char *Path) { - FILETIME Result = {}; - - HANDLE File = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - if(File) - { - FILETIME Creation, LastAccess, LastWrite; - GetFileTime(File, &Creation, &LastAccess, &LastWrite); - - Result = LastWrite; - - CloseHandle(File); - } - - return(Result); + FILETIME Result = {}; + + HANDLE File = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(File) + { + FILETIME Creation, LastAccess, LastWrite; + GetFileTime(File, &Creation, &LastAccess, &LastWrite); + + Result = LastWrite; + + CloseHandle(File); + } + + return(Result); } #if !W32_LINK_SINGLE static win32_loaded_code Win32_LoadCode(void) { - win32_loaded_code Code = {}; - - win32_state *State = &Global_Win32State; - - if(CopyFile(State->DLLPath, State->TempDLLPath, FALSE)) - { - Code.LastWriteTime = Win32_GetLastWriteTime(State->DLLPath); - - Code.DLL = LoadLibraryA(State->TempDLLPath); - if(Code.DLL) - { - Code.UpdateAndRender = (vn_update_and_render *)GetProcAddress(Code.DLL, "VN_UpdateAndRender"); - if(Code.UpdateAndRender) - { - Code.IsValid = true; - } - } - } - - return(Code); + win32_loaded_code Code = {}; + + win32_state *State = &Global_Win32State; + + if(CopyFile(State->DLLPath, State->TempDLLPath, FALSE)) + { + Code.LastWriteTime = Win32_GetLastWriteTime(State->DLLPath); + + Code.DLL = LoadLibraryA(State->TempDLLPath); + if(Code.DLL) + { + Code.UpdateAndRender = (vn_update_and_render *)GetProcAddress(Code.DLL, "VN_UpdateAndRender"); + if(Code.UpdateAndRender) + { + Code.IsValid = true; + } + } + } + + return(Code); } static void Win32_UnloadCode(win32_loaded_code *Code) { - if(Code->DLL) - { - FreeLibrary(Code->DLL); - } - - *Code = {}; + if(Code->DLL) + { + FreeLibrary(Code->DLL); + } + + *Code = {}; } static void Win32_UpdateCode(win32_loaded_code *Code) { - win32_state *State = &Global_Win32State; - - FILETIME LastWriteTime = Win32_GetLastWriteTime(State->DLLPath); - if(CompareFileTime(&Code->LastWriteTime, &LastWriteTime) != 0) - { - Win32_UnloadCode(Code); - *Code = Win32_LoadCode(); - - // sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over... - Sleep(500); - } + win32_state *State = &Global_Win32State; + + FILETIME LastWriteTime = Win32_GetLastWriteTime(State->DLLPath); + if(CompareFileTime(&Code->LastWriteTime, &LastWriteTime) != 0) + { + Win32_UnloadCode(Code); + *Code = Win32_LoadCode(); + + // sixten(NOTE): Sometimes the program decides to crash upon reloads, so we just wait for those to be over... + Sleep(500); + } } #endif static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen) { - HWND Window = Global_Win32State.Window; - - DWORD Style = GetWindowLong(Window, GWL_STYLE); - if(Style & WS_OVERLAPPEDWINDOW) - { - MONITORINFO MonitorInfo = {sizeof(MonitorInfo)}; - if(GetWindowPlacement(Window, &Global_WindowPosition) && - GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo)) - { - // sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the - // window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed. - SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW); - SetWindowPos(Window, HWND_TOP, - MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top, - MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, - MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - } - } - else - { - SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW); - SetWindowPlacement(Window, &Global_WindowPosition); - SetWindowPos(Window, 0, 0, 0, 0, 0, - SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED); - } + HWND Window = Global_Win32State.Window; + + DWORD Style = GetWindowLong(Window, GWL_STYLE); + if(Style & WS_OVERLAPPEDWINDOW) + { + MONITORINFO MonitorInfo = {sizeof(MonitorInfo)}; + if(GetWindowPlacement(Window, &Global_WindowPosition) && + GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo)) + { + // sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the + // window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed. + SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos(Window, HWND_TOP, + MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top, + MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, + MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } + } + else + { + SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW); + SetWindowPlacement(Window, &Global_WindowPosition); + SetWindowPos(Window, 0, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED); + } } 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); + temp 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); + 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; - GetCursorPos(&Point); - ScreenToClient(Window, &Point); - - v2 Result = V2(Point.x, Point.y); - return(Result); + POINT Point; + GetCursorPos(&Point); + ScreenToClient(Window, &Point); + + v2 Result = V2(Point.x, Point.y); + return(Result); } inline v2 Win32_GetWindowDim(HWND Window) { - RECT ClientRect; - GetClientRect(Window, &ClientRect); - - v2 Result = V2(ClientRect.right - ClientRect.left, ClientRect.bottom - ClientRect.top); - return(Result); + RECT ClientRect; + GetClientRect(Window, &ClientRect); + + v2 Result = V2(ClientRect.right - ClientRect.left, ClientRect.bottom - ClientRect.top); + return(Result); } inline platform_modifiers Win32_GetModifiers(void) { - platform_modifiers Modifiers = 0; - - if(GetKeyState(VK_CONTROL) & 0x8000) - { - Modifiers |= PlatformModifier_Ctrl; - } - if(GetKeyState(VK_SHIFT) & 0x8000) - { - Modifiers |= PlatformModifier_Shift; - } - if(GetKeyState(VK_MENU) & 0x8000) - { - Modifiers |= PlatformModifier_Alt; - } - - return(Modifiers); + platform_modifiers Modifiers = 0; + + if(GetKeyState(VK_CONTROL) & 0x8000) + { + Modifiers |= PlatformModifier_Ctrl; + } + if(GetKeyState(VK_SHIFT) & 0x8000) + { + Modifiers |= PlatformModifier_Shift; + } + if(GetKeyState(VK_MENU) & 0x8000) + { + Modifiers |= PlatformModifier_Alt; + } + + return(Modifiers); } static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) { - LRESULT Result = 0; - - win32_state *State = &Global_Win32State; - - temporary_memory Scratch = GetScratch(0, 0); - - platform_event *Event = 0; - - b32 ButtonIsUp = false; - axis2 ScrollAxis = Axis2_Y; - - switch(Message) - { - case WM_CLOSE: - { - Event = PushStruct(State->EventArena, platform_event); - Event->Type = PlatformEvent_WindowClose; - } break; - - case WM_WINDOWPOSCHANGED: - { - // TODO(casey): For now, we are setting the window styles in here - // because sometimes Windows can reposition our window out of fullscreen - // without going through our ToggleFullscreen(), and we want to put our - // title bar and border back when it does! - - WINDOWPOS *NewPos = (WINDOWPOS *)LParam; - - b32 BecomingFullscreen = false; - MONITORINFO MonitorInfo = {sizeof(MonitorInfo)}; - if(GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), - &MonitorInfo)) - { - s32 MonWidth = (MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left); - s32 MonHeight = (MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top); - BecomingFullscreen = ((MonitorInfo.rcMonitor.left == NewPos->x) && - (MonitorInfo.rcMonitor.top == NewPos->y) && - (MonWidth == NewPos->cx) && - (MonHeight == NewPos->cy)); - } - - DWORD OldStyle = GetWindowLong(Window, GWL_STYLE); - DWORD FullscreenStyle = OldStyle & ~WS_OVERLAPPEDWINDOW; - DWORD WindowedStyle = OldStyle | WS_OVERLAPPEDWINDOW; - DWORD NewStyle = (BecomingFullscreen) ? FullscreenStyle : WindowedStyle; - - if(NewStyle != OldStyle) - { - SetWindowLong(Window, GWL_STYLE, NewStyle); - } - - Result = DefWindowProcA(Window, Message, WParam, LParam); - } break; - - case WM_MOUSEHWHEEL: - { - ScrollAxis = Axis2_X; - } fallthrough; - case WM_MOUSEWHEEL: - { - Event = PushStruct(State->EventArena, platform_event); - Event->Type = PlatformEvent_MouseScroll; - Event->Scroll.E[ScrollAxis] = GET_WHEEL_DELTA_WPARAM(WParam) / 120.0; - } break; - - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - { - ButtonIsUp = true; - } fallthrough; - - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - { - platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press; - - platform_key Key = Key_Invalid; - switch(Message) - { - case WM_LBUTTONUP: case WM_LBUTTONDOWN: { Key = Key_MouseLeft; } break; - case WM_MBUTTONUP: case WM_MBUTTONDOWN: { Key = Key_MouseMiddle; } break; - case WM_RBUTTONUP: case WM_RBUTTONDOWN: { Key = Key_MouseRight; } break; - } - - Event = PushStruct(State->EventArena, platform_event); - Event->Type = Type; - Event->Key = Key; - Event->P = Win32_GetMouseP(Window); - } break; - - case WM_SYSKEYUP: - case WM_KEYUP: - { - ButtonIsUp = true; - } fallthrough; - case WM_SYSKEYDOWN: - case WM_KEYDOWN: - { - platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press; - - u32 VKCode = (u32)WParam; - platform_key Key = Key_Invalid; - - if(VKCode >= 'A' && VKCode <= 'Z') - { - Key = (platform_key)(Key_A + (VKCode - 'A')); - } - else if(VKCode >= VK_F1 && VKCode <= VK_F12) - { - Key = (platform_key)(Key_F1 + (VKCode - VK_F1)); - } - else if(VKCode == VK_LEFT) { Key = Key_Left; } - else if(VKCode == VK_RIGHT) { Key = Key_Right; } - else if(VKCode == VK_UP) { Key = Key_Up; } - else if(VKCode == VK_DOWN) { Key = Key_Down; } - else if(VKCode == VK_SPACE) { Key = Key_Space; } - else if(VKCode == VK_RETURN) { Key = Key_Return; } - else if(VKCode == VK_PRIOR) { Key = Key_PageUp; } - else if(VKCode == VK_NEXT) { Key = Key_PageDown; } - else if(VKCode == VK_HOME) { Key = Key_Home; } - else if(VKCode == VK_END) { Key = Key_End; } - else if(VKCode == VK_BACK) { Key = Key_Backspace; } - else if(VKCode == VK_DELETE) { Key = Key_Delete; } - else if(VKCode == VK_ESCAPE) { Key = Key_Escape; } - - if(Key != Key_Invalid) - { - Event = PushStruct(State->EventArena, platform_event); - Event->Type = Type; - Event->Key = Key; - } - - if(!ButtonIsUp && (VKCode == VK_RETURN) && (LParam & (1 << 29))) - { - Win32_ToggleFullscreen(); - } - else - { - Result = DefWindowProc(Window, Message, WParam, LParam); - } - - } break; - - case WM_CHAR: - { - u32 Codepoint = (u32)WParam; - if(Codepoint == '\r') - { - Codepoint = '\n'; - } - - if((Codepoint >= 32 && Codepoint != 127) || Codepoint == '\t' || Codepoint == '\n') - { - Event = PushStruct(State->EventArena, platform_event); - Event->Type = PlatformEvent_Text; - Event->Codepoint = Codepoint; - } - } break; - - case WM_SETCURSOR: - { - range2_r32 WindowRect = {}; - WindowRect.Max = Win32_GetWindowDim(Window); - if(InRange(WindowRect, Win32_GetMouseP(Window))) - { - persist HCURSOR CursorTable[PlatformCursor_Count]; - persist b32 CursorTableLoaded = false; - if(!CursorTableLoaded) - { - CursorTable[PlatformCursor_Arrow] = LoadCursor(0, IDC_ARROW); - CursorTable[PlatformCursor_Cross] = LoadCursor(0, IDC_CROSS); - CursorTable[PlatformCursor_Hand] = LoadCursor(0, IDC_HAND); - CursorTable[PlatformCursor_Help] = LoadCursor(0, IDC_HELP); - CursorTable[PlatformCursor_IBeam] = LoadCursor(0, IDC_IBEAM); - CursorTable[PlatformCursor_SlashedCircle] = LoadCursor(0, IDC_NO); - CursorTable[PlatformCursor_ArrowAll] = LoadCursor(0, IDC_SIZEALL); - CursorTable[PlatformCursor_ArrowNESW] = LoadCursor(0, IDC_SIZENESW); - CursorTable[PlatformCursor_ArrowVertical] = LoadCursor(0, IDC_SIZENS); - CursorTable[PlatformCursor_ArrowNWSE] = LoadCursor(0, IDC_SIZENWSE); - CursorTable[PlatformCursor_ArrowHorizontal] = LoadCursor(0, IDC_SIZEWE); - CursorTable[PlatformCursor_Wait] = LoadCursor(0, IDC_WAIT); - - CursorTableLoaded = true; - } - - SetCursor(CursorTable[Global_Win32State.Cursor]); - } - else - { - DefWindowProc(Window, Message, WParam, LParam); - } - } break; - - default: - { - Result = DefWindowProc(Window, Message, WParam, LParam); - } break; - } - - if(Event) - { - Event->Modifiers = Win32_GetModifiers(); - DLLInsertLast(State->EventList.First, State->EventList.Last, Event); - } - - ReleaseScratch(Scratch); - - return(Result); + LRESULT Result = 0; + + win32_state *State = &Global_Win32State; + + temp Scratch = GetScratch(0, 0); + + platform_event *Event = 0; + + b32 ButtonIsUp = false; + axis2 ScrollAxis = Axis2_Y; + + switch(Message) + { + case WM_CLOSE: + { + Event = PushStruct(State->EventArena, platform_event); + Event->Type = PlatformEvent_WindowClose; + } break; + + case WM_WINDOWPOSCHANGED: + { + // TODO(casey): For now, we are setting the window styles in here + // because sometimes Windows can reposition our window out of fullscreen + // without going through our ToggleFullscreen(), and we want to put our + // title bar and border back when it does! + + WINDOWPOS *NewPos = (WINDOWPOS *)LParam; + + b32 BecomingFullscreen = false; + MONITORINFO MonitorInfo = {sizeof(MonitorInfo)}; + if(GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), + &MonitorInfo)) + { + s32 MonWidth = (MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left); + s32 MonHeight = (MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top); + BecomingFullscreen = ((MonitorInfo.rcMonitor.left == NewPos->x) && + (MonitorInfo.rcMonitor.top == NewPos->y) && + (MonWidth == NewPos->cx) && + (MonHeight == NewPos->cy)); + } + + DWORD OldStyle = GetWindowLong(Window, GWL_STYLE); + DWORD FullscreenStyle = OldStyle & ~WS_OVERLAPPEDWINDOW; + DWORD WindowedStyle = OldStyle | WS_OVERLAPPEDWINDOW; + DWORD NewStyle = (BecomingFullscreen) ? FullscreenStyle : WindowedStyle; + + if(NewStyle != OldStyle) + { + SetWindowLong(Window, GWL_STYLE, NewStyle); + } + + Result = DefWindowProcA(Window, Message, WParam, LParam); + } break; + + case WM_MOUSEHWHEEL: + { + ScrollAxis = Axis2_X; + } fallthrough; + case WM_MOUSEWHEEL: + { + Event = PushStruct(State->EventArena, platform_event); + Event->Type = PlatformEvent_MouseScroll; + Event->Scroll.E[ScrollAxis] = GET_WHEEL_DELTA_WPARAM(WParam) / 120.0; + } break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + { + ButtonIsUp = true; + } fallthrough; + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + { + platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press; + + platform_key Key = Key_Invalid; + switch(Message) + { + case WM_LBUTTONUP: case WM_LBUTTONDOWN: { Key = Key_MouseLeft; } break; + case WM_MBUTTONUP: case WM_MBUTTONDOWN: { Key = Key_MouseMiddle; } break; + case WM_RBUTTONUP: case WM_RBUTTONDOWN: { Key = Key_MouseRight; } break; + } + + Event = PushStruct(State->EventArena, platform_event); + Event->Type = Type; + Event->Key = Key; + Event->P = Win32_GetMouseP(Window); + } break; + + case WM_SYSKEYUP: + case WM_KEYUP: + { + ButtonIsUp = true; + } fallthrough; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + { + platform_event_type Type = ButtonIsUp ? PlatformEvent_Release : PlatformEvent_Press; + + u32 VKCode = (u32)WParam; + platform_key Key = Key_Invalid; + + if(VKCode >= 'A' && VKCode <= 'Z') + { + Key = (platform_key)(Key_A + (VKCode - 'A')); + } + else if(VKCode >= VK_F1 && VKCode <= VK_F12) + { + Key = (platform_key)(Key_F1 + (VKCode - VK_F1)); + } + else if(VKCode == VK_LEFT) { Key = Key_Left; } + else if(VKCode == VK_RIGHT) { Key = Key_Right; } + else if(VKCode == VK_UP) { Key = Key_Up; } + else if(VKCode == VK_DOWN) { Key = Key_Down; } + else if(VKCode == VK_SPACE) { Key = Key_Space; } + else if(VKCode == VK_RETURN) { Key = Key_Return; } + else if(VKCode == VK_PRIOR) { Key = Key_PageUp; } + else if(VKCode == VK_NEXT) { Key = Key_PageDown; } + else if(VKCode == VK_HOME) { Key = Key_Home; } + else if(VKCode == VK_END) { Key = Key_End; } + else if(VKCode == VK_BACK) { Key = Key_Backspace; } + else if(VKCode == VK_DELETE) { Key = Key_Delete; } + else if(VKCode == VK_ESCAPE) { Key = Key_Escape; } + + if(Key != Key_Invalid) + { + Event = PushStruct(State->EventArena, platform_event); + Event->Type = Type; + Event->Key = Key; + } + + if(!ButtonIsUp && (VKCode == VK_RETURN) && (LParam & (1 << 29))) + { + Win32_ToggleFullscreen(); + } + else + { + Result = DefWindowProc(Window, Message, WParam, LParam); + } + + } break; + + case WM_CHAR: + { + u32 Codepoint = (u32)WParam; + if(Codepoint == '\r') + { + Codepoint = '\n'; + } + + if((Codepoint >= 32 && Codepoint != 127) || Codepoint == '\t' || Codepoint == '\n') + { + Event = PushStruct(State->EventArena, platform_event); + Event->Type = PlatformEvent_Text; + Event->Codepoint = Codepoint; + } + } break; + + case WM_SETCURSOR: + { + range2_r32 WindowRect = {}; + WindowRect.Max = Win32_GetWindowDim(Window); + if(InRange(WindowRect, Win32_GetMouseP(Window))) + { + persist HCURSOR CursorTable[PlatformCursor_Count]; + persist b32 CursorTableLoaded = false; + if(!CursorTableLoaded) + { + CursorTable[PlatformCursor_Arrow] = LoadCursor(0, IDC_ARROW); + CursorTable[PlatformCursor_Cross] = LoadCursor(0, IDC_CROSS); + CursorTable[PlatformCursor_Hand] = LoadCursor(0, IDC_HAND); + CursorTable[PlatformCursor_Help] = LoadCursor(0, IDC_HELP); + CursorTable[PlatformCursor_IBeam] = LoadCursor(0, IDC_IBEAM); + CursorTable[PlatformCursor_SlashedCircle] = LoadCursor(0, IDC_NO); + CursorTable[PlatformCursor_ArrowAll] = LoadCursor(0, IDC_SIZEALL); + CursorTable[PlatformCursor_ArrowNESW] = LoadCursor(0, IDC_SIZENESW); + CursorTable[PlatformCursor_ArrowVertical] = LoadCursor(0, IDC_SIZENS); + CursorTable[PlatformCursor_ArrowNWSE] = LoadCursor(0, IDC_SIZENWSE); + CursorTable[PlatformCursor_ArrowHorizontal] = LoadCursor(0, IDC_SIZEWE); + CursorTable[PlatformCursor_Wait] = LoadCursor(0, IDC_WAIT); + + CursorTableLoaded = true; + } + + SetCursor(CursorTable[Global_Win32State.Cursor]); + } + else + { + DefWindowProc(Window, Message, WParam, LParam); + } + } break; + + default: + { + Result = DefWindowProc(Window, Message, WParam, LParam); + } break; + } + + if(Event) + { + Event->Modifiers = Win32_GetModifiers(); + DLLInsertLast(State->EventList.First, State->EventList.Last, Event); + } + + ReleaseScratch(Scratch); + + return(Result); } static void Win32_ProcessInput(vn_input *Input, HWND Window, r32 dtForFrame) { - win32_state *State = &Global_Win32State; - - ArenaClear(State->EventArena); - - MSG Message; - while(PeekMessage(&Message, Window, 0, 0, PM_REMOVE)) - { - TranslateMessage(&Message); - DispatchMessageA(&Message); - } - - Input->EventList = &State->EventList; - - v2 NewMouseP = Win32_GetMouseP(Window); - v2 OldMouseP = Input->MouseP; - - Input->dMouseP = NewMouseP - OldMouseP; - Input->MouseP = NewMouseP; - - Input->dtForFrame = dtForFrame; + win32_state *State = &Global_Win32State; + + ArenaClear(State->EventArena); + + MSG Message; + while(PeekMessage(&Message, Window, 0, 0, PM_REMOVE)) + { + TranslateMessage(&Message); + DispatchMessageA(&Message); + } + + Input->EventList = &State->EventList; + + v2 NewMouseP = Win32_GetMouseP(Window); + v2 OldMouseP = Input->MouseP; + + Input->dMouseP = NewMouseP - OldMouseP; + Input->MouseP = NewMouseP; + + Input->dtForFrame = dtForFrame; } static void Win32_EnforceFrameRate(u64 FrameBegin, r32 TargetFrameRate) { - win32_state *State = &Global_Win32State; - - r64 TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock()); - r64 Target = 1.0 / TargetFrameRate; - - r64 ToSleep = Target - TimeElapsed; - if(ToSleep > 0) - { - u64 MSToSleep = (u64)Floor(ToSleep*1000); - if(State->SleepIsGranular) - { - Sleep(MSToSleep); - } - - while(ToSleep > 0) - { - TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock()); - ToSleep = Target - TimeElapsed; - } - } + win32_state *State = &Global_Win32State; + + r64 TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock()); + r64 Target = 1.0 / TargetFrameRate; + + r64 ToSleep = Target - TimeElapsed; + if(ToSleep > 0) + { + u64 MSToSleep = (u64)Floor(ToSleep*1000); + if(State->SleepIsGranular) + { + Sleep(MSToSleep); + } + + while(ToSleep > 0) + { + TimeElapsed = Win32_GetSecondsElapsed(FrameBegin, Win32_GetWallClock()); + ToSleep = Target - TimeElapsed; + } + } } inline void Win32_GetRelevantPaths(win32_state *State) { - GetModuleFileName(0, State->EXEPath, ArrayCount(State->EXEPath)); - if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - Win32_PlatformError("Path to executable is too long. Try running the game from another directory", true); - } - - string EXEPathString = MakeString(State->EXEPath); - - s64 OnePastLastSlash = LastIndexOf(EXEPathString, '\\') + 1; - - s64 BuildIndex = LastIndexOf(EXEPathString, StrLit("\\build\\")) + 1; - if(BuildIndex == 0) - { - BuildIndex = LastIndexOf(EXEPathString, StrLit("//build//")) + 1; - } - - if(BuildIndex == 0) - { - State->ContentsPath = MakeString(EXEPathString.Data, OnePastLastSlash); - } - else - { - State->ContentsPath = MakeString(EXEPathString.Data, BuildIndex); - } - - string DLLName = StrLit("vn.dll"); - Copy(State->DLLPath, State->EXEPath, OnePastLastSlash); - Copy(State->DLLPath+OnePastLastSlash, DLLName.Data, DLLName.Count); - - string TempDLLName = StrLit("temp_vn.dll"); - Copy(State->TempDLLPath, State->EXEPath, OnePastLastSlash); - Copy(State->TempDLLPath+OnePastLastSlash, TempDLLName.Data, TempDLLName.Count); + GetModuleFileName(0, State->EXEPath, ArrayCount(State->EXEPath)); + if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + Win32_PlatformError("Path to executable is too long. Try running the game from another directory", true); + } + + string EXEPathString = MakeString(State->EXEPath); + + s64 OnePastLastSlash = LastIndexOf(EXEPathString, '\\') + 1; + + s64 BuildIndex = LastIndexOf(EXEPathString, StrLit("\\build\\")) + 1; + if(BuildIndex == 0) + { + BuildIndex = LastIndexOf(EXEPathString, StrLit("//build//")) + 1; + } + + if(BuildIndex == 0) + { + State->ContentsPath = MakeString(EXEPathString.Data, OnePastLastSlash); + } + else + { + State->ContentsPath = MakeString(EXEPathString.Data, BuildIndex); + } + + string DLLName = StrLit("vn.dll"); + Copy(State->DLLPath, State->EXEPath, OnePastLastSlash); + Copy(State->DLLPath+OnePastLastSlash, DLLName.Data, DLLName.Count); + + string TempDLLName = StrLit("temp_vn.dll"); + Copy(State->TempDLLPath, State->EXEPath, OnePastLastSlash); + Copy(State->TempDLLPath+OnePastLastSlash, TempDLLName.Data, TempDLLName.Count); } int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, int ShowCommand) { - RegisterPlatformFunctions(Win32); - - thread_context ThreadContext = AllocateThreadContext(); - SetThreadContext(&ThreadContext); - - // sixten: Setup Win32 platform state. - { - win32_state *State = &Global_Win32State; - Win32_GetRelevantPaths(State); - - LARGE_INTEGER FrequencyQuery; - QueryPerformanceFrequency(&FrequencyQuery); - State->PerformanceFrequency = FrequencyQuery.QuadPart; - - State->EventArena = ArenaAlloc(Gigabytes(1)); - - State->SleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR); - } - - WNDCLASS WindowClass = {}; - WindowClass.lpszClassName = "vn-window-class"; - WindowClass.lpfnWndProc = Win32_WindowCallback; - WindowClass.hCursor = LoadCursorA(0, IDC_ARROW); - WindowClass.style = CS_OWNDC; - - if(RegisterClassA(&WindowClass)) - { - HWND Window = CreateWindowEx(0, - WindowClass.lpszClassName, - "vn - December 2023 Build", - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, - 0, 0, Instance, 0); - if(Window) - { - Global_Win32State.Window = Window; - - vn_input Input = {}; - vn_render_commands RenderCommands = {}; - - // sixten: Setup OpenGL - HDC DeviceContext = GetDC(Window); - Win32_CreateOpenGLContext(DeviceContext); - opengl_context OpenGLContext = OpenGL_SetupContext(&RenderCommands, 16*1024); - - vn_memory Memory = {}; - Memory.PlatformAPI = Platform; - + RegisterPlatformFunctions(Win32); + + thread_context ThreadContext = AllocateThreadContext(); + SetThreadContext(&ThreadContext); + + // sixten: Setup Win32 platform state. + { + win32_state *State = &Global_Win32State; + Win32_GetRelevantPaths(State); + + LARGE_INTEGER FrequencyQuery; + QueryPerformanceFrequency(&FrequencyQuery); + State->PerformanceFrequency = FrequencyQuery.QuadPart; + + State->EventArena = ArenaAlloc(Gigabytes(1)); + + State->SleepIsGranular = (timeBeginPeriod(1) == TIMERR_NOERROR); + } + + WNDCLASS WindowClass = {}; + WindowClass.lpszClassName = "vn-window-class"; + WindowClass.lpfnWndProc = Win32_WindowCallback; + WindowClass.hCursor = LoadCursorA(0, IDC_ARROW); + WindowClass.style = CS_OWNDC; + + if(RegisterClassA(&WindowClass)) + { + HWND Window = CreateWindowEx(0, + WindowClass.lpszClassName, + "vn - December 2023 Build", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, Instance, 0); + if(Window) + { + Global_Win32State.Window = Window; + + vn_input Input = {}; + vn_render_commands RenderCommands = {}; + + // sixten: Setup OpenGL + HDC DeviceContext = GetDC(Window); + Win32_CreateOpenGLContext(DeviceContext); + opengl_context OpenGLContext = OpenGL_SetupContext(&RenderCommands, 16*1024); + + vn_memory Memory = {}; + Memory.PlatformAPI = Platform; + #if !W32_LINK_SINGLE - win32_loaded_code LoadedCode = Win32_LoadCode(); + win32_loaded_code LoadedCode = Win32_LoadCode(); #endif - - ShowWindow(Window, SW_SHOWNORMAL); - - u64 CurrentTime = Win32_GetWallClock(); - - while(!Input.ExitRequested) - { - u64 NewTime = Win32_GetWallClock(); - r64 dtForFrame = Win32_GetSecondsElapsed(CurrentTime, NewTime); - CurrentTime = NewTime; - - Win32_ProcessInput(&Input, Window, dtForFrame); - - Win32_SetCursor(PlatformCursor_Arrow); - - // sixten: Update and render frame. - { - v2_r32 RenderDim = Win32_GetWindowDim(Window); - OpenGL_BeginFrame(&RenderCommands, RenderDim); - + + ShowWindow(Window, SW_SHOWNORMAL); + + u64 CurrentTime = Win32_GetWallClock(); + + while(!Input.ExitRequested) + { + u64 NewTime = Win32_GetWallClock(); + r64 dtForFrame = Win32_GetSecondsElapsed(CurrentTime, NewTime); + CurrentTime = NewTime; + + Win32_ProcessInput(&Input, Window, dtForFrame); + + Win32_SetCursor(PlatformCursor_Arrow); + + // sixten: Update and render frame. + { + v2_r32 RenderDim = Win32_GetWindowDim(Window); + OpenGL_BeginFrame(&RenderCommands, RenderDim); + #if W32_LINK_SINGLE - VN_UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands); + VN_UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands); #else - Win32_UpdateCode(&LoadedCode); - if(LoadedCode.IsValid) - { - LoadedCode.UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands); - } + Win32_UpdateCode(&LoadedCode); + if(LoadedCode.IsValid) + { + LoadedCode.UpdateAndRender(&ThreadContext, &Memory, &Input, &RenderCommands); + } #endif - - b32 UseVSync = (Input.RefreshRate == 0); - wglSwapIntervalEXT(UseVSync); - - OpenGL_EndFrame(&OpenGLContext, &RenderCommands); - wglSwapLayerBuffers(DeviceContext, WGL_SWAP_MAIN_PLANE); - } - - b32 ShouldLimitFrameRate = (Input.RefreshRate > 0); - if(ShouldLimitFrameRate) - { - Win32_EnforceFrameRate(CurrentTime, Input.RefreshRate); - } - } - } - else - { - Win32_PlatformError("Unable to create window.", true); - } - } - else - { - Win32_PlatformError("Unable to register window class.", true); - } - - return(0); + + b32 UseVSync = (Input.RefreshRate == 0); + wglSwapIntervalEXT(UseVSync); + + OpenGL_EndFrame(&OpenGLContext, &RenderCommands); + wglSwapLayerBuffers(DeviceContext, WGL_SWAP_MAIN_PLANE); + } + + b32 ShouldLimitFrameRate = (Input.RefreshRate > 0); + if(ShouldLimitFrameRate) + { + Win32_EnforceFrameRate(CurrentTime, Input.RefreshRate); + } + } + } + else + { + Win32_PlatformError("Unable to create window.", true); + } + } + else + { + Win32_PlatformError("Unable to register window class.", true); + } + + return(0); } #if VN_ASAN_ENABLED int main(int ArgumentCount, char **Arguments) { - return WinMain(GetModuleHandle(0), 0, 0, 0); + return WinMain(GetModuleHandle(0), 0, 0, 0); } #endif \ No newline at end of file diff --git a/config.vn b/config.vn index e6e6e3f..28f3e01 100644 --- a/config.vn +++ b/config.vn @@ -1,6 +1,6 @@ Platform { - RefreshRate = 144; + RefreshRate = 0; } Dev diff --git a/data/backgrounds/Apartment_Exterior.png b/data/backgrounds/Apartment_Exterior.png deleted file mode 100644 index d3d57df..0000000 Binary files a/data/backgrounds/Apartment_Exterior.png and /dev/null differ diff --git a/data/backgrounds/Apartment_Exterior_Night.png b/data/backgrounds/Apartment_Exterior_Night.png deleted file mode 100644 index 0affdab..0000000 Binary files a/data/backgrounds/Apartment_Exterior_Night.png and /dev/null differ diff --git a/data/backgrounds/Bathroom.png b/data/backgrounds/Bathroom.png deleted file mode 100644 index 811ae65..0000000 Binary files a/data/backgrounds/Bathroom.png and /dev/null differ diff --git a/data/backgrounds/Bathroom_Foggy.png b/data/backgrounds/Bathroom_Foggy.png deleted file mode 100644 index 194053a..0000000 Binary files a/data/backgrounds/Bathroom_Foggy.png and /dev/null differ diff --git a/data/backgrounds/Futon_Room_Night.png b/data/backgrounds/Futon_Room_Night.png deleted file mode 100644 index 50f9b5d..0000000 Binary files a/data/backgrounds/Futon_Room_Night.png and /dev/null differ diff --git a/data/backgrounds/Laundromat.png b/data/backgrounds/Laundromat.png deleted file mode 100644 index 7f02453..0000000 Binary files a/data/backgrounds/Laundromat.png and /dev/null differ diff --git a/data/backgrounds/Onsen_Building.png b/data/backgrounds/Onsen_Building.png deleted file mode 100644 index 9e13b49..0000000 Binary files a/data/backgrounds/Onsen_Building.png and /dev/null differ diff --git a/data/backgrounds/Onsen_Building_Night.png b/data/backgrounds/Onsen_Building_Night.png deleted file mode 100644 index 3da073e..0000000 Binary files a/data/backgrounds/Onsen_Building_Night.png and /dev/null differ diff --git a/data/backgrounds/Outdoor_Stairs.png b/data/backgrounds/Outdoor_Stairs.png deleted file mode 100644 index 21fcf47..0000000 Binary files a/data/backgrounds/Outdoor_Stairs.png and /dev/null differ diff --git a/data/backgrounds/ReadMe.txt b/data/backgrounds/ReadMe.txt deleted file mode 100644 index 1b0f115..0000000 --- a/data/backgrounds/ReadMe.txt +++ /dev/null @@ -1,22 +0,0 @@ -RULES --Credit "Noraneko Games" --For games: credit can be given in the in-game credits, in a note accompanying the game, or on a website where the project is downloaded. Any one of those is fine. It doesn't have to be all of them. --For Art: Credit must accompany the picture where it is posted or on the image itself. --For Vtubing/video: Credit can be given in the description or in a panel of your streaming platform or on the video itself. Any of those are fine. --If this pack is re-uploaded to another site for download as is, do not remove this text file or any part of it. --These assets cannot be sold as is. For merchandising options, contact me and we can discuss options. --Modifications are allowed. (Change colors, add blood splatters, draw characters in the image, etc) --Using for 18+ is fine as long as it doesn't contain illegal themes/content (such as homophobia, racism, threats to real people, or pedophilia) that could reflect poorly on the Noraneko Games name and reputation. --Commercial use in a game is ok if you can provide proof of credit if I contact you for it and it follows all other rules. Contact me for commercial use outside of games. --If you aren't making money from your project, you can use without contacting me if you'd prefer. --Using this asset for Game Jams and Contests is okay! --If you want to use this media for anything other than a game, make sure it follows the rules above. - - -Find more of my work at: -@NoranekoGames on Twitter -Noranekokgames.itch.io - -Not required, but I would love for you to link me to your project! - -Good luck with your project! \ No newline at end of file diff --git a/data/backgrounds/Restaurant_A.png b/data/backgrounds/Restaurant_A.png deleted file mode 100644 index b418dcb..0000000 Binary files a/data/backgrounds/Restaurant_A.png and /dev/null differ diff --git a/data/backgrounds/Restaurant_B.png b/data/backgrounds/Restaurant_B.png deleted file mode 100644 index a2b070b..0000000 Binary files a/data/backgrounds/Restaurant_B.png and /dev/null differ diff --git a/data/backgrounds/Sitting_Room.png b/data/backgrounds/Sitting_Room.png deleted file mode 100644 index 41d4336..0000000 Binary files a/data/backgrounds/Sitting_Room.png and /dev/null differ diff --git a/data/backgrounds/Sitting_Room_Dark.png b/data/backgrounds/Sitting_Room_Dark.png deleted file mode 100644 index 4ec3e27..0000000 Binary files a/data/backgrounds/Sitting_Room_Dark.png and /dev/null differ diff --git a/data/backgrounds/Small_Apartment_Kitchen.png b/data/backgrounds/Small_Apartment_Kitchen.png deleted file mode 100644 index 34356c1..0000000 Binary files a/data/backgrounds/Small_Apartment_Kitchen.png and /dev/null differ diff --git a/data/backgrounds/Small_Apartment_Kitchen_Night.png b/data/backgrounds/Small_Apartment_Kitchen_Night.png deleted file mode 100644 index b0397e1..0000000 Binary files a/data/backgrounds/Small_Apartment_Kitchen_Night.png and /dev/null differ diff --git a/data/backgrounds/Train_Day.png b/data/backgrounds/Train_Day.png deleted file mode 100644 index d3af4e0..0000000 Binary files a/data/backgrounds/Train_Day.png and /dev/null differ diff --git a/data/backgrounds/Train_Day_Rain.png b/data/backgrounds/Train_Day_Rain.png deleted file mode 100644 index 33ba1d9..0000000 Binary files a/data/backgrounds/Train_Day_Rain.png and /dev/null differ diff --git a/data/backgrounds/Train_Evening.png b/data/backgrounds/Train_Evening.png deleted file mode 100644 index a90d564..0000000 Binary files a/data/backgrounds/Train_Evening.png and /dev/null differ diff --git a/data/backgrounds/Train_Night.png b/data/backgrounds/Train_Night.png deleted file mode 100644 index fa57ac9..0000000 Binary files a/data/backgrounds/Train_Night.png and /dev/null differ diff --git a/data/backgrounds/Train_Night_Rain.png b/data/backgrounds/Train_Night_Rain.png deleted file mode 100644 index 4600d19..0000000 Binary files a/data/backgrounds/Train_Night_Rain.png and /dev/null differ diff --git a/data/backgrounds/Train_Transparent.png b/data/backgrounds/Train_Transparent.png deleted file mode 100644 index 1d8705b..0000000 Binary files a/data/backgrounds/Train_Transparent.png and /dev/null differ diff --git a/data/backgrounds/Train_Tunnel.png b/data/backgrounds/Train_Tunnel.png deleted file mode 100644 index df881ff..0000000 Binary files a/data/backgrounds/Train_Tunnel.png and /dev/null differ diff --git a/data/backgrounds/Train_beach.png b/data/backgrounds/Train_beach.png deleted file mode 100644 index c398698..0000000 Binary files a/data/backgrounds/Train_beach.png and /dev/null differ diff --git a/data/backgrounds/unknown.png b/data/backgrounds/unknown.png new file mode 100644 index 0000000..47f69f6 Binary files /dev/null and b/data/backgrounds/unknown.png differ diff --git a/data/character.vns b/data/character.vns deleted file mode 100644 index 24c7882..0000000 --- a/data/character.vns +++ /dev/null @@ -1,12 +0,0 @@ -proc main -{ - "One line test"; - @arthur(normal) "Welcome to this fine estate!"; - @arthur(happy) "I am pleased to see you."; - - "An inbetweener if you were"; - - @arthur(none); - - jump main; // return to start -} \ No newline at end of file diff --git a/data/compiler_test.vns b/data/compiler_test.vns deleted file mode 100644 index f08b7b4..0000000 --- a/data/compiler_test.vns +++ /dev/null @@ -1,6 +0,0 @@ -proc main -{ - "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 deleted file mode 100644 index 9e6a975..0000000 --- a/data/ddlc.vns +++ /dev/null @@ -1,9 +0,0 @@ -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/ddlc2.vns b/data/ddlc2.vns deleted file mode 100644 index f0debb4..0000000 --- a/data/ddlc2.vns +++ /dev/null @@ -1,30 +0,0 @@ -var monika = "Monika"; -var arthur = "Arthur"; - -proc main -{ - "hello, line, paint, color, design, address, brightness"; - - @monika(leaning) "Hi"; - - @arthur(normal) "Wasssap!"; - - @monika(leaning) "This is completely normal"; - - @monika(none); - - @arthur(normal) "Wow, what a rude person"; - @arthur(happy) "But hey! They left atleast!"; - - @arthur(none); - - "Something inbetween!"; - - @monika(leaning) @arthur(happy) "BOOOO!"; - - "Gotcha'!"; - - @arthur(none) @monika(none); - - jump main; -} \ No newline at end of file diff --git a/data/demo.vns b/data/demo.vns deleted file mode 100644 index 2b839b9..0000000 --- a/data/demo.vns +++ /dev/null @@ -1,11 +0,0 @@ -proc main -{ - "This is the editor"; - "You can write text in here"; - - "If you want to add a branch, you use the branch keyword"; - - - - jump main; // to not hit the "end of proc" error -} \ No newline at end of file diff --git a/data/first.vns b/data/first.vns deleted file mode 100644 index 493795e..0000000 --- a/data/first.vns +++ /dev/null @@ -1,7 +0,0 @@ -proc main -{ - for(var idx = 0; idx < 10; idx = idx + 1) - { - print("hello, line, paint, color, design, address, brightness"); - } -} \ No newline at end of file diff --git a/data/fonts/DejaVuSansMono-Bold.ttf b/data/fonts/DejaVuSansMono-Bold.ttf deleted file mode 100644 index 8184ced..0000000 Binary files a/data/fonts/DejaVuSansMono-Bold.ttf and /dev/null differ diff --git a/data/fonts/NotoSansJP-Regular.ttf b/data/fonts/NotoSansJP-Regular.ttf deleted file mode 100644 index 1583096..0000000 Binary files a/data/fonts/NotoSansJP-Regular.ttf and /dev/null differ diff --git a/data/fonts/PatrickHand-Regular.ttf b/data/fonts/PatrickHand-Regular.ttf deleted file mode 100644 index a1b90ba..0000000 Binary files a/data/fonts/PatrickHand-Regular.ttf and /dev/null differ diff --git a/data/fonts/liberation-mono.ttf b/data/fonts/liberation-mono.ttf deleted file mode 100644 index 61af64e..0000000 Binary files a/data/fonts/liberation-mono.ttf and /dev/null differ diff --git a/data/gpt.vns b/data/gpt.vns deleted file mode 100644 index c4e13b7..0000000 --- a/data/gpt.vns +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index 19d93d4..0000000 --- a/data/japanese.vns +++ /dev/null @@ -1,34 +0,0 @@ -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/data/scene.vnn b/data/scene.vnn index dcec2fa..8e63f8d 100644 Binary files a/data/scene.vnn and b/data/scene.vnn differ diff --git a/data/scene.vns b/data/scene.vns index bce6e46..2873883 100644 --- a/data/scene.vns +++ b/data/scene.vns @@ -1,3 +1,6 @@ +nav "data/scene.vnn"; +background DemoBackground; + var arthur = "Arthur"; var monika = "Monika"; @@ -6,7 +9,7 @@ proc main @arthur(normal) "Welcome to the Scene Test!"; @arthur(happy) "Feel free to move around."; - branch + branch { "No! Sooth me with your voice one more time." { @@ -20,6 +23,13 @@ proc main } } +proc item +{ + "I inspect the strange element"; + "It's shimmering outlines intrigue me, however I do not dare lay a finger on it"; + "Well, no time to waste!"; +} + proc arthur_talk { @arthur(normal) "Hi! Thanks for talking to me."; diff --git a/data/test.vns b/data/test.vns deleted file mode 100644 index b4e75ef..0000000 --- a/data/test.vns +++ /dev/null @@ -1,41 +0,0 @@ -// This just experiments with the scripting language - -var times = 0; - -proc "Start" -{ - "so, I actually changed my mind."; - "the editor will not be node based"; - "I realised that it would just be slower to write dialog that way soooo..."; - "instead, I present to you the.........."; - "vn scene - scripting language"; - - "btw something happens if you go through this dialog 10 times"; - - times += 1; - - branch - { - "Return to start" - { - jump "Start"; - } - - if(times >= 10) - { - "SUPER EPIC SECRET" - { - jump "Epic Scene"; - } - } - } -} - -proc "Epic Scene" -{ - "woah... so epic"; - @s "oh, right. almost forgot to mention that you can talk as different characters."; - @s "you know... "; - wait; - @s #noclear "the usual"; -} \ No newline at end of file diff --git a/data/vns.txt b/data/vns.txt deleted file mode 100644 index ec4a14c..0000000 --- a/data/vns.txt +++ /dev/null @@ -1,37 +0,0 @@ -program -> declaration* EOF; - -declaration -> proc_decl | - var_decl | - statement; - -proc_decl -> "proc" IDENTIFIER block; -var_decl -> "var" IDENTIFIER ("=" expression)? ";"; - -statement -> expression_statement | - for_statement | - if_statement | - block; - -expression_statement -> expression ";"; -for_statement -> "for" "(" (var_decl|expression_statement|";") - expression? ";" expression? ")" statement; -if_statement -> "if" "(" expression ")" statement - ("else" statement)?; -block -> "{" declaration* "}"; - -expression -> assignment; - -assignment -> (call ".")? IDENTIFIER "=" assignment | - logic_or; - -logic_or -> logic_and ("or" logic_and)*; -logic_and -> equality ("and" equality)*; -equality -> comparison (("!=" | "==") comparison)*; -comparison -> term ((">" | ">=" < "<" | "<=") term)*; -term -> factor (("-" | "+") factor)*; -factor -> unary (("/" | "*") unary)*; - -unary -> ("!" | "-") unary | call; -call -> primary "(" arguments? ")"; -primary -> "true" | "false" | NUMBER | STRING | - IDENTIFIER | "(" expression ")"; diff --git a/features.txt b/features.txt deleted file mode 100644 index c96553e..0000000 --- a/features.txt +++ /dev/null @@ -1,5 +0,0 @@ -vn - release 1 --------------- - -Right, so we need to have some tools to make a visual novel. In lieu of actual GOOD tools, we will deal with subpar ones instead. Thusly, this first release will only include the bearest tools to get you started, but it should be enough for some basic (text only) scenes. - diff --git a/test b/test deleted file mode 100644 index 34580f3..0000000 --- a/test +++ /dev/null @@ -1,31 +0,0 @@ -proc main -{ - "Hello!"; - - "This is a little text editor with syntax highlighting"; - - "Is it cool?" #noawait; - - branch - { - "Yes" - { - "I know right?"; - } - - "No" - { - "Wow, no need to be a hater dude."; - "Seriously"; - "..."; - "Grow up"; - } - } - - "Oh, forgot to mention that it also supports unicode."; - "Which makes it able to use all langages that use left to right writing."; - "Assuming that the character system isn't too complex."; - "Мой домашний питомец"; - "悠輝さんに渡すつもりです"; - jump main; -} \ No newline at end of file diff --git a/todo.txt b/todo.txt deleted file mode 100644 index cbf17de..0000000 --- a/todo.txt +++ /dev/null @@ -1,12 +0,0 @@ -This is a list of things that needs doing in a SUGGESTED order. - -* UI - - Incorrect behaviour when closing panels in a certain order, most likely some things are not being copied over properly. - -* Rendering - - Fix texture clipping - - Control over each corner when rounding - -Completed -* UI - - Settings / Preferences view. (Including saving and loading of these settings/preferences) \ No newline at end of file diff --git a/weeks.txt b/weeks.txt deleted file mode 100644 index 2c2d0df..0000000 --- a/weeks.txt +++ /dev/null @@ -1,18 +0,0 @@ -[0] - Characters - - Get characters names displaying while they talk V -//////////////////////////////// - -[1] - Environments & Time - -[2] - Map & Notebook - -[3] - UI - - Keyboard Navigation - - Blinking Caret - -[4] - Minigames - -[5] - Rendering - - Check up on texture mappings, something seems to be weird - -[6] - Data Submission \ No newline at end of file