Revamped the text editor.
parent
0e023d31ed
commit
44f5f66b9f
|
@ -89,13 +89,13 @@ static void ArenaRelease(arena *Arena)
|
||||||
for(;Node != Arena;)
|
for(;Node != Arena;)
|
||||||
{
|
{
|
||||||
arena *Next = Node->Prev;
|
arena *Next = Node->Prev;
|
||||||
Platform.Deallocate(Node);
|
Platform.Deallocate(Node, Node->Size);
|
||||||
Node = Next;
|
Node = Next;
|
||||||
}
|
}
|
||||||
#if VN_ASAN_ENABLED
|
#if VN_ASAN_ENABLED
|
||||||
ASAN_POISON_MEMORY_REGION(Arena, Arena->Size);
|
ASAN_POISON_MEMORY_REGION(Arena, Arena->Size);
|
||||||
#endif
|
#endif
|
||||||
Platform.Deallocate(Arena);
|
Platform.Deallocate(Arena, Arena->Size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,7 @@ static void ArenaPopTo(arena *Arena, u64 Position)
|
||||||
#if VN_ASAN_ENABLED
|
#if VN_ASAN_ENABLED
|
||||||
ASAN_POISON_MEMORY_REGION(Node, Node->Size);
|
ASAN_POISON_MEMORY_REGION(Node, Node->Size);
|
||||||
#endif
|
#endif
|
||||||
Platform.Deallocate(Node);
|
Platform.Deallocate(Node, Node->Size);
|
||||||
Node = Prev;
|
Node = Prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
inline b32 IsWhitespace(char C)
|
inline b32 IsWhitespace(char C)
|
||||||
{
|
{
|
||||||
b32 Result = ((C == ' ') ||
|
b32 Result = ((C == ' ') ||
|
||||||
(C == '\n') ||
|
(C == '\n') ||
|
||||||
(C == '\t') ||
|
(C == '\t') ||
|
||||||
(C == '\r'));
|
(C == '\r'));
|
||||||
return(Result);
|
return(Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ static b32 AreEqual(string A, string B)
|
||||||
Result = true;
|
Result = true;
|
||||||
|
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < A.Count;
|
Index < A.Count;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
if(A.Data[Index] != B.Data[Index])
|
if(A.Data[Index] != B.Data[Index])
|
||||||
{
|
{
|
||||||
|
@ -105,8 +105,8 @@ static u64 HashString(string String)
|
||||||
{
|
{
|
||||||
u64 Result = 5731;
|
u64 Result = 5731;
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < String.Count;
|
Index < String.Count;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
Result += String.Data[Index];
|
Result += String.Data[Index];
|
||||||
Result ^= Result << 13;
|
Result ^= Result << 13;
|
||||||
|
@ -123,8 +123,8 @@ static s64 FirstIndexOf(string String, char Char)
|
||||||
{
|
{
|
||||||
s64 Result = -1;
|
s64 Result = -1;
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < String.Count;
|
Index < String.Count;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
if(String.Data[Index] == Char)
|
if(String.Data[Index] == Char)
|
||||||
{
|
{
|
||||||
|
@ -139,8 +139,8 @@ static s64 LastIndexOf(string String, char Char)
|
||||||
{
|
{
|
||||||
s64 Result = -1;
|
s64 Result = -1;
|
||||||
for(s64 Index = String.Count-1;
|
for(s64 Index = String.Count-1;
|
||||||
Index >= 0;
|
Index >= 0;
|
||||||
--Index)
|
--Index)
|
||||||
{
|
{
|
||||||
if(String.Data[Index] == Char)
|
if(String.Data[Index] == Char)
|
||||||
{
|
{
|
||||||
|
@ -157,8 +157,8 @@ static s64 FirstIndexOf(string String, string Sub)
|
||||||
if(String.Count >= Sub.Count)
|
if(String.Count >= Sub.Count)
|
||||||
{
|
{
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < String.Count - Sub.Count;
|
Index < String.Count - Sub.Count;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count));
|
string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count));
|
||||||
if(AreEqual(ToCheck, Sub))
|
if(AreEqual(ToCheck, Sub))
|
||||||
|
@ -178,8 +178,8 @@ static s64 LastIndexOf(string String, string Sub)
|
||||||
if(String.Count >= Sub.Count)
|
if(String.Count >= Sub.Count)
|
||||||
{
|
{
|
||||||
for(s64 Index = String.Count - Sub.Count - 1;
|
for(s64 Index = String.Count - Sub.Count - 1;
|
||||||
Index >= 0;
|
Index >= 0;
|
||||||
--Index)
|
--Index)
|
||||||
{
|
{
|
||||||
string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count));
|
string ToCheck = Substring(String, Range1S64(Index, Index + Sub.Count));
|
||||||
if(AreEqual(ToCheck, Sub))
|
if(AreEqual(ToCheck, Sub))
|
||||||
|
@ -287,8 +287,8 @@ static string ConvertS64ToString(arena *Arena, s64 Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < DigitCount;
|
Index < DigitCount;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
String.Data[TotalBufferCount - 1 - Index] = '0' + (Value % 10);
|
String.Data[TotalBufferCount - 1 - Index] = '0' + (Value % 10);
|
||||||
Value /= 10;
|
Value /= 10;
|
||||||
|
@ -395,13 +395,13 @@ static string JoinStringList(string_list *List, arena *Arena)
|
||||||
s64 GlobalIndex = 0;
|
s64 GlobalIndex = 0;
|
||||||
|
|
||||||
for(string_node *Node = List->First;
|
for(string_node *Node = List->First;
|
||||||
Node != 0;
|
Node != 0;
|
||||||
Node = Node->Next)
|
Node = Node->Next)
|
||||||
{
|
{
|
||||||
string String = Node->String;
|
string String = Node->String;
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < String.Count;
|
Index < String.Count;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
Buffer[GlobalIndex++] = String.Data[Index];
|
Buffer[GlobalIndex++] = String.Data[Index];
|
||||||
}
|
}
|
||||||
|
@ -411,118 +411,6 @@ static string JoinStringList(string_list *List, arena *Arena)
|
||||||
return(Result);
|
return(Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////
|
|
||||||
//~ sixten: String Chunk Functions
|
|
||||||
static string_chunk_list MakeStringChunkList(s64 ChunkSize)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//~ sixten: Unicode
|
//~ sixten: Unicode
|
||||||
|
@ -621,7 +509,7 @@ static string_decode DecodeUTF16Codepoint(u16 *Data, s64 Count)
|
||||||
else if(Count >= 2)
|
else if(Count >= 2)
|
||||||
{
|
{
|
||||||
if(0xD800 <= Data[0] && Data[0] < 0xDC00 &&
|
if(0xD800 <= Data[0] && Data[0] < 0xDC00 &&
|
||||||
0xDC00 <= Data[1] && Data[1] < 0xE000)
|
0xDC00 <= Data[1] && Data[1] < 0xE000)
|
||||||
{
|
{
|
||||||
Result.Codepoint = ((Data[0] - 0xD800)<<10)|(Data[1]-0xDC00);
|
Result.Codepoint = ((Data[0] - 0xD800)<<10)|(Data[1]-0xDC00);
|
||||||
Result.Size = 2;
|
Result.Size = 2;
|
||||||
|
@ -768,8 +656,8 @@ static text_point TextPointFromOffset(string String, s64 Offset)
|
||||||
{
|
{
|
||||||
text_point Point = {1, 1};
|
text_point Point = {1, 1};
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < String.Count && Index < Offset;
|
Index < String.Count && Index < Offset;
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
if(String.Data[Index] == '\n')
|
if(String.Data[Index] == '\n')
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,20 +35,6 @@ struct string_list
|
||||||
s64 TotalCount;
|
s64 TotalCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////
|
|
||||||
//~ sixten: String Chunk Types
|
|
||||||
struct string_chunk_list
|
|
||||||
{
|
|
||||||
s64 ChunkSize;
|
|
||||||
s64 ChunkCount;
|
|
||||||
s64 TotalCount;
|
|
||||||
|
|
||||||
string_node *First;
|
|
||||||
string_node *Last;
|
|
||||||
string_node *FirstFree;
|
|
||||||
string_node *LastFree;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//~ sixten: Char funcitons
|
//~ sixten: Char funcitons
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#define PLATFORM_COMMIT(name) void name(void *Pointer, u64 Size)
|
#define PLATFORM_COMMIT(name) void name(void *Pointer, u64 Size)
|
||||||
#define PLATFORM_DECOMMIT(name) void name(void *Pointer, u64 Size)
|
#define PLATFORM_DECOMMIT(name) void name(void *Pointer, u64 Size)
|
||||||
#define PLATFORM_ALLOCATE(name) void * name(u64 Size)
|
#define PLATFORM_ALLOCATE(name) void * name(u64 Size)
|
||||||
#define PLATFORM_DEALLOCATE(name) void name(void *Pointer)
|
#define PLATFORM_DEALLOCATE(name) void name(void *Pointer, u64 Size)
|
||||||
#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess)
|
#define PLATFORM_OPEN_FILE(name) platform_file_handle name(string Path, platform_access_flags FileAccess)
|
||||||
#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle)
|
#define PLATFORM_CLOSE_FILE(name) void name(platform_file_handle Handle)
|
||||||
#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size)
|
#define PLATFORM_READ_FILE(name) void name(platform_file_handle Handle, void *Dest, u64 Offset, u64 Size)
|
||||||
|
|
27
code/vn.cpp
27
code/vn.cpp
|
@ -98,7 +98,7 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
||||||
arena *Arena = ArenaAlloc(Kilobytes(24), true);
|
arena *Arena = ArenaAlloc(Kilobytes(24), true);
|
||||||
State = Memory->State = PushStruct(Arena, vn_state);
|
State = Memory->State = PushStruct(Arena, vn_state);
|
||||||
State->Arena = Arena;
|
State->Arena = Arena;
|
||||||
State->FrameArena = ArenaAlloc(Megabytes(1), true);
|
State->FrameArena = ArenaAlloc(Kilobytes(1), true);
|
||||||
|
|
||||||
State->GlyphAtlas = CreateGlyphAtlas(RenderCommands);
|
State->GlyphAtlas = CreateGlyphAtlas(RenderCommands);
|
||||||
State->Config = CreateConfig();
|
State->Config = CreateConfig();
|
||||||
|
@ -193,8 +193,8 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
||||||
|
|
||||||
//- sixten: consume all remaining evetns
|
//- sixten: consume all remaining evetns
|
||||||
for(platform_event *Event = Input->EventList->First;
|
for(platform_event *Event = Input->EventList->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
if(Event->Type == PlatformEvent_WindowClose)
|
if(Event->Type == PlatformEvent_WindowClose)
|
||||||
{
|
{
|
||||||
|
@ -213,17 +213,28 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
||||||
UI_RenderFrame(&Group, State->GlyphAtlas);
|
UI_RenderFrame(&Group, State->GlyphAtlas);
|
||||||
|
|
||||||
#if VN_INTERNAL
|
#if VN_INTERNAL
|
||||||
|
r32 DEBUGDisplayOffsetY = 20;
|
||||||
if(DEBUG_DebugSettings->ListHotAndActive)
|
if(DEBUG_DebugSettings->ListHotAndActive)
|
||||||
{
|
{
|
||||||
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - 20), 15, Color_Grey,
|
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
|
||||||
PushFormat(State->UI.FrameArena, "Hot: %S:%llu", UI_BoxStringFromKey(UI_HotKey()), UI_HotKey()));
|
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,
|
DEBUGDisplayOffsetY += 20;
|
||||||
PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_BoxStringFromKey(UI_ActiveKey()), UI_ActiveKey()));
|
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
|
||||||
|
PushFormat(State->UI.FrameArena, "Active: %S:%llu", UI_BoxStringFromKey(UI_ActiveKey()), UI_ActiveKey()));
|
||||||
|
DEBUGDisplayOffsetY += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(DEBUG_DebugSettings->RenderFPSCounter)
|
||||||
|
{
|
||||||
|
PushText(&Group, State->GlyphAtlas, Font_Regular, V2(5, RenderCommands->RenderDim.y - DEBUGDisplayOffsetY), 15, Color_Grey,
|
||||||
|
PushFormat(State->UI.FrameArena, "FPS: %.2f", 1.0f/Input->dtForFrame));
|
||||||
|
DEBUGDisplayOffsetY += 20;
|
||||||
|
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if VN_INTERNAL
|
#if VN_INTERNAL
|
||||||
DI_EndFrame();
|
DI_EndFrame();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,20 +26,20 @@ static void RasterizeGlyph(glyph_atlas *Atlas, font_id Font, glyph *Glyph, u32 C
|
||||||
s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize;
|
s32 GlyphsPerRow = Atlas->BitmapSize / Atlas->GlyphSize;
|
||||||
|
|
||||||
v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize,
|
v2_s32 BaseTextureOffset = V2S32((InternalIndex % GlyphsPerRow)*Atlas->GlyphSize,
|
||||||
(InternalIndex / GlyphsPerRow)*Atlas->GlyphSize);
|
(InternalIndex / GlyphsPerRow)*Atlas->GlyphSize);
|
||||||
|
|
||||||
int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint);
|
int GlyphIndex = stbtt_FindGlyphIndex(Info, Codepoint);
|
||||||
|
|
||||||
stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale,
|
stbtt_GetGlyphBitmapBoxSubpixel(Info, GlyphIndex, Scale, Scale,
|
||||||
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
||||||
&Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y);
|
&Glyph->P0.x, &Glyph->P0.y, &Glyph->P1.x, &Glyph->P1.y);
|
||||||
|
|
||||||
Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize);
|
Fill(Atlas->BitmapBuffer, 0, Atlas->GlyphSize*Atlas->GlyphSize);
|
||||||
stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer,
|
stbtt_MakeGlyphBitmapSubpixel(Info, Atlas->BitmapBuffer,
|
||||||
Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize,
|
Atlas->GlyphSize, Atlas->GlyphSize, Atlas->GlyphSize,
|
||||||
Scale, Scale,
|
Scale, Scale,
|
||||||
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
(r32)Subpixel/GLYPH_SUBPIXEL_SEGMENTS, 0,
|
||||||
GlyphIndex);
|
GlyphIndex);
|
||||||
|
|
||||||
s32 Advance, LeftSideBearing;
|
s32 Advance, LeftSideBearing;
|
||||||
stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing);
|
stbtt_GetGlyphHMetrics(Info, GlyphIndex, &Advance, &LeftSideBearing);
|
||||||
|
@ -54,8 +54,8 @@ static void RasterizeGlyph(glyph_atlas *Atlas, font_id Font, glyph *Glyph, u32 C
|
||||||
Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2);
|
Glyph->P1 = BaseTextureOffset + Dim + V2S32(2, 2);
|
||||||
|
|
||||||
Atlas->RenderCommands->FillRegion(Atlas->Texture,
|
Atlas->RenderCommands->FillRegion(Atlas->Texture,
|
||||||
BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize),
|
BaseTextureOffset, V2S32(Atlas->GlyphSize, Atlas->GlyphSize),
|
||||||
Atlas->BitmapBuffer);
|
Atlas->BitmapBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static glyph *GetGlyph(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size, s32 Subpixel)
|
static glyph *GetGlyph(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size, s32 Subpixel)
|
||||||
|
@ -63,8 +63,8 @@ static glyph *GetGlyph(glyph_atlas *Atlas, font_id Font, u32 Codepoint, r32 Size
|
||||||
glyph *Glyph = 0;
|
glyph *Glyph = 0;
|
||||||
|
|
||||||
for(s32 GlyphIndex = 0;
|
for(s32 GlyphIndex = 0;
|
||||||
GlyphIndex < Atlas->GlyphsUsed;
|
GlyphIndex < Atlas->GlyphsUsed;
|
||||||
++GlyphIndex)
|
++GlyphIndex)
|
||||||
{
|
{
|
||||||
glyph *At = Atlas->Glyphs + GlyphIndex;
|
glyph *At = Atlas->Glyphs + GlyphIndex;
|
||||||
if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel))
|
if((At->Font == Font) && (At->Codepoint == Codepoint) && (At->Size == Size) && (At->Subpixel == Subpixel))
|
||||||
|
@ -125,13 +125,13 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands,
|
||||||
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/icons.ttf"));
|
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("data/fonts/icons.ttf"));
|
||||||
|
|
||||||
for(s32 FontIndex = 0;
|
for(s32 FontIndex = 0;
|
||||||
FontIndex < Font_Count;
|
FontIndex < Font_Count;
|
||||||
++FontIndex)
|
++FontIndex)
|
||||||
{
|
{
|
||||||
loaded_font *Font = Atlas->Fonts + FontIndex;
|
loaded_font *Font = Atlas->Fonts + FontIndex;
|
||||||
stbtt_InitFont(&Font->Info,
|
stbtt_InitFont(&Font->Info,
|
||||||
Font->Data.Data,
|
Font->Data.Data,
|
||||||
stbtt_GetFontOffsetForIndex(Font->Data.Data, 0));
|
stbtt_GetFontOffsetForIndex(Font->Data.Data, 0));
|
||||||
|
|
||||||
stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap);
|
stbtt_GetFontVMetrics(&Font->Info, &Font->Ascent, &Font->Descent, &Font->LineGap);
|
||||||
}
|
}
|
||||||
|
@ -163,9 +163,9 @@ static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
||||||
v2 Dim = RenderDim*(1.0 / Font_Oversample);
|
v2 Dim = RenderDim*(1.0 / Font_Oversample);
|
||||||
|
|
||||||
PushTexturedQuad(Group,
|
PushTexturedQuad(Group,
|
||||||
Range2R32(GlyphP, GlyphP+Dim),
|
Range2R32(GlyphP, GlyphP+Dim),
|
||||||
Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)),
|
Range2R32(ConvertV2ToR32(Glyph->P0), ConvertV2ToR32(Glyph->P1)),
|
||||||
Color, Color, Color, Color, 0, 0, 0, Atlas->Texture);
|
Color, Color, Color, Color, 0, 0, 0, Atlas->Texture);
|
||||||
|
|
||||||
OffsetX += Glyph->Advance/Font_Oversample;
|
OffsetX += Glyph->Advance/Font_Oversample;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ enum platform_key
|
||||||
|
|
||||||
Key_PageUp, Key_PageDown,
|
Key_PageUp, Key_PageDown,
|
||||||
Key_Home, Key_End,
|
Key_Home, Key_End,
|
||||||
|
Key_Plus, Key_Minus,
|
||||||
|
|
||||||
Key_Backspace, Key_Delete,
|
Key_Backspace, Key_Delete,
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
@table(Name, NameLower, NameCaps, Type, Arguments) platform_functions:
|
@table(Name, NameLower, NameCaps, Type, Arguments) platform_functions:
|
||||||
{
|
{
|
||||||
{ Reserve reserve RESERVE `void *` `u64 Size` }
|
{ Reserve reserve RESERVE `void *` `u64 Size` }
|
||||||
{ Release release RELEASE `void` `void *Pointer` }
|
{ Release release RELEASE `void` `void *Pointer` }
|
||||||
{ Commit commit COMMIT `void` `void *Pointer, u64 Size` }
|
{ Commit commit COMMIT `void` `void *Pointer, u64 Size` }
|
||||||
{ Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` }
|
{ Decommit decommit DECOMMIT `void` `void *Pointer, u64 Size` }
|
||||||
{ Allocate allocate ALLOCATE `void *` `u64 Size` }
|
{ Allocate allocate ALLOCATE `void *` `u64 Size` }
|
||||||
{ Deallocate deallocate DEALLOCATE `void` `void *Pointer` }
|
{ Deallocate deallocate DEALLOCATE `void` `void *Pointer, u64 Size` }
|
||||||
{ OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` }
|
{ OpenFile open_file OPEN_FILE `platform_file_handle` `string Path, platform_access_flags FileAccess` }
|
||||||
{ CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` }
|
{ CloseFile close_file CLOSE_FILE `void` `platform_file_handle Handle` }
|
||||||
{ ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` }
|
{ ReadFile read_file READ_FILE `void` `platform_file_handle Handle, void *Dest, u64 Offset, u64 Size` }
|
||||||
{ WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` }
|
{ WriteFile write_file WRITE_FILE `void` `platform_file_handle Handle, void *Source, u64 Offset, u64 Size` }
|
||||||
{ GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` }
|
{ GetFileSize get_file_size GET_FILE_SIZE `u64` `platform_file_handle Handle` }
|
||||||
{ SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` }
|
{ SetCursor set_cursor SET_CURSOR `void` `platform_cursor Cursor` }
|
||||||
{ ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` }
|
{ ToggleFullscreen toggle_fullscreen TOGGLE_FULLSCREEN `void` `void` }
|
||||||
{ ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` }
|
{ ShowMessage show_message SHOW_MESSAGE `void` `string Message, platform_message_type Type` }
|
||||||
{ BeginFileIter begin_file_iter BEGIN_FILE_ITER `platform_file_iter *` `arena *Arena, string Path` }
|
{ BeginFileIter begin_file_iter BEGIN_FILE_ITER `platform_file_iter *` `arena *Arena, string Path` }
|
||||||
{ AdvanceFileIter advance_file_iter ADVANCE_FILE_ITER `b32` `arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo` }
|
{ AdvanceFileIter advance_file_iter ADVANCE_FILE_ITER `b32` `arena *Arena, platform_file_iter *Iter, platform_file_info *OutInfo` }
|
||||||
{ EndFileIter end_file_iter END_FILE_ITER `void` `platform_file_iter *Iter` }
|
{ EndFileIter end_file_iter END_FILE_ITER `void` `platform_file_iter *Iter` }
|
||||||
{ SetClipboard set_clipboard SET_CLIPBOARD `void` `string String` }
|
{ SetClipboard set_clipboard SET_CLIPBOARD `void` `string String` }
|
||||||
{ GetClipboard get_clipboard GET_CLIPBOARD `string` `arena *Arena` }
|
{ GetClipboard get_clipboard GET_CLIPBOARD `string` `arena *Arena` }
|
||||||
}
|
}
|
||||||
|
|
||||||
@table_gen
|
@table_gen
|
||||||
{
|
{
|
||||||
@expand(platform_functions s) `#define PLATFORM_$(s.NameCaps)(name) $(s.Type) name($(s.Arguments))`
|
@expand(platform_functions s) `#define PLATFORM_$(s.NameCaps)(name) $(s.Type) name($(s.Arguments))`
|
||||||
}
|
}
|
||||||
|
|
||||||
@table_gen
|
@table_gen
|
||||||
{
|
{
|
||||||
@expand(platform_functions s) `typedef PLATFORM_$(s.NameCaps)(platform_$(s.NameLower));`
|
@expand(platform_functions s) `typedef PLATFORM_$(s.NameCaps)(platform_$(s.NameLower));`
|
||||||
}
|
}
|
||||||
|
|
||||||
@table_gen
|
@table_gen
|
||||||
{
|
{
|
||||||
`struct platform_api`
|
`struct platform_api`
|
||||||
`{`
|
`{`
|
||||||
@expand(platform_functions s) `platform_$(s.NameLower) *$(s.Name); `
|
@expand(platform_functions s) `platform_$(s.NameLower) *$(s.Name); `
|
||||||
`};`
|
`};`
|
||||||
}
|
}
|
||||||
|
|
||||||
@table_gen
|
@table_gen
|
||||||
{
|
{
|
||||||
`#define RegisterPlatformFunctions(PlatformName)\\`
|
`#define RegisterPlatformFunctions(PlatformName)\\`
|
||||||
@expand(platform_functions s) `Platform.$(s.Name) = PlatformName##_$(s.Name);\\`
|
@expand(platform_functions s) `Platform.$(s.Name) = PlatformName##_$(s.Name);\\`
|
||||||
}
|
}
|
|
@ -91,22 +91,6 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void S_AdvanceCompiler(scene_compiler *Compiler)
|
static void S_AdvanceCompiler(scene_compiler *Compiler)
|
||||||
{
|
{
|
||||||
Compiler->At += 1;
|
Compiler->At += 1;
|
||||||
|
@ -143,17 +127,6 @@ static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Tok
|
||||||
return(Result);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message)
|
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message)
|
||||||
{
|
{
|
||||||
token Token = Compiler->At[0];
|
token Token = Compiler->At[0];
|
||||||
|
@ -1002,7 +975,7 @@ static void S_ResetRuntime(scene_runtime *Runtime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32 AdvanceOnAwait)
|
static scene_runtime_result S_Run(scene_runtime *Runtime, arena *Arena, b32 AdvanceOnAwait)
|
||||||
{
|
{
|
||||||
scene_runtime_result Result = {};
|
scene_runtime_result Result = {};
|
||||||
compiled_scene *Compiled = &Runtime->Compiled;
|
compiled_scene *Compiled = &Runtime->Compiled;
|
||||||
|
@ -1194,7 +1167,7 @@ static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32
|
||||||
|
|
||||||
case S_Op_ClearDialog:
|
case S_Op_ClearDialog:
|
||||||
{
|
{
|
||||||
scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action);
|
scene_textbox_action *Action = PushStructNoClear(Arena, scene_textbox_action);
|
||||||
Action->Kind = S_TextboxActionKind_Set;
|
Action->Kind = S_TextboxActionKind_Set;
|
||||||
Action->String = StrLit("");
|
Action->String = StrLit("");
|
||||||
QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action);
|
QueuePush(Runtime->FirstTextboxAction, Runtime->LastTextboxAction, Action);
|
||||||
|
@ -1220,7 +1193,7 @@ static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32
|
||||||
}
|
}
|
||||||
|
|
||||||
// sixten: create & apply character action
|
// sixten: create & apply character action
|
||||||
scene_character_action *Action = PushStruct(FrameArena, scene_character_action);
|
scene_character_action *Action = PushStruct(Arena, scene_character_action);
|
||||||
Action->Target = Character;
|
Action->Target = Character;
|
||||||
|
|
||||||
if(State.Kind == S_ValueKind_Nil)
|
if(State.Kind == S_ValueKind_Nil)
|
||||||
|
@ -1236,7 +1209,7 @@ static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32
|
||||||
|
|
||||||
case S_Op_LineEntry:
|
case S_Op_LineEntry:
|
||||||
{
|
{
|
||||||
scene_textbox_action *Action = PushStructNoClear(FrameArena, scene_textbox_action);
|
scene_textbox_action *Action = PushStructNoClear(Arena, scene_textbox_action);
|
||||||
Action->Kind = S_TextboxActionKind_Append;
|
Action->Kind = S_TextboxActionKind_Append;
|
||||||
|
|
||||||
Runtime->IP += 1;
|
Runtime->IP += 1;
|
||||||
|
|
|
@ -393,7 +393,6 @@ static void S_EmitConstant(scene_compiler *Compiler, scene_value Value);
|
||||||
static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
|
static void S_SetEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
|
||||||
static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
|
static void S_PushEmissionTarget(scene_compiler *Compiler, scene_emission_target Target);
|
||||||
static void S_PopEmissionTarget(scene_compiler *Compiler);
|
static void S_PopEmissionTarget(scene_compiler *Compiler);
|
||||||
static scene_bytecode_chunk *S_FindBytecodeChunkByName(scene_compiler *Compiler, string Name);
|
|
||||||
|
|
||||||
inline scene_emission_target S_RawEmissionTarget(arena *Arena, scene_bytecode_bucket *Bucket)
|
inline scene_emission_target S_RawEmissionTarget(arena *Arena, scene_bytecode_bucket *Bucket)
|
||||||
{
|
{
|
||||||
|
@ -417,7 +416,6 @@ inline scene_emission_target S_NamedEmissionTarget(arena *Arena, scene_bytecode_
|
||||||
//- sixten: parsing helpers
|
//- sixten: parsing helpers
|
||||||
static void S_AdvanceCompiler(scene_compiler *Compiler);
|
static void S_AdvanceCompiler(scene_compiler *Compiler);
|
||||||
static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token);
|
static scene_parse_rule S_ParseRuleFromToken(scene_compiler *Compiler, token Token);
|
||||||
static b32 S_MatchToken(scene_compiler *Compiler, token Token, token_kind Kind);
|
|
||||||
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message);
|
static token S_ConsumeToken(scene_compiler *Compiler, token_kind Kind, char *Message);
|
||||||
|
|
||||||
//- sixten: parsing
|
//- sixten: parsing
|
||||||
|
@ -455,6 +453,6 @@ static compiled_scene S_CopyCompiledScene(arena *Arena, compiled_scene *Compiled
|
||||||
static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name);
|
static scene_proc *S_FindProcByName(compiled_scene *Compiled, string Name);
|
||||||
static scene_named_value *S_FindGlobalVariableByName(scene_runtime *Runtime, string Name, b32 AlwaysCreate = true);
|
static scene_named_value *S_FindGlobalVariableByName(scene_runtime *Runtime, string Name, b32 AlwaysCreate = true);
|
||||||
static void S_ResetRuntime(scene_runtime *Runtime);
|
static void S_ResetRuntime(scene_runtime *Runtime);
|
||||||
static scene_runtime_result S_Run(scene_runtime *Runtime, arena *FrameArena, b32 AdvanceOnAwait);
|
static scene_runtime_result S_Run(scene_runtime *Runtime, arena *Arena, b32 AdvanceOnAwait);
|
||||||
|
|
||||||
#endif //VN_SCENE_H
|
#endif //VN_SCENE_H
|
||||||
|
|
|
@ -482,26 +482,26 @@ static text_op TextOpFromAction(arena *Arena, string String,
|
||||||
Op.NewCursor = Op.NewMark = Op.Range.Min;
|
Op.NewCursor = Op.NewMark = Op.Range.Min;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Action->Codepoint != 0)
|
//- sixten: handle insertions
|
||||||
{
|
{
|
||||||
Op.ReplaceString = StringFromCodepoint(Arena, Action->Codepoint);
|
b32 InsertedSomething = false;
|
||||||
|
if(Action->Codepoint != 0)
|
||||||
|
{
|
||||||
|
Op.ReplaceString = StringFromCodepoint(Arena, Action->Codepoint);
|
||||||
|
InsertedSomething = true;
|
||||||
|
}
|
||||||
|
else if(Action->Flags & TextActionFlag_Paste)
|
||||||
|
{
|
||||||
|
Op.ReplaceString = RemoveAll(Arena, Platform.GetClipboard(Arena), '\r');;
|
||||||
|
InsertedSomething = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(State->Cursor == State->Mark)
|
if(InsertedSomething)
|
||||||
{
|
{
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
range1_s64 Selection = Range1S64(State->Cursor, State->Mark);
|
||||||
Op.Range = Range1S64(State->Cursor, State->Cursor);
|
Op.Range = Selection;
|
||||||
|
Op.NewCursor = Op.NewMark = Selection.Min+Op.ReplaceString.Count;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
|
||||||
Op.Range = Range1S64(State->Cursor, State->Mark);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(Action->Flags & TextActionFlag_Paste)
|
|
||||||
{
|
|
||||||
Op.ReplaceString = Platform.GetClipboard(Arena);
|
|
||||||
Op.Range = Range1S64(State->Cursor, State->Cursor);
|
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!(Action->Flags & TextActionFlag_KeepMark))
|
if(!(Action->Flags & TextActionFlag_KeepMark))
|
||||||
|
|
|
@ -121,12 +121,12 @@ static ui_signal UI_Button(string String)
|
||||||
{
|
{
|
||||||
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
||||||
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText|
|
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText|
|
||||||
UI_BoxFlag_DrawBackground|
|
UI_BoxFlag_DrawBackground|
|
||||||
UI_BoxFlag_DrawBorder|
|
UI_BoxFlag_DrawBorder|
|
||||||
UI_BoxFlag_HotAnimation|
|
UI_BoxFlag_HotAnimation|
|
||||||
UI_BoxFlag_ActiveAnimation|
|
UI_BoxFlag_ActiveAnimation|
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
String);
|
String);
|
||||||
ui_signal Signal = UI_SignalFromBox(Box);
|
ui_signal Signal = UI_SignalFromBox(Box);
|
||||||
return(Signal);
|
return(Signal);
|
||||||
}
|
}
|
||||||
|
@ -143,12 +143,12 @@ static ui_signal UI_ButtonF(char *Format, ...)
|
||||||
va_end(Arguments);
|
va_end(Arguments);
|
||||||
|
|
||||||
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText|
|
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText|
|
||||||
UI_BoxFlag_DrawBackground|
|
UI_BoxFlag_DrawBackground|
|
||||||
UI_BoxFlag_DrawBorder|
|
UI_BoxFlag_DrawBorder|
|
||||||
UI_BoxFlag_HotAnimation|
|
UI_BoxFlag_HotAnimation|
|
||||||
UI_BoxFlag_ActiveAnimation|
|
UI_BoxFlag_ActiveAnimation|
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
String);
|
String);
|
||||||
ui_signal Signal = UI_SignalFromBox(Box);
|
ui_signal Signal = UI_SignalFromBox(Box);
|
||||||
|
|
||||||
ReleaseScratch(Scratch);
|
ReleaseScratch(Scratch);
|
||||||
|
@ -200,24 +200,26 @@ static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset)
|
||||||
UI_SetNextFont(Font_Icons);
|
UI_SetNextFont(Font_Icons);
|
||||||
UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_UpDir:FontIcon_LeftDir);
|
UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_UpDir:FontIcon_LeftDir);
|
||||||
|
|
||||||
UI_Spacer(UI_Pixels(Offset, 1));
|
Offset = Clamp(Offset, 0, DimOfRange(UI_TopParent()->Rect).E[Axis]-Size-UI_TopFontSize()*2);
|
||||||
|
|
||||||
UI_SetNextCornerRadius(4.0f);
|
UI_Spacer(UI_Pixels(Offset, 1));
|
||||||
UI_SetNextAxisSize(Axis, UI_Pixels(Size, 1));
|
|
||||||
UI_SetNextAxisSize(Opposite(Axis), UI_Percent(1, 1));
|
|
||||||
|
|
||||||
Signal = UI_SignalFromBox(UI_MakeBox(UI_BoxFlag_DrawBorder |
|
UI_SetNextCornerRadius(4.0f);
|
||||||
UI_BoxFlag_DrawBackground |
|
UI_SetNextAxisSize(Axis, UI_Pixels(Size, 1));
|
||||||
UI_BoxFlag_Clickable |
|
UI_SetNextAxisSize(Opposite(Axis), UI_Percent(1, 1));
|
||||||
UI_BoxFlag_HotAnimation |
|
|
||||||
UI_BoxFlag_ActiveAnimation,
|
|
||||||
StrLit("Slider")));
|
|
||||||
|
|
||||||
UI_Spacer(UI_Percent(1, 0));
|
Signal = UI_SignalFromBox(UI_MakeBox(UI_BoxFlag_DrawBorder |
|
||||||
|
UI_BoxFlag_DrawBackground |
|
||||||
|
UI_BoxFlag_Clickable |
|
||||||
|
UI_BoxFlag_HotAnimation |
|
||||||
|
UI_BoxFlag_ActiveAnimation,
|
||||||
|
StrLit("Slider")));
|
||||||
|
|
||||||
UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1));
|
UI_Spacer(UI_Percent(1, 0));
|
||||||
UI_SetNextFont(Font_Icons);
|
|
||||||
UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_DownDir:FontIcon_RightDir);
|
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);
|
return(Signal);
|
||||||
|
@ -247,7 +249,7 @@ static void UI_ScrollBegin(r32 *X, r32 *Y, ui_box_flags Flags, string Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
UI_SetNextSize(AllowOnX?UI_ChildrenSum(1, 1):UI_Percent(1, 0),
|
UI_SetNextSize(AllowOnX?UI_ChildrenSum(1, 1):UI_Percent(1, 0),
|
||||||
AllowOnY?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_box *ScrollableBox = UI_MakeBox(ScrollFlags, StrLit("Scrollable Box"));
|
||||||
|
|
||||||
|
@ -300,8 +302,8 @@ static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags, string Name)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(platform_event *Event = UI_EventList()->First;
|
for(platform_event *Event = UI_EventList()->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.x != 0)
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.x != 0)
|
||||||
{
|
{
|
||||||
|
@ -351,8 +353,8 @@ static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags, string Name)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(platform_event *Event = UI_EventList()->First;
|
for(platform_event *Event = UI_EventList()->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0)
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0)
|
||||||
{
|
{
|
||||||
|
@ -394,12 +396,12 @@ static r32 UI_Slider(r32 Value, range1_r32 Range)
|
||||||
UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1));
|
UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1));
|
||||||
UI_SetNextFixedX((DimOfRange(Container->Rect).x-UI_TopFontSize())*(Value-Range.Min)/DimOfRange(Range));
|
UI_SetNextFixedX((DimOfRange(Container->Rect).x-UI_TopFontSize())*(Value-Range.Min)/DimOfRange(Range));
|
||||||
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground|
|
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground|
|
||||||
UI_BoxFlag_DrawBorder|
|
UI_BoxFlag_DrawBorder|
|
||||||
UI_BoxFlag_HotAnimation|
|
UI_BoxFlag_HotAnimation|
|
||||||
UI_BoxFlag_ActiveAnimation|
|
UI_BoxFlag_ActiveAnimation|
|
||||||
UI_BoxFlag_Clickable|
|
UI_BoxFlag_Clickable|
|
||||||
UI_BoxFlag_FloatingX|
|
UI_BoxFlag_FloatingX|
|
||||||
0, "Dragable");
|
0, "Dragable");
|
||||||
ui_signal Signal = UI_SignalFromBox(Box);
|
ui_signal Signal = UI_SignalFromBox(Box);
|
||||||
if(Signal.Dragging)
|
if(Signal.Dragging)
|
||||||
{
|
{
|
||||||
|
@ -426,12 +428,12 @@ static void UI_TooltipLabel(string Label, v2 P)
|
||||||
UI_CornerRadius(4);
|
UI_CornerRadius(4);
|
||||||
UI_SetNextFixedP(P+V2R32(15.0f, 0.0f));
|
UI_SetNextFixedP(P+V2R32(15.0f, 0.0f));
|
||||||
UI_MakeBox(UI_BoxFlag_DrawBorder |
|
UI_MakeBox(UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_DrawBackground |
|
UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawText |
|
UI_BoxFlag_DrawText |
|
||||||
UI_BoxFlag_DrawDropShadow |
|
UI_BoxFlag_DrawDropShadow |
|
||||||
UI_BoxFlag_FloatingX |
|
UI_BoxFlag_FloatingX |
|
||||||
UI_BoxFlag_FloatingY,
|
UI_BoxFlag_FloatingY,
|
||||||
Label);
|
Label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,14 +80,14 @@ static void W_ProcessKeyBinds()
|
||||||
platform_event_list *EventList = Workspace->EventList;
|
platform_event_list *EventList = Workspace->EventList;
|
||||||
|
|
||||||
for(platform_event *Event = EventList->First;
|
for(platform_event *Event = EventList->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
if(Event->Type == PlatformEvent_Press)
|
if(Event->Type == PlatformEvent_Press)
|
||||||
{
|
{
|
||||||
for(s32 KeybindIndex = 0;
|
for(s32 KeybindIndex = 0;
|
||||||
KeybindIndex < ArrayCount(Workspace_Keybinds);
|
KeybindIndex < ArrayCount(Workspace_Keybinds);
|
||||||
++KeybindIndex)
|
++KeybindIndex)
|
||||||
{
|
{
|
||||||
workspace_keybind *Keybind = Workspace_Keybinds + KeybindIndex;
|
workspace_keybind *Keybind = Workspace_Keybinds + KeybindIndex;
|
||||||
if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers))
|
if((Event->Key == Keybind->Key) && (Event->Modifiers == Keybind->Modifiers))
|
||||||
|
@ -111,12 +111,12 @@ static ui_signal W_BuildToolbarButton(char *Text, workspace_toolbar_menu Menu)
|
||||||
UI_SetNextBackgroundColor(ColorFromHex(0x252728FF));
|
UI_SetNextBackgroundColor(ColorFromHex(0x252728FF));
|
||||||
|
|
||||||
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawBorder |
|
UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_DrawText |
|
UI_BoxFlag_DrawText |
|
||||||
UI_BoxFlag_HotAnimation |
|
UI_BoxFlag_HotAnimation |
|
||||||
UI_BoxFlag_ActiveAnimation |
|
UI_BoxFlag_ActiveAnimation |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
Text);
|
Text);
|
||||||
|
|
||||||
ui_signal Signal = UI_SignalFromBox(Box);
|
ui_signal Signal = UI_SignalFromBox(Box);
|
||||||
|
|
||||||
|
@ -153,11 +153,11 @@ static ui_signal W_BuildMenuItem(u32 Icon, char *Text, char *Shortcut)
|
||||||
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
||||||
|
|
||||||
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawBorder |
|
UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_HotAnimation |
|
UI_BoxFlag_HotAnimation |
|
||||||
UI_BoxFlag_ActiveAnimation |
|
UI_BoxFlag_ActiveAnimation |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
"Menu Item %s", Text);
|
"Menu Item %s", Text);
|
||||||
|
|
||||||
UI_Parent(Box)
|
UI_Parent(Box)
|
||||||
{
|
{
|
||||||
|
@ -184,7 +184,7 @@ static void W_BuildToolbar(void)
|
||||||
UI_SetNextBackgroundColor(Theme_BackgroundColor);
|
UI_SetNextBackgroundColor(Theme_BackgroundColor);
|
||||||
|
|
||||||
ui_box *ToolbarBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder,
|
ui_box *ToolbarBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder,
|
||||||
"Workspace Toolbar");
|
"Workspace Toolbar");
|
||||||
|
|
||||||
UI_Parent(ToolbarBox)
|
UI_Parent(ToolbarBox)
|
||||||
{
|
{
|
||||||
|
@ -204,11 +204,11 @@ static void W_BuildToolbar(void)
|
||||||
UI_SetNextWidth(UI_Pixels(250, 1));
|
UI_SetNextWidth(UI_Pixels(250, 1));
|
||||||
UI_SetNextHeight(UI_ChildrenSum(MenuT, 1));
|
UI_SetNextHeight(UI_ChildrenSum(MenuT, 1));
|
||||||
ui_box *Dropdown = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
ui_box *Dropdown = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawDropShadow |
|
UI_BoxFlag_DrawDropShadow |
|
||||||
UI_BoxFlag_Clip |
|
UI_BoxFlag_Clip |
|
||||||
UI_BoxFlag_FloatingX |
|
UI_BoxFlag_FloatingX |
|
||||||
UI_BoxFlag_FloatingY,
|
UI_BoxFlag_FloatingY,
|
||||||
"Workspace Dropdown");
|
"Workspace Dropdown");
|
||||||
|
|
||||||
UI_Parent(Dropdown)
|
UI_Parent(Dropdown)
|
||||||
UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1))
|
UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1))
|
||||||
|
@ -300,40 +300,6 @@ static void W_DeletePanel(workspace_panel *Panel)
|
||||||
DLLInsertLast(Workspace->FirstFreePanel, Workspace->LastFreePanel, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void W_PanelViewRemove(workspace_panel_view_list *List, workspace_panel_view *PanelView)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void W_DeletePanelView(workspace_panel_view *PanelView)
|
|
||||||
{
|
|
||||||
workspace *Workspace = W_GetState();
|
|
||||||
ZeroStruct(PanelView);
|
|
||||||
W_PanelViewPush(&Workspace->PanelViewFreeList, PanelView);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void W_SplitPanel(workspace_panel *Panel, axis2 Axis)
|
static void W_SplitPanel(workspace_panel *Panel, axis2 Axis)
|
||||||
{
|
{
|
||||||
workspace *Workspace = W_GetState();
|
workspace *Workspace = W_GetState();
|
||||||
|
@ -357,8 +323,8 @@ static void W_SplitPanel(workspace_panel *Panel, axis2 Axis)
|
||||||
|
|
||||||
// sixten: Update the parents of the children.
|
// sixten: Update the parents of the children.
|
||||||
for(workspace_view *Child = NewPanel->FirstView;
|
for(workspace_view *Child = NewPanel->FirstView;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
{
|
{
|
||||||
Child->Parent = NewPanel;
|
Child->Parent = NewPanel;
|
||||||
}
|
}
|
||||||
|
@ -442,12 +408,12 @@ static void W_BuildTabItem(workspace_panel *Panel, workspace_view *View)
|
||||||
UI_SetNextCornerRadius(0.0);
|
UI_SetNextCornerRadius(0.0);
|
||||||
|
|
||||||
ui_box *TabBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
ui_box *TabBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawDropShadow |
|
UI_BoxFlag_DrawDropShadow |
|
||||||
UI_BoxFlag_HotAnimation |
|
UI_BoxFlag_HotAnimation |
|
||||||
UI_BoxFlag_ActiveAnimation |
|
UI_BoxFlag_ActiveAnimation |
|
||||||
UI_BoxFlag_Clip |
|
UI_BoxFlag_Clip |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
"Workspace Panel Tab Item %S#%p", Name, View);
|
"Workspace Panel Tab Item %S#%p", Name, View);
|
||||||
|
|
||||||
UI_Parent(TabBox) UI_Padding(UI_Pixels(5, 1))
|
UI_Parent(TabBox) UI_Padding(UI_Pixels(5, 1))
|
||||||
{
|
{
|
||||||
|
@ -506,8 +472,8 @@ static void W_BuildPanelHeader(workspace_panel *Panel)
|
||||||
UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "Workspace Panel Header"))
|
UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_Clip, "Workspace Panel Header"))
|
||||||
{
|
{
|
||||||
for(workspace_view *View = Panel->FirstView;
|
for(workspace_view *View = Panel->FirstView;
|
||||||
View != 0;
|
View != 0;
|
||||||
View = View->Next)
|
View = View->Next)
|
||||||
{
|
{
|
||||||
W_BuildTabItem(Panel, View);
|
W_BuildTabItem(Panel, View);
|
||||||
}
|
}
|
||||||
|
@ -523,10 +489,10 @@ static void W_BuildPanelHeader(workspace_panel *Panel)
|
||||||
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
||||||
|
|
||||||
ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_HotAnimation |
|
ui_box *CloseBox = UI_MakeBoxF(UI_BoxFlag_HotAnimation |
|
||||||
UI_BoxFlag_ActiveAnimation |
|
UI_BoxFlag_ActiveAnimation |
|
||||||
UI_BoxFlag_DrawText |
|
UI_BoxFlag_DrawText |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
"%U", FontIcon_Cancel);
|
"%U", FontIcon_Cancel);
|
||||||
|
|
||||||
ui_signal Signal = UI_SignalFromBox(CloseBox);
|
ui_signal Signal = UI_SignalFromBox(CloseBox);
|
||||||
if(Signal.Clicked)
|
if(Signal.Clicked)
|
||||||
|
@ -549,8 +515,8 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
{
|
{
|
||||||
r32 TotalOfParent = 0;
|
r32 TotalOfParent = 0;
|
||||||
for(workspace_panel *Child = Parent->First;
|
for(workspace_panel *Child = Parent->First;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
{
|
{
|
||||||
if(Child != Panel)
|
if(Child != Panel)
|
||||||
{
|
{
|
||||||
|
@ -579,10 +545,10 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
UI_SetNextBackgroundColor(Theme_BackgroundColor);
|
UI_SetNextBackgroundColor(Theme_BackgroundColor);
|
||||||
|
|
||||||
ui_box *BodyBox = UI_MakeBoxF(UI_BoxFlag_DrawBorder |
|
ui_box *BodyBox = UI_MakeBoxF(UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_DrawBackground |
|
UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_Clip |
|
UI_BoxFlag_Clip |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
"Workspace Panel Body");
|
"Workspace Panel Body");
|
||||||
UI_Parent(BodyBox)
|
UI_Parent(BodyBox)
|
||||||
{
|
{
|
||||||
if(Panel->FirstView)
|
if(Panel->FirstView)
|
||||||
|
@ -622,7 +588,7 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
b32 DragActive = W_GetDragPayload(&Payload);
|
b32 DragActive = W_GetDragPayload(&Payload);
|
||||||
|
|
||||||
b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) &&
|
b32 OverlayActive = (DragActive && (Payload.View->Parent != Panel) &&
|
||||||
InRange(BodyBox->Rect, UI_GetState()->MouseP));
|
InRange(BodyBox->Rect, UI_GetState()->MouseP));
|
||||||
|
|
||||||
if(OverlayActive && Workspace->DragPayloadState == W_DragPayload_Released)
|
if(OverlayActive && Workspace->DragPayloadState == W_DragPayload_Released)
|
||||||
{
|
{
|
||||||
|
@ -640,7 +606,7 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
{
|
{
|
||||||
workspace_panel *OldParent = View->Parent;
|
workspace_panel *OldParent = View->Parent;
|
||||||
b32 ViewWasCurrent = ((OldParent->CurrentView == View) &&
|
b32 ViewWasCurrent = ((OldParent->CurrentView == View) &&
|
||||||
(Workspace->CurrentPanel == OldParent));
|
(Workspace->CurrentPanel == OldParent));
|
||||||
|
|
||||||
// sixten: Detatch view
|
// sixten: Detatch view
|
||||||
{
|
{
|
||||||
|
@ -673,9 +639,9 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
|
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
|
||||||
|
|
||||||
UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_FloatingX |
|
UI_BoxFlag_FloatingX |
|
||||||
UI_BoxFlag_FloatingY,
|
UI_BoxFlag_FloatingY,
|
||||||
"Workspace Panel Drag Hover");
|
"Workspace Panel Drag Hover");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,8 +663,8 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
{
|
{
|
||||||
s32 ChildCount = 0;
|
s32 ChildCount = 0;
|
||||||
for(workspace_panel *Child = Panel->First;
|
for(workspace_panel *Child = Panel->First;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
{
|
{
|
||||||
++ChildCount;
|
++ChildCount;
|
||||||
}
|
}
|
||||||
|
@ -711,8 +677,8 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
r32 SizeScalar = 1.0 - PercentPaddedSpace;
|
r32 SizeScalar = 1.0 - PercentPaddedSpace;
|
||||||
|
|
||||||
for(workspace_panel *Child = Panel->First;
|
for(workspace_panel *Child = Panel->First;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
{
|
{
|
||||||
UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0));
|
UI_SetNextAxisSize(Panel->SplitAxis, UI_Percent(Child->PercentOfParent*SizeScalar, 0));
|
||||||
UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0));
|
UI_SetNextAxisSize(Opposite(Panel->SplitAxis), UI_Percent(1, 0));
|
||||||
|
@ -729,8 +695,8 @@ static void W_BuildPanel(workspace_panel *Panel)
|
||||||
if(Signal.Hovering || Signal.Dragging)
|
if(Signal.Hovering || Signal.Dragging)
|
||||||
{
|
{
|
||||||
Platform.SetCursor((Panel->SplitAxis == Axis2_X) ?
|
Platform.SetCursor((Panel->SplitAxis == Axis2_X) ?
|
||||||
PlatformCursor_ArrowHorizontal :
|
PlatformCursor_ArrowHorizontal :
|
||||||
PlatformCursor_ArrowVertical);
|
PlatformCursor_ArrowVertical);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Signal.Dragging)
|
if(Signal.Dragging)
|
||||||
|
|
|
@ -103,9 +103,6 @@ struct workspace
|
||||||
workspace_drag_payload_state DragPayloadState;
|
workspace_drag_payload_state DragPayloadState;
|
||||||
workspace_drag_payload DragPayload;
|
workspace_drag_payload DragPayload;
|
||||||
|
|
||||||
arena *PanelViewArena;
|
|
||||||
workspace_panel_view_list PanelViewFreeList;
|
|
||||||
|
|
||||||
workspace_toolbar_menu Menu;
|
workspace_toolbar_menu Menu;
|
||||||
v2 MenuP;
|
v2 MenuP;
|
||||||
r32 MenuT;
|
r32 MenuT;
|
||||||
|
@ -138,12 +135,6 @@ static void W_SplitPanel(workspace_panel *Panel, axis2 Axis);
|
||||||
//- sixten: Views
|
//- sixten: Views
|
||||||
static b32 W_ViewIsDragged(workspace_view *View);
|
static b32 W_ViewIsDragged(workspace_view *View);
|
||||||
|
|
||||||
//- sixten: Panel Views
|
|
||||||
static void W_PanelViewPush(workspace_panel_view_list *List, workspace_panel_view *PanelView);
|
|
||||||
static void W_PanelViewRemove(workspace_panel_view_list *List, workspace_panel_view *PanelView);
|
|
||||||
static workspace_panel_view *W_CreatePanelView(void);
|
|
||||||
static void W_DeletePanelView(workspace_panel_view *PanelView);
|
|
||||||
|
|
||||||
//- sixten: Builder code
|
//- sixten: Builder code
|
||||||
static ui_signal W_BuildToolbarButton(char *Text, workspace_toolbar_menu Menu);
|
static ui_signal W_BuildToolbarButton(char *Text, workspace_toolbar_menu Menu);
|
||||||
static ui_signal W_BuildMenuItem(u32 Icon, char *Text, char *Shortcut);
|
static ui_signal W_BuildMenuItem(u32 Icon, char *Text, char *Shortcut);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
struct mutable_string
|
struct mutable_string
|
||||||
{
|
{
|
||||||
arena *Arena;
|
arena *Arena;
|
||||||
string String;
|
string String;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -17,22 +17,22 @@ struct mutable_string
|
||||||
|
|
||||||
struct history_entry
|
struct history_entry
|
||||||
{
|
{
|
||||||
range1_s64 Range;
|
range1_s64 Range;
|
||||||
string ReplaceString;
|
string ReplaceString;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct history_node
|
struct history_node
|
||||||
{
|
{
|
||||||
history_node *Next;
|
history_node *Next;
|
||||||
history_node *Prev;
|
history_node *Prev;
|
||||||
history_entry Forward;
|
history_entry Forward;
|
||||||
history_entry Backward;
|
history_entry Backward;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct history_list
|
struct history_list
|
||||||
{
|
{
|
||||||
history_node *At;
|
history_node *At;
|
||||||
history_node Sentinel;
|
history_node Sentinel;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -40,42 +40,44 @@ struct history_list
|
||||||
|
|
||||||
struct workspace_text_data
|
struct workspace_text_data
|
||||||
{
|
{
|
||||||
token_array Tokens;
|
token_array Tokens;
|
||||||
range1_s64_array Lines;
|
range1_s64_array Lines;
|
||||||
|
s64 MaxLineCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct workspace_view_text_editor
|
struct workspace_view_text_editor
|
||||||
{
|
{
|
||||||
// sixten: processed text
|
// sixten: processed text
|
||||||
arena *ProcessingArena;
|
arena *ProcessingArena;
|
||||||
token_array Tokens;
|
token_array Tokens;
|
||||||
range1_s64_array Lines;
|
range1_s64_array Lines;
|
||||||
compiled_scene Compiled;
|
s64 MaxLineCount;
|
||||||
|
compiled_scene Compiled;
|
||||||
|
|
||||||
// sixten: text being edited
|
// sixten: text being edited
|
||||||
string FileName;
|
string FileName;
|
||||||
string FilePath;
|
string FilePath;
|
||||||
mutable_string Text;
|
mutable_string Text;
|
||||||
|
|
||||||
// sixten: text editing
|
// sixten: text editing
|
||||||
text_edit_state EditState;
|
text_edit_state EditState;
|
||||||
text_point LastTextPoint;
|
text_point LastTextPoint;
|
||||||
|
|
||||||
// sixten: text rendering
|
// sixten: text rendering
|
||||||
r32 FontSize;
|
r32 FontSize;
|
||||||
|
|
||||||
// sixten: history
|
// sixten: history
|
||||||
arena *HistoryArena;
|
arena *HistoryArena;
|
||||||
history_list History;
|
history_list History;
|
||||||
history_node *SavePoint;
|
history_node *SavePoint;
|
||||||
|
|
||||||
// sixten: ui building & rendering
|
// sixten: ui building & rendering
|
||||||
ui_box *ContainerBox;
|
ui_box *ContainerBox;
|
||||||
v2 TextDim;
|
v2 TextDim;
|
||||||
v2 Offset;
|
v2 Offset;
|
||||||
b32 DropdownActive;
|
b32 DropdownActive;
|
||||||
v2 DropdownP;
|
v2 DropdownP;
|
||||||
r32 DropdownTransition;
|
r32 DropdownTransition;
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
|
@ -39,6 +39,8 @@ inline workspace_view *W_CreateNewView(workspace_view_kind Kind, workspace_panel
|
||||||
workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String);
|
workspace_text_data TextData = W_TextDataFromString(Editor->ProcessingArena, Editor->Text.String);
|
||||||
Editor->Tokens = TextData.Tokens;
|
Editor->Tokens = TextData.Tokens;
|
||||||
Editor->Lines = TextData.Lines;
|
Editor->Lines = TextData.Lines;
|
||||||
|
|
||||||
|
Editor->FontSize = 13.0f;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case W_ViewKind_NavEditor:
|
case W_ViewKind_NavEditor:
|
||||||
|
@ -194,8 +196,8 @@ static void W_BuildSettingsTabButton(workspace_view_settings *Settings, char *Na
|
||||||
UI_SetNextTextColor(Color);
|
UI_SetNextTextColor(Color);
|
||||||
|
|
||||||
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawText |
|
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawText |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
Name);
|
Name);
|
||||||
|
|
||||||
ui_signal Signal = UI_SignalFromBox(Box);
|
ui_signal Signal = UI_SignalFromBox(Box);
|
||||||
if(Signal.Hovering)
|
if(Signal.Hovering)
|
||||||
|
@ -215,11 +217,11 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 *
|
||||||
|
|
||||||
UI_SetNextLayoutAxis(Axis2_X);
|
UI_SetNextLayoutAxis(Axis2_X);
|
||||||
ui_box *DropdownBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
ui_box *DropdownBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawBorder |
|
UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_HotAnimation |
|
UI_BoxFlag_HotAnimation |
|
||||||
UI_BoxFlag_ActiveAnimation |
|
UI_BoxFlag_ActiveAnimation |
|
||||||
UI_BoxFlag_Clickable,
|
UI_BoxFlag_Clickable,
|
||||||
"Dropdown");
|
"Dropdown");
|
||||||
UI_Parent(DropdownBox)
|
UI_Parent(DropdownBox)
|
||||||
{
|
{
|
||||||
UI_Width(UI_Percent(1, 0)) UI_LabelF(Alternatives[*Selected]);
|
UI_Width(UI_Percent(1, 0)) UI_LabelF(Alternatives[*Selected]);
|
||||||
|
@ -249,15 +251,15 @@ static b32 UI_DropdownSelection(char **Alternatives, s32 AlternativeCount, b32 *
|
||||||
UI_SetNextWidth(UI_Pixels(200, 1));
|
UI_SetNextWidth(UI_Pixels(200, 1));
|
||||||
UI_SetNextHeight(UI_ChildrenSum(OpenTransition, 1));
|
UI_SetNextHeight(UI_ChildrenSum(OpenTransition, 1));
|
||||||
UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip |
|
UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip |
|
||||||
UI_BoxFlag_DrawDropShadow |
|
UI_BoxFlag_DrawDropShadow |
|
||||||
UI_BoxFlag_FloatingX |
|
UI_BoxFlag_FloatingX |
|
||||||
UI_BoxFlag_FloatingY, "Dropdown Contents %p%p", Alternatives, Open))
|
UI_BoxFlag_FloatingY, "Dropdown Contents %p%p", Alternatives, Open))
|
||||||
{
|
{
|
||||||
UI_PushWidth(UI_Percent(1, 1));
|
UI_PushWidth(UI_Percent(1, 1));
|
||||||
|
|
||||||
for(s64 Index = 0;
|
for(s64 Index = 0;
|
||||||
Index < Round(AlternativeCount*OpenTransition);
|
Index < Round(AlternativeCount*OpenTransition);
|
||||||
++Index)
|
++Index)
|
||||||
{
|
{
|
||||||
ui_signal ButtonSignal = UI_ButtonF(Alternatives[Index]);
|
ui_signal ButtonSignal = UI_ButtonF(Alternatives[Index]);
|
||||||
if(AreEqual(UI_ActiveKey(), ButtonSignal.Box->Key))
|
if(AreEqual(UI_ActiveKey(), ButtonSignal.Box->Key))
|
||||||
|
@ -316,8 +318,8 @@ static void W_BuildImageViewer(workspace_view *View)
|
||||||
if(AreEqual(UI_HotKey(), ImageBox->Key))
|
if(AreEqual(UI_HotKey(), ImageBox->Key))
|
||||||
{
|
{
|
||||||
for(platform_event *Event = UI_EventList()->First;
|
for(platform_event *Event = UI_EventList()->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0)
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0)
|
||||||
{
|
{
|
||||||
|
@ -426,7 +428,7 @@ static void W_BuildSettings(workspace_view *View)
|
||||||
UI_SetNextWidth(UI_Pixels(200, 1));
|
UI_SetNextWidth(UI_Pixels(200, 1));
|
||||||
UI_SetNextCornerRadius(4);
|
UI_SetNextCornerRadius(4);
|
||||||
if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives),
|
if(UI_DropdownSelection(Alternatives, ArrayCount(Alternatives),
|
||||||
&Settings->GeneralDropdownOpen, &DropdownSelected))
|
&Settings->GeneralDropdownOpen, &DropdownSelected))
|
||||||
{
|
{
|
||||||
Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected];
|
Workspace->Input->RefreshRate = AlternativeMapping[DropdownSelected];
|
||||||
Settings->GeneralDropdownOpen = false;
|
Settings->GeneralDropdownOpen = false;
|
||||||
|
@ -463,10 +465,10 @@ static void W_BuildView(workspace_view *View)
|
||||||
UI_SetNextCornerRadius(3);
|
UI_SetNextCornerRadius(3);
|
||||||
|
|
||||||
ui_box *ViewBox = UI_MakeBoxF(UI_BoxFlag_Clickable |
|
ui_box *ViewBox = UI_MakeBoxF(UI_BoxFlag_Clickable |
|
||||||
UI_BoxFlag_DrawBackground |
|
UI_BoxFlag_DrawBackground |
|
||||||
UI_BoxFlag_DrawBorder |
|
UI_BoxFlag_DrawBorder |
|
||||||
UI_BoxFlag_Clip,
|
UI_BoxFlag_Clip,
|
||||||
"Workspace View %p", View);
|
"Workspace View %p", View);
|
||||||
|
|
||||||
UI_Parent(ViewBox)
|
UI_Parent(ViewBox)
|
||||||
UI_Size(UI_Percent(1, 0), UI_Percent(1, 0))
|
UI_Size(UI_Percent(1, 0), UI_Percent(1, 0))
|
||||||
|
|
Binary file not shown.
|
@ -344,16 +344,16 @@ static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen)
|
||||||
{
|
{
|
||||||
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
||||||
if(GetWindowPlacement(Window, &Global_WindowPosition) &&
|
if(GetWindowPlacement(Window, &Global_WindowPosition) &&
|
||||||
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo))
|
GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &MonitorInfo))
|
||||||
{
|
{
|
||||||
// sixten(NOTE): This doesn't work when the window is maximized. One wordaround would be to set the
|
// 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.
|
// window to "normal" size using ShowWindow(Window, SW_SHOWNORMAL) but it looks *very* scuffed.
|
||||||
SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW);
|
SetWindowLong(Window, GWL_STYLE, Style & ~WS_OVERLAPPEDWINDOW);
|
||||||
SetWindowPos(Window, HWND_TOP,
|
SetWindowPos(Window, HWND_TOP,
|
||||||
MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
|
MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
|
||||||
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left,
|
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left,
|
||||||
MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
|
MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
|
||||||
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -361,7 +361,7 @@ static PLATFORM_TOGGLE_FULLSCREEN(Win32_ToggleFullscreen)
|
||||||
SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW);
|
SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW);
|
||||||
SetWindowPlacement(Window, &Global_WindowPosition);
|
SetWindowPlacement(Window, &Global_WindowPosition);
|
||||||
SetWindowPos(Window, 0, 0, 0, 0, 0,
|
SetWindowPos(Window, 0, 0, 0, 0, 0,
|
||||||
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED);
|
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_FRAMECHANGED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,14 +481,14 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
||||||
b32 BecomingFullscreen = false;
|
b32 BecomingFullscreen = false;
|
||||||
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
|
||||||
if(GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY),
|
if(GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY),
|
||||||
&MonitorInfo))
|
&MonitorInfo))
|
||||||
{
|
{
|
||||||
s32 MonWidth = (MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left);
|
s32 MonWidth = (MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left);
|
||||||
s32 MonHeight = (MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top);
|
s32 MonHeight = (MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top);
|
||||||
BecomingFullscreen = ((MonitorInfo.rcMonitor.left == NewPos->x) &&
|
BecomingFullscreen = ((MonitorInfo.rcMonitor.left == NewPos->x) &&
|
||||||
(MonitorInfo.rcMonitor.top == NewPos->y) &&
|
(MonitorInfo.rcMonitor.top == NewPos->y) &&
|
||||||
(MonWidth == NewPos->cx) &&
|
(MonWidth == NewPos->cx) &&
|
||||||
(MonHeight == NewPos->cy));
|
(MonHeight == NewPos->cy));
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD OldStyle = GetWindowLong(Window, GWL_STYLE);
|
DWORD OldStyle = GetWindowLong(Window, GWL_STYLE);
|
||||||
|
@ -573,6 +573,8 @@ static LRESULT Win32_WindowCallback(HWND Window, UINT Message, WPARAM WParam, LP
|
||||||
else if(VKCode == VK_NEXT) { Key = Key_PageDown; }
|
else if(VKCode == VK_NEXT) { Key = Key_PageDown; }
|
||||||
else if(VKCode == VK_HOME) { Key = Key_Home; }
|
else if(VKCode == VK_HOME) { Key = Key_Home; }
|
||||||
else if(VKCode == VK_END) { Key = Key_End; }
|
else if(VKCode == VK_END) { Key = Key_End; }
|
||||||
|
else if(VKCode == VK_OEM_PLUS) { Key = Key_Plus; }
|
||||||
|
else if(VKCode == VK_OEM_MINUS) { Key = Key_Minus; }
|
||||||
else if(VKCode == VK_BACK) { Key = Key_Backspace; }
|
else if(VKCode == VK_BACK) { Key = Key_Backspace; }
|
||||||
else if(VKCode == VK_DELETE) { Key = Key_Delete; }
|
else if(VKCode == VK_DELETE) { Key = Key_Delete; }
|
||||||
else if(VKCode == VK_ESCAPE) { Key = Key_Escape; }
|
else if(VKCode == VK_ESCAPE) { Key = Key_Escape; }
|
||||||
|
@ -776,12 +778,12 @@ int WinMain(HINSTANCE Instance, HINSTANCE PreviousInstance, LPSTR CommandLine, i
|
||||||
if(RegisterClassA(&WindowClass))
|
if(RegisterClassA(&WindowClass))
|
||||||
{
|
{
|
||||||
HWND Window = CreateWindowEx(0,
|
HWND Window = CreateWindowEx(0,
|
||||||
WindowClass.lpszClassName,
|
WindowClass.lpszClassName,
|
||||||
"vn - December 2023 Build",
|
"vn - December 2023 Build",
|
||||||
WS_OVERLAPPEDWINDOW,
|
WS_OVERLAPPEDWINDOW,
|
||||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
0, 0, Instance, 0);
|
0, 0, Instance, 0);
|
||||||
if(Window)
|
if(Window)
|
||||||
{
|
{
|
||||||
Global_Win32State.Window = Window;
|
Global_Win32State.Window = Window;
|
||||||
|
|
Loading…
Reference in New Issue