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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
17
code/vn.cpp
17
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();
|
||||||
|
@ -213,12 +213,23 @@ 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;
|
||||||
|
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()));
|
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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{ 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` }
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//- sixten: handle insertions
|
||||||
|
{
|
||||||
|
b32 InsertedSomething = false;
|
||||||
if(Action->Codepoint != 0)
|
if(Action->Codepoint != 0)
|
||||||
{
|
{
|
||||||
Op.ReplaceString = StringFromCodepoint(Arena, Action->Codepoint);
|
Op.ReplaceString = StringFromCodepoint(Arena, Action->Codepoint);
|
||||||
|
InsertedSomething = true;
|
||||||
if(State->Cursor == State->Mark)
|
|
||||||
{
|
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
|
||||||
Op.Range = Range1S64(State->Cursor, State->Cursor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
|
||||||
Op.Range = Range1S64(State->Cursor, State->Mark);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(Action->Flags & TextActionFlag_Paste)
|
else if(Action->Flags & TextActionFlag_Paste)
|
||||||
{
|
{
|
||||||
Op.ReplaceString = Platform.GetClipboard(Arena);
|
Op.ReplaceString = RemoveAll(Arena, Platform.GetClipboard(Arena), '\r');;
|
||||||
Op.Range = Range1S64(State->Cursor, State->Cursor);
|
InsertedSomething = true;
|
||||||
Op.NewCursor += Op.ReplaceString.Count;
|
}
|
||||||
|
|
||||||
|
if(InsertedSomething)
|
||||||
|
{
|
||||||
|
range1_s64 Selection = Range1S64(State->Cursor, State->Mark);
|
||||||
|
Op.Range = Selection;
|
||||||
|
Op.NewCursor = Op.NewMark = Selection.Min+Op.ReplaceString.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!(Action->Flags & TextActionFlag_KeepMark))
|
if(!(Action->Flags & TextActionFlag_KeepMark))
|
||||||
|
|
|
@ -200,6 +200,8 @@ 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);
|
||||||
|
|
||||||
|
Offset = Clamp(Offset, 0, DimOfRange(UI_TopParent()->Rect).E[Axis]-Size-UI_TopFontSize()*2);
|
||||||
|
|
||||||
UI_Spacer(UI_Pixels(Offset, 1));
|
UI_Spacer(UI_Pixels(Offset, 1));
|
||||||
|
|
||||||
UI_SetNextCornerRadius(4.0f);
|
UI_SetNextCornerRadius(4.0f);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -75,6 +75,7 @@ static workspace_text_data W_TextDataFromString(arena *Arena, string Text)
|
||||||
|
|
||||||
//- sixten: gather all line ranges
|
//- sixten: gather all line ranges
|
||||||
range1_s64_list Lines = {};
|
range1_s64_list Lines = {};
|
||||||
|
s64 MaxLineCount = 0;
|
||||||
{
|
{
|
||||||
u8 *TextBegin = Text.Data;
|
u8 *TextBegin = Text.Data;
|
||||||
u8 *TextEnd = TextBegin + Text.Count;
|
u8 *TextEnd = TextBegin + Text.Count;
|
||||||
|
@ -86,6 +87,10 @@ static workspace_text_data W_TextDataFromString(arena *Arena, string Text)
|
||||||
//- sixten: push line range on newline and EOF
|
//- sixten: push line range on newline and EOF
|
||||||
if(Char == TextEnd || *Char == '\n')
|
if(Char == TextEnd || *Char == '\n')
|
||||||
{
|
{
|
||||||
|
if(DimOfRange(Range) > MaxLineCount)
|
||||||
|
{
|
||||||
|
MaxLineCount = DimOfRange(Range);
|
||||||
|
}
|
||||||
Range1S64ListPush(Scratch.Arena, &Lines, Range);
|
Range1S64ListPush(Scratch.Arena, &Lines, Range);
|
||||||
Range = Range1S64(Range.Max, Range.Max);
|
Range = Range1S64(Range.Max, Range.Max);
|
||||||
}
|
}
|
||||||
|
@ -97,6 +102,7 @@ static workspace_text_data W_TextDataFromString(arena *Arena, string Text)
|
||||||
{
|
{
|
||||||
Result.Tokens = Tokens;
|
Result.Tokens = Tokens;
|
||||||
Result.Lines = Range1S64ArrayFromList(Arena, &Lines);;
|
Result.Lines = Range1S64ArrayFromList(Arena, &Lines);;
|
||||||
|
Result.MaxLineCount = MaxLineCount;
|
||||||
}
|
}
|
||||||
ReleaseScratch(Scratch);
|
ReleaseScratch(Scratch);
|
||||||
return(Result);
|
return(Result);
|
||||||
|
@ -107,6 +113,7 @@ static void W_TextEditorApplyChanges(workspace_view_text_editor *Editor)
|
||||||
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->MaxLineCount = TextData.MaxLineCount;
|
||||||
Editor->Compiled = S_ScriptFromText(Editor->ProcessingArena, Editor->Text.String);
|
Editor->Compiled = S_ScriptFromText(Editor->ProcessingArena, Editor->Text.String);
|
||||||
if(Editor->Compiled.IsValid)
|
if(Editor->Compiled.IsValid)
|
||||||
{
|
{
|
||||||
|
@ -136,247 +143,6 @@ static void W_SaveTextEditorToFile(workspace_view_text_editor *Editor)
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ sixten: Workspace Text Editor Builder Functions
|
//~ sixten: Workspace Text Editor Builder Functions
|
||||||
|
|
||||||
static UI_CUSTOM_DRAW_CALLBACK(W_TextEditorDrawCallback)
|
|
||||||
{
|
|
||||||
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)
|
static b32 W_ProcessTextEditorEvent(workspace_view_text_editor *Editor, platform_event *Event)
|
||||||
{
|
{
|
||||||
b32 CursorHasBeenModified = false;
|
b32 CursorHasBeenModified = false;
|
||||||
|
@ -435,40 +201,8 @@ static void W_BuildTextEditorInfoBar(workspace_view_text_editor *Editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void W_BuildTextEditor(workspace_view *View)
|
static void W_BuildTextEditorErrorList(workspace_view_text_editor *Editor)
|
||||||
{
|
{
|
||||||
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);
|
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))
|
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))
|
||||||
{
|
{
|
||||||
|
@ -506,9 +240,388 @@ static void W_BuildTextEditor(workspace_view *View)
|
||||||
UI_Spacer(UI_Em(0.5, 1));
|
UI_Spacer(UI_Em(0.5, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI_CUSTOM_DRAW_CALLBACK(W_TextEditorLinesBarDrawCallback)
|
||||||
|
{
|
||||||
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data;
|
||||||
|
|
||||||
|
//- sixten: calculate dimensions
|
||||||
|
ui_box *Parent = Box->Parent;
|
||||||
|
v2_r32 ParentDim = DimOfRange(Parent->Rect);
|
||||||
|
|
||||||
|
r32 FontSize = Editor->FontSize;
|
||||||
|
r32 LineHeight = FontSize + 4.0f;
|
||||||
|
|
||||||
|
s64 LineStart = Floor(-Parent->Offset.y/LineHeight);
|
||||||
|
s64 LineEnd = Min(Editor->Lines.Count, (s64)Floor((ParentDim.y-Parent->Offset.y)/LineHeight)+1);
|
||||||
|
|
||||||
|
s64 LineNumberCount = (s64)(Log(Editor->Lines.Count)/Log(10))+1;
|
||||||
|
|
||||||
|
for(s64 Line = LineStart; Line < LineEnd; Line += 1)
|
||||||
|
{
|
||||||
|
PushTextF(Group, Atlas, Font_Monospace, Box->Rect.Min+V2R32(0, Line*LineHeight), Editor->FontSize, Color_Grey, " %*i", LineNumberCount, Line+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI_CUSTOM_DRAW_CALLBACK(W_TextEditorDrawCallback)
|
||||||
|
{
|
||||||
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)Data;
|
||||||
|
temp Scratch = GetScratch();
|
||||||
|
|
||||||
|
//- sixten: calculate dimensions
|
||||||
|
ui_box *Parent = Box->Parent;
|
||||||
|
v2_r32 ParentDim = DimOfRange(Parent->Rect);
|
||||||
|
|
||||||
|
//- 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);
|
||||||
|
|
||||||
|
s64 LineStart = Floor(-Parent->Offset.y/LineHeight);
|
||||||
|
s64 LineEnd = Min(Editor->Lines.Count, (s64)Floor((ParentDim.y-Parent->Offset.y)/LineHeight)+1);
|
||||||
|
|
||||||
|
//- 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;
|
||||||
|
v2_r32 StartTokenP = Box->Rect.Min;
|
||||||
|
|
||||||
|
//- sixten: find the first visible token
|
||||||
|
token *VisibleTokensBegin = TokensBegin;
|
||||||
|
for(s64 LinesFound = 0; LinesFound < LineStart && VisibleTokensBegin < TokensEnd; VisibleTokensBegin += 1)
|
||||||
|
{
|
||||||
|
if(VisibleTokensBegin->Kind == TokenKind_Newline)
|
||||||
|
{
|
||||||
|
LinesFound += 1;
|
||||||
|
StartTokenP.y += LineHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: find the last visible token
|
||||||
|
token *VisibleTokensEnd = VisibleTokensBegin;
|
||||||
|
for(s64 LinesFound = 0; LinesFound < LineEnd-LineStart && VisibleTokensEnd < TokensEnd; VisibleTokensEnd += 1)
|
||||||
|
{
|
||||||
|
if(VisibleTokensEnd->Kind == TokenKind_Newline)
|
||||||
|
{
|
||||||
|
LinesFound += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: render tokens
|
||||||
|
v2_r32 TokenP = StartTokenP;
|
||||||
|
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 = StartTokenP.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 = StartTokenP.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 the selection
|
||||||
|
{
|
||||||
|
text_range Selection = TextRange(CursorTextP, MarkTextP);
|
||||||
|
range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line);
|
||||||
|
for(s64 LineIndex = LineStart; LineIndex < LineEnd; LineIndex += 1)
|
||||||
|
{
|
||||||
|
if(Contains(LineRange, LineIndex + 1))
|
||||||
|
{
|
||||||
|
range1_s64 ColumnRange = Editor->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(ColumnOffsetRange.Min*GlyphAdvance, LineY),
|
||||||
|
Box->Rect.Min+V2(ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight));
|
||||||
|
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- 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(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void W_BuildTextEditor(workspace_view *View)
|
||||||
|
{
|
||||||
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
||||||
|
|
||||||
|
//- sixten: calculate dimensions
|
||||||
|
r32 FontSize = Editor->FontSize;
|
||||||
|
r32 LineHeight = FontSize + 4.0f;
|
||||||
|
|
||||||
|
glyph *Glyph = GetGlyph(UI_GlyphAtlas(), Font_Monospace, 'A', FontSize, 0);
|
||||||
|
r32 GlyphAdvance = Glyph->Advance;
|
||||||
|
|
||||||
|
s64 LineCount = Editor->Lines.Count;
|
||||||
|
s64 MaxLineCount = Editor->MaxLineCount;
|
||||||
|
|
||||||
|
s64 LineNumberCount = (s64)(Log(Editor->Lines.Count)/Log(10))+1;
|
||||||
|
v2_r32 LinesBarDim = V2R32((LineNumberCount + 2)*GlyphAdvance, LineCount*LineHeight);
|
||||||
|
|
||||||
|
v2_r32 TextEditorDim = V2R32((MaxLineCount+LineNumberCount+2)*GlyphAdvance, (LineCount+1)*LineHeight);
|
||||||
|
v2_r32 VisibleRegionDim; // sixten(NOTE): This is set further down
|
||||||
|
|
||||||
b32 CursorHasBeenModified = false;
|
b32 CursorHasBeenModified = false;
|
||||||
|
|
||||||
|
//- sixten: layout editor
|
||||||
|
UI_WidthFill UI_HeightFill UI_Column()
|
||||||
|
{
|
||||||
|
//- sixten: line numbers, editor & scrollbar
|
||||||
|
UI_HeightFill UI_Row(UI_BoxFlag_DrawBorder)
|
||||||
|
{
|
||||||
|
UI_WidthFill UI_Column(0, StrLit("Scrollable & Scroll X"))
|
||||||
|
{
|
||||||
|
UI_Row(0, StrLit("Scroll Region")) // contains lines bar & text editor
|
||||||
|
{
|
||||||
|
v2_r32 ScrollRegionDim = DimOfRange(UI_TopParent()->Rect);
|
||||||
|
|
||||||
|
v2_r32 AnimatedOffset = V2R32(AC_AnimateValueF(Editor->Offset.x, Editor->Offset.x, 0.3f, "%p Offset X", View),
|
||||||
|
AC_AnimateValueF(Editor->Offset.y, Editor->Offset.y, 0.3f, "%p Offset Y", View));
|
||||||
|
|
||||||
|
// sixten(NOTE): We put the lines bar & text editor in containers to be able to easily offset them.
|
||||||
|
UI_SetNextBackgroundColor(ColorFromHex(0x10203080));
|
||||||
|
UI_SetNextOffsetY(-AnimatedOffset.y);
|
||||||
|
UI_Width(UI_ChildrenSum(1, 1)) UI_Parent(UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawDropShadow, StrLit("Lines Bar Container")))
|
||||||
|
{
|
||||||
|
UI_SetNextSize(UI_Pixels(LinesBarDim.x, 1), UI_Pixels(LinesBarDim.y, 1));
|
||||||
|
ui_box *LinesBarBox = UI_MakeBoxF(0, "Workspace View Text Editor Lines Bar");
|
||||||
|
UI_EquipBoxCustomDrawCallback(LinesBarBox, W_TextEditorLinesBarDrawCallback, Editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
UI_SetNextOffset(-AnimatedOffset.x, -AnimatedOffset.y);
|
||||||
|
UI_WidthFill UI_Parent(UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, StrLit("Text Editor Container")))
|
||||||
|
{
|
||||||
|
ui_box *EditorContainerBox = UI_TopParent();
|
||||||
|
VisibleRegionDim = DimOfRange(EditorContainerBox->Rect);
|
||||||
|
ui_signal EditorContainerSignal = UI_SignalFromBox(EditorContainerBox);
|
||||||
|
//- sixten: text editor scrolling
|
||||||
|
if(AreEqual(UI_HotKey(), EditorContainerBox->Key))
|
||||||
|
{
|
||||||
|
for(platform_event *Event = UI_EventList()->First;
|
||||||
|
Event != 0;
|
||||||
|
Event = Event->Next)
|
||||||
|
{
|
||||||
|
if(Event->Type == PlatformEvent_MouseScroll && (Event->Modifiers != PlatformModifier_Ctrl) && Event->Scroll.y != 0)
|
||||||
|
{
|
||||||
|
Editor->Offset.y -= Event->Scroll.y*LineHeight*4;
|
||||||
|
Platform_ConsumeEvent(UI_EventList(), Event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UI_SetNextSize(UI_Pixels(TextEditorDim.x, 1), UI_Pixels(TextEditorDim.y, 1));
|
||||||
|
ui_box *TextEditorBox = UI_MakeBoxF(0, "Workspace View Text Editor");
|
||||||
|
UI_EquipBoxCustomDrawCallback(TextEditorBox, W_TextEditorDrawCallback, Editor);
|
||||||
|
|
||||||
|
if(EditorContainerSignal.Dragging)
|
||||||
|
{
|
||||||
|
//- sixten: translate mouse position to text point
|
||||||
|
v2 MouseOffset = EditorContainerSignal.MouseP - TextEditorBox->Rect.Min;
|
||||||
|
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);
|
||||||
|
|
||||||
|
CursorHasBeenModified = true;
|
||||||
|
Editor->DropdownActive = false;
|
||||||
|
|
||||||
|
text_point Point = {LineIndex + 1, ColumnIndex + 1};
|
||||||
|
Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
||||||
|
|
||||||
|
if(EditorContainerSignal.Pressed)
|
||||||
|
{
|
||||||
|
Editor->EditState.Mark = Editor->EditState.Cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: build scrollbar x
|
||||||
|
{
|
||||||
|
ui_box *ParentBox = UI_TopParent();
|
||||||
|
v2_r32 ParentDim = DimOfRange(ParentBox->Rect);
|
||||||
|
r32 MaxScrollWidth = ParentDim.x-UI_TopFontSize()*2;
|
||||||
|
r32 ScrollScaleX = ParentDim.x/TextEditorDim.x;
|
||||||
|
if(ScrollScaleX > 1)
|
||||||
|
{
|
||||||
|
ScrollScaleX = 0;
|
||||||
|
}
|
||||||
|
ui_signal ScrollX = UI_Scrollbar(Axis2_X, StrLit("Workspace View Text Editor Scrollbar X"), MaxScrollWidth*ScrollScaleX, Editor->Offset.x/TextEditorDim.x*MaxScrollWidth);
|
||||||
|
if(ScrollX.Dragging)
|
||||||
|
{
|
||||||
|
if(ScrollX.Pressed)
|
||||||
|
{
|
||||||
|
UI_StoreDragR32(Editor->Offset.x);
|
||||||
|
}
|
||||||
|
Editor->Offset.x = UI_GetDragR32() + ScrollX.DragDelta.x/ScrollScaleX;
|
||||||
|
|
||||||
|
}
|
||||||
|
Editor->Offset.x = Clamp(Editor->Offset.x, 0, Max(0.0f, TextEditorDim.x-ParentDim.x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UI_Width(UI_ChildrenSum(1, 1)) UI_Column(0, StrLit("Scrollable & Scroll Y"))
|
||||||
|
{
|
||||||
|
//- sixten: build scrollbar y
|
||||||
|
{
|
||||||
|
ui_box *ParentBox = UI_TopParent();
|
||||||
|
v2_r32 ParentDim = DimOfRange(ParentBox->Rect);
|
||||||
|
r32 MaxScrollHeight = ParentDim.y-UI_TopFontSize()*3;
|
||||||
|
r32 ScrollScaleY = ParentDim.y/TextEditorDim.y;
|
||||||
|
if(ScrollScaleY > 1)
|
||||||
|
{
|
||||||
|
MaxScrollHeight = 0;
|
||||||
|
}
|
||||||
|
ui_signal ScrollY = UI_Scrollbar(Axis2_Y, StrLit("Workspace View Text Editor Scrollbar Y"), MaxScrollHeight*ScrollScaleY, Editor->Offset.y/TextEditorDim.y*MaxScrollHeight);
|
||||||
|
if(ScrollY.Dragging)
|
||||||
|
{
|
||||||
|
if(ScrollY.Pressed)
|
||||||
|
{
|
||||||
|
UI_StoreDragR32(Editor->Offset.y);
|
||||||
|
}
|
||||||
|
Editor->Offset.y = UI_GetDragR32() + ScrollY.DragDelta.y/ScrollScaleY;
|
||||||
|
}
|
||||||
|
Editor->Offset.y = Clamp(Editor->Offset.y, 0, Max(0.0f, TextEditorDim.y-ParentDim.y));
|
||||||
|
}
|
||||||
|
UI_Width(UI_Em(1, 1)) UI_Height(UI_Em(1, 1)) UI_MakeBox(UI_BoxFlag_DrawBorder, StrLit(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: info bar
|
||||||
|
W_BuildTextEditorInfoBar(Editor);
|
||||||
|
W_BuildTextEditorErrorList(Editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: process inputs
|
||||||
if(W_ViewIsCurrent(View))
|
if(W_ViewIsCurrent(View))
|
||||||
{
|
{
|
||||||
//- sixten: handle history
|
//- sixten: handle history
|
||||||
|
@ -568,6 +681,18 @@ static void W_BuildTextEditor(workspace_view *View)
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
{
|
{
|
||||||
|
//- sixten: check for scaling
|
||||||
|
if(Event->Type == PlatformEvent_Press && Event->Modifiers == PlatformModifier_Ctrl && (Event->Key == Key_Plus || Event->Key == Key_Minus))
|
||||||
|
{
|
||||||
|
Editor->FontSize += (Event->Key == Key_Plus ? 1 : -1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Modifiers == PlatformModifier_Ctrl && (Event->Scroll.y != 0))
|
||||||
|
{
|
||||||
|
Editor->FontSize += (Event->Scroll.y);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text)
|
if(Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text)
|
||||||
{
|
{
|
||||||
//- sixten: auto-indent
|
//- sixten: auto-indent
|
||||||
|
@ -610,7 +735,12 @@ static void W_BuildTextEditor(workspace_view *View)
|
||||||
CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent);
|
CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, &FakeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
CursorHasBeenModified |= W_ProcessTextEditorEvent(Editor, Event);
|
//- sixten: default event
|
||||||
|
if(W_ProcessTextEditorEvent(Editor, Event))
|
||||||
|
{
|
||||||
|
CursorHasBeenModified = true;
|
||||||
|
Platform_ConsumeEvent(UI_EventList(), Event);
|
||||||
|
}
|
||||||
|
|
||||||
//- sixten: apply indent
|
//- sixten: apply indent
|
||||||
{
|
{
|
||||||
|
@ -625,103 +755,27 @@ static void W_BuildTextEditor(workspace_view *View)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//- sixten: right-click dropdown
|
//- sixten: change last text point & adapt view to cursor
|
||||||
{
|
|
||||||
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)
|
if(CursorHasBeenModified)
|
||||||
{
|
{
|
||||||
text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
text_point Point = TextPointFromOffset(Editor->Text.String, Editor->EditState.Cursor);
|
||||||
|
s64 LineIndex = Point.Line-1;
|
||||||
|
string Line = Substring(Editor->Text.String, Editor->Lines.Ranges[LineIndex]);
|
||||||
|
s64 ColumnIndex = Point.Column-1;
|
||||||
|
s64 ColumnOffset = UTF8OffsetFromIndex(Line, ColumnIndex);
|
||||||
|
|
||||||
|
v2 CursorP = V2(ColumnOffset*GlyphAdvance, LineIndex*LineHeight);
|
||||||
|
|
||||||
|
if(CursorP.y < Editor->Offset.y)
|
||||||
|
{
|
||||||
|
Editor->Offset.y = CursorP.y;
|
||||||
|
}
|
||||||
|
if(CursorP.y > Editor->Offset.y + VisibleRegionDim.y - LineHeight)
|
||||||
|
{
|
||||||
|
Editor->Offset.y = CursorP.y - VisibleRegionDim.y + LineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
if(Editor->LastTextPoint.Line == Point.Line)
|
if(Editor->LastTextPoint.Line == Point.Line)
|
||||||
{
|
{
|
||||||
Editor->LastTextPoint = Point;
|
Editor->LastTextPoint = Point;
|
||||||
|
@ -733,5 +787,4 @@ static void W_BuildTextEditor(workspace_view *View)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReleaseScratch(Scratch);
|
|
||||||
}
|
}
|
|
@ -42,6 +42,7 @@ 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
|
||||||
|
@ -50,6 +51,7 @@ struct workspace_view_text_editor
|
||||||
arena *ProcessingArena;
|
arena *ProcessingArena;
|
||||||
token_array Tokens;
|
token_array Tokens;
|
||||||
range1_s64_array Lines;
|
range1_s64_array Lines;
|
||||||
|
s64 MaxLineCount;
|
||||||
compiled_scene Compiled;
|
compiled_scene Compiled;
|
||||||
|
|
||||||
// sixten: text being edited
|
// sixten: text being edited
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Binary file not shown.
|
@ -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; }
|
||||||
|
|
Loading…
Reference in New Issue