Added undo & redo support.
parent
a9f257ca7c
commit
30e384e0a4
|
@ -148,6 +148,27 @@ IsNull(p) ? (SetNull((n)->prev), (n)->next = (f), (IsNull(f) ? (0) : ((f)->prev
|
||||||
#define DLLRemove(First, Last, Element) DLLRemove_NP(First, Last, Element, Next, Prev)
|
#define DLLRemove(First, Last, Element) DLLRemove_NP(First, Last, Element, Next, Prev)
|
||||||
#define DLLIsEmpty(First) ((First) == 0)
|
#define DLLIsEmpty(First) ((First) == 0)
|
||||||
|
|
||||||
|
#define SenDLLInit(Sentinel)\
|
||||||
|
(Sentinel)->Next = (Sentinel);\
|
||||||
|
(Sentinel)->Prev = (Sentinel);
|
||||||
|
|
||||||
|
#define SenDLLInsertFirst(Sentinel, Element)\
|
||||||
|
(Element)->Next = (Sentinel)->Next;\
|
||||||
|
(Element)->Prev = (Sentinel);\
|
||||||
|
(Element)->Next->Prev = (Element);\
|
||||||
|
(Element)->Prev->Next = (Element);
|
||||||
|
|
||||||
|
#define SenDLLInsertLast(Sentinel, Element)\
|
||||||
|
(Element)->Next = (Sentinel);\
|
||||||
|
(Element)->Prev = (Sentinel)->Prev;\
|
||||||
|
(Element)->Next->Prev = (Element);\
|
||||||
|
(Element)->Prev->Next = (Element);
|
||||||
|
|
||||||
|
#define SenDLLRemove(Element)\
|
||||||
|
auto __Temp = (Element)->Next->Prev;\
|
||||||
|
(Element)->Next->Prev = (Element)->Prev->Next;\
|
||||||
|
(Element)->Prev->Next = __Temp;
|
||||||
|
|
||||||
//- sixten: Stringify
|
//- sixten: Stringify
|
||||||
|
|
||||||
#define _Stringify(x) #x
|
#define _Stringify(x) #x
|
||||||
|
|
|
@ -509,54 +509,141 @@ read_only u8 UTF8Lengths[] =
|
||||||
0, // 11111
|
0, // 11111
|
||||||
};
|
};
|
||||||
|
|
||||||
static utf8_iterator IterateUTF8String(string String)
|
static string_decode DecodeUTF8Codepoint(u8 *Data, s64 Count)
|
||||||
{
|
{
|
||||||
utf8_iterator Iter = {};
|
string_decode Result = {};
|
||||||
Iter.Data = String;
|
u8 FirstByteMask[] = {0, 0x7F, 0x1F, 0x0F, 0x07};
|
||||||
Advance(&Iter);
|
u8 FinalShift[] = {0, 18, 12, 6, 0};
|
||||||
|
if(Count > 0)
|
||||||
return(Iter);
|
{
|
||||||
|
Result.Codepoint = '#';
|
||||||
|
Result.Size = 1;
|
||||||
|
|
||||||
|
u8 Byte = Data[0];
|
||||||
|
u8 Length = UTF8Lengths[Byte>>3];
|
||||||
|
if(0 < Length && Length <= Count)
|
||||||
|
{
|
||||||
|
u32 Codepoint = (Byte&FirstByteMask[Length])<<18;
|
||||||
|
switch(Length)
|
||||||
|
{
|
||||||
|
case 4: {Codepoint |= ((Data[3] & 0x3F) << 0);} fallthrough;
|
||||||
|
case 3: {Codepoint |= ((Data[2] & 0x3F) << 6);} fallthrough;
|
||||||
|
case 2: {Codepoint |= ((Data[1] & 0x3F) << 12);} fallthrough;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.Codepoint = Codepoint >> FinalShift[Length];
|
||||||
|
Result.Size = Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Advance(utf8_iterator *Iter)
|
static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint)
|
||||||
{
|
{
|
||||||
u8 *At = Iter->Data.Data + Iter->Index;
|
u32 Size = 0;
|
||||||
|
u8 DummyDest[4];
|
||||||
if(Iter->Index < Iter->Data.Count)
|
Dest = Dest?Dest:DummyDest;
|
||||||
|
if(Codepoint < (1<<8))
|
||||||
{
|
{
|
||||||
if((At[0] & 0x80) == 0x00)
|
Dest[0] = Codepoint;
|
||||||
{
|
Size = 1;
|
||||||
Iter->Codepoint = (At[0] & 0x7F);
|
}
|
||||||
Iter->Index += 1;
|
else if (Codepoint < (1 << 11))
|
||||||
}
|
{
|
||||||
else if((At[0] & 0xE0) == 0xC0)
|
Dest[0] = 0xC0|(Codepoint >> 6);
|
||||||
{
|
Dest[1] = 0x80|(Codepoint & 0x3F);
|
||||||
Iter->Codepoint = ((At[0] & 0x1F) << 6)|(At[1] & 0x3F);
|
Size = 2;
|
||||||
Iter->Index += 2;
|
}
|
||||||
}
|
else if (Codepoint < (1 << 16))
|
||||||
else if((At[0] & 0xF0) == 0xE0)
|
{
|
||||||
{
|
Dest[0] = 0xE0|(Codepoint >> 12);
|
||||||
Iter->Codepoint = ((At[0] & 0x0F) << 12)|((At[1] & 0x3F) << 6)|(At[2] & 0x3F);
|
Dest[1] = 0x80|((Codepoint >> 6) & 0x3F);
|
||||||
Iter->Index += 3;
|
Dest[2] = 0x80|(Codepoint & 0x3F);
|
||||||
}
|
Size = 3;
|
||||||
else if((Iter->Data.Data[Iter->Index] & 0xF8) == 0xF0)
|
}
|
||||||
{
|
else if (Codepoint < (1 << 21))
|
||||||
Iter->Codepoint = ((At[0] & 0x0F) << 18)|((At[1] & 0x3F) << 12)|((At[2] & 0x3F) << 6)|(At[3] & 0x3F);
|
{
|
||||||
Iter->Index += 4;
|
Dest[0] = 0xF0|(Codepoint >> 18);
|
||||||
}
|
Dest[1] = 0x80|((Codepoint >> 12) & 0x3F);
|
||||||
|
Dest[2] = 0x80|((Codepoint >> 6) & 0x3F);
|
||||||
|
Dest[3] = 0x80|(Codepoint & 0x3F);
|
||||||
|
Size = 4;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Iter->Codepoint = 0;
|
Dest[0] = '#';
|
||||||
|
Size = 1;
|
||||||
}
|
}
|
||||||
|
return(Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static b32 IsValid(utf8_iterator *Iter)
|
static string_decode DecodeUTF16Codepoint(u8 *Data, s64 Count)
|
||||||
{
|
{
|
||||||
b32 Result = (Iter->Codepoint != 0);
|
string_decode Result = {'#', 1};
|
||||||
|
if(Data[0] < 0xD800 || 0xDFFF < Data[0])
|
||||||
|
{
|
||||||
|
Result.Codepoint = Data[0];
|
||||||
|
Result.Size = 1;
|
||||||
|
}
|
||||||
|
else if(Count >= 2)
|
||||||
|
{
|
||||||
|
if(0xD800 <= Data[0] && Data[0] < 0xDC00 &&
|
||||||
|
0xDC00 <= Data[1] && Data[1] < 0xE000)
|
||||||
|
{
|
||||||
|
Result.Codepoint = ((Data[0] - 0xD800)<<10)|(Data[1]-0xDC00);
|
||||||
|
Result.Size = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
return(Result);
|
return(Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 EncodeUTF16Codepoint(u16 *Dest, u32 Codepoint)
|
||||||
|
{
|
||||||
|
u32 Size = 0;
|
||||||
|
u16 DummyDest[2];
|
||||||
|
Dest = Dest?Dest:DummyDest;
|
||||||
|
if(Codepoint < 0x10000)
|
||||||
|
{
|
||||||
|
Dest[0] = Codepoint;
|
||||||
|
Size = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dest[0] = ((Codepoint - 0x10000) >> 10) + 0xD800;
|
||||||
|
Dest[1] = ((Codepoint - 0x10000) & 0x3FF) + 0xDC00;
|
||||||
|
Size = 2;
|
||||||
|
}
|
||||||
|
return(Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static s64 UTF8IndexFromOffset(string String, s64 Offset)
|
||||||
|
{
|
||||||
|
u8 *StringBegin = String.Data;
|
||||||
|
u8 *StringEnd = StringBegin+String.Count;
|
||||||
|
u8 *Byte = StringBegin;
|
||||||
|
for(;Byte < StringEnd && Offset > 1; Offset -= 1)
|
||||||
|
{
|
||||||
|
Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size;
|
||||||
|
}
|
||||||
|
s64 Result = Byte-StringBegin;
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static s64 UTF8OffsetFromIndex(string String, s64 Index)
|
||||||
|
{
|
||||||
|
s64 Offset = 0;
|
||||||
|
u8 *StringBegin = String.Data;
|
||||||
|
u8 *StringEnd = StringBegin+Min(Index, String.Count);
|
||||||
|
u8 *Byte = StringBegin;
|
||||||
|
for(;Byte < StringEnd;)
|
||||||
|
{
|
||||||
|
Offset += 1;
|
||||||
|
Byte += DecodeUTF8Codepoint(Byte, StringEnd-Byte).Size;
|
||||||
|
}
|
||||||
|
return(Offset);
|
||||||
|
}
|
||||||
|
|
||||||
static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint)
|
static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint)
|
||||||
{
|
{
|
||||||
s64 Length = 0;
|
s64 Length = 0;
|
||||||
|
|
|
@ -104,11 +104,13 @@ static string RemoveAll(memory_arena *Arena, string Text, char ToRemove);
|
||||||
static s64 StringLength(char *String);
|
static s64 StringLength(char *String);
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
//~ sixten: String Chunk Functions
|
//~ sixten: String Chunk Functions
|
||||||
static string_chunk_list MakeStringChunkList(s64 ChunkSize);
|
static string_chunk_list MakeStringChunkList(s64 ChunkSize);
|
||||||
static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List);
|
static string JoinStringChunkList(memory_arena *Arena, string_chunk_list *List);
|
||||||
static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range);
|
static void ReplaceRange(memory_arena *Arena, string_chunk_list *List, string Text, range1_s64 Range);
|
||||||
|
#endif
|
||||||
|
|
||||||
//~ sixten: String list
|
//~ sixten: String list
|
||||||
|
|
||||||
|
@ -118,19 +120,17 @@ static string JoinStringList(string_list *List, memory_arena *Arena);
|
||||||
|
|
||||||
//~ sixten: Unicode
|
//~ sixten: Unicode
|
||||||
|
|
||||||
struct utf8_iterator
|
struct string_decode
|
||||||
{
|
{
|
||||||
// sixten: Input
|
|
||||||
string Data;
|
|
||||||
s64 Index;
|
|
||||||
|
|
||||||
// sixten: Output
|
|
||||||
u32 Codepoint;
|
u32 Codepoint;
|
||||||
|
s32 Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
static utf8_iterator IterateUTF8String(string String);
|
static string_decode DecodeUTF8Codepoint(u8 *Data, s64 Count);
|
||||||
static void Advance(utf8_iterator *Iter);
|
static u32 EncodeUTF8Codepoint(u8 *Dest, u32 Codepoint);
|
||||||
static b32 IsValid(utf8_iterator *Iter);
|
|
||||||
|
static string_decode DecodeUTF16Codepoint(u8 *Data, s64 Count);
|
||||||
|
static u32 EncodeUTF16Codepoint(u16 *Dest, u32 Codepoint);
|
||||||
|
|
||||||
static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint);
|
static s64 UTF8FromCodepoint(u8 *Out, u32 Codepoint);
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ per_thread debug_settings *DEBUG_DebugSettings = 0;
|
||||||
#include "vn_ui.h"
|
#include "vn_ui.h"
|
||||||
#include "vn_ui_utils.h"
|
#include "vn_ui_utils.h"
|
||||||
#include "vn_workspace.h"
|
#include "vn_workspace.h"
|
||||||
#include "vn_theme_dark.h"
|
|
||||||
#include "vn_animation_curve.h"
|
#include "vn_animation_curve.h"
|
||||||
|
#include "vn_theme_dark.h"
|
||||||
#include "vn_scene.h"
|
#include "vn_scene.h"
|
||||||
|
|
||||||
#include "vn_tokenizer.cpp"
|
#include "vn_tokenizer.cpp"
|
||||||
|
@ -163,10 +163,12 @@ VN_UPDATE_AND_RENDER(VN_UpdateAndRender)
|
||||||
render_group Group = BeginRenderGroup(RenderCommands);
|
render_group Group = BeginRenderGroup(RenderCommands);
|
||||||
PushClear(&Group, V3(0.1, 0.1, 0.1));
|
PushClear(&Group, V3(0.1, 0.1, 0.1));
|
||||||
|
|
||||||
|
#if 0
|
||||||
PushTexturedQuad(&Group,
|
PushTexturedQuad(&Group,
|
||||||
Range2R32(V2R32(0, 0), RenderCommands->RenderDim),
|
Range2R32(V2R32(0, 0), RenderCommands->RenderDim),
|
||||||
Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(State->BackgroundTexture))),
|
Range2R32(V2R32(0, 0), ConvertV2ToR32(DimFromTexture(State->BackgroundTexture))),
|
||||||
Color_White, Color_White, Color_White, Color_White, 0, 0, 0, State->BackgroundTexture);
|
Color_White, Color_White, Color_White, Color_White, 0, 0, 0, State->BackgroundTexture);
|
||||||
|
#endif
|
||||||
|
|
||||||
UI_RenderFrame(&Group, State->GlyphAtlas);
|
UI_RenderFrame(&Group, State->GlyphAtlas);
|
||||||
|
|
||||||
|
|
|
@ -117,11 +117,12 @@ static glyph_atlas *CreateGlyphAtlas(vn_render_commands *RenderCommands,
|
||||||
Atlas->RenderCommands = RenderCommands;
|
Atlas->RenderCommands = RenderCommands;
|
||||||
Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, 0);
|
Atlas->Texture = RenderCommands->AllocateTexture(V2S32(BitmapSize, BitmapSize), Render_TextureFormat_R8, 0);
|
||||||
|
|
||||||
Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf"));
|
Atlas->Fonts[Font_Regular].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Regular.ttf"));
|
||||||
Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf"));
|
Atlas->Fonts[Font_Bold].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/Roboto-Bold.ttf"));
|
||||||
Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono.ttf"));
|
Atlas->Fonts[Font_Monospace].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono.ttf"));
|
||||||
Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf"));
|
Atlas->Fonts[Font_MonospaceOblique].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/DejaVuSansMono-Oblique.ttf"));
|
||||||
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/icons.ttf"));
|
Atlas->Fonts[Font_Hand].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/PatrickHand-Regular.ttf"));
|
||||||
|
Atlas->Fonts[Font_Icons].Data = Platform_ReadEntireFile(Atlas->Arena, StrLit("fonts/icons.ttf"));
|
||||||
|
|
||||||
for(s32 FontIndex = 0;
|
for(s32 FontIndex = 0;
|
||||||
FontIndex < Font_Count;
|
FontIndex < Font_Count;
|
||||||
|
@ -145,11 +146,13 @@ static r32 PushText(render_group *Group, glyph_atlas *Atlas, font_id Font,
|
||||||
string Text)
|
string Text)
|
||||||
{
|
{
|
||||||
r32 OffsetX = 0;
|
r32 OffsetX = 0;
|
||||||
for(utf8_iterator Iter = IterateUTF8String(Text);
|
u8 *TextBegin = Text.Data;
|
||||||
IsValid(&Iter);
|
u8 *TextEnd = TextBegin+Text.Count;
|
||||||
Advance(&Iter))
|
for(u8 *Byte = TextBegin; Byte < TextEnd;)
|
||||||
{
|
{
|
||||||
u32 Codepoint = Iter.Codepoint;
|
string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
|
||||||
|
Byte += Decode.Size;
|
||||||
|
u32 Codepoint = Decode.Codepoint;
|
||||||
|
|
||||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample));
|
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(P.x*Font_Oversample));
|
||||||
Assert(Glyph);
|
Assert(Glyph);
|
||||||
|
@ -189,11 +192,13 @@ inline r32 CalculateRasterizedTextWidth(glyph_atlas *Atlas, font_id Font, r32 Si
|
||||||
{
|
{
|
||||||
r32 X = 0;
|
r32 X = 0;
|
||||||
|
|
||||||
for(utf8_iterator Iter = IterateUTF8String(Text);
|
u8 *TextBegin = Text.Data;
|
||||||
Iter.Codepoint != 0;
|
u8 *TextEnd = TextBegin+Text.Count;
|
||||||
Advance(&Iter))
|
for(u8 *Byte = TextBegin; Byte < TextEnd;)
|
||||||
{
|
{
|
||||||
u32 Codepoint = Iter.Codepoint;
|
string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
|
||||||
|
Byte += Decode.Size;
|
||||||
|
u32 Codepoint = Decode.Codepoint;
|
||||||
|
|
||||||
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample));
|
glyph *Glyph = GetGlyph(Atlas, Font, Codepoint, Size*Font_Oversample, GetSubpixelSegmentAtP(X*Font_Oversample));
|
||||||
Assert(Glyph);
|
Assert(Glyph);
|
||||||
|
@ -211,11 +216,13 @@ inline r32 CalculateRasterizedTextHeight(glyph_atlas *Atlas, font_id Font, r32 S
|
||||||
|
|
||||||
r32 Y = Size*Scale;
|
r32 Y = Size*Scale;
|
||||||
|
|
||||||
for(utf8_iterator Iter = IterateUTF8String(Text);
|
u8 *TextBegin = Text.Data;
|
||||||
Iter.Codepoint != 0;
|
u8 *TextEnd = TextBegin+Text.Count;
|
||||||
Advance(&Iter))
|
for(u8 *Byte = TextBegin; Byte < TextEnd;)
|
||||||
{
|
{
|
||||||
u32 Codepoint = Iter.Codepoint;
|
string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
|
||||||
|
Byte += Decode.Size;
|
||||||
|
u32 Codepoint = Decode.Codepoint;
|
||||||
|
|
||||||
if(Codepoint == '\n')
|
if(Codepoint == '\n')
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ enum font_id
|
||||||
Font_Regular,
|
Font_Regular,
|
||||||
Font_Bold,
|
Font_Bold,
|
||||||
Font_Monospace,
|
Font_Monospace,
|
||||||
|
Font_MonospaceOblique,
|
||||||
Font_Hand,
|
Font_Hand,
|
||||||
Font_Icons,
|
Font_Icons,
|
||||||
|
|
||||||
|
|
|
@ -423,6 +423,10 @@ static text_op TextOpFromAction(memory_arena *Arena, string String,
|
||||||
Op.NewMark = Op.NewCursor;
|
Op.NewMark = Op.NewCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s64 NewStringCount = String.Count-DimOfRange(Op.Range)+Op.ReplaceString.Count;
|
||||||
|
Op.NewCursor = Clamp(Op.NewCursor, 0, NewStringCount);
|
||||||
|
Op.NewMark = Clamp(Op.NewMark, 0, NewStringCount);
|
||||||
|
|
||||||
return(Op);
|
return(Op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,16 +151,16 @@ static tokenize_result T_TokenizeFromText(memory_arena *Arena, string Filename,
|
||||||
TokenFlags = TokenFlag_Identifier;
|
TokenFlags = TokenFlag_Identifier;
|
||||||
TokenStart = Byte;
|
TokenStart = Byte;
|
||||||
TokenEnd = Byte;
|
TokenEnd = Byte;
|
||||||
Byte += 1;
|
Byte += UTF8Lengths[*Byte>>3];
|
||||||
for(;Byte <= TextEnd; Byte += 1)
|
for(;Byte <= TextEnd; Byte += UTF8Lengths[*Byte>>3])
|
||||||
{
|
{
|
||||||
TokenEnd += 1;
|
|
||||||
if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') ||
|
if(Byte == TextEnd || !(('A' <= *Byte && *Byte <= 'Z') ||
|
||||||
('a' <= *Byte && *Byte <= 'z') ||
|
('a' <= *Byte && *Byte <= 'z') ||
|
||||||
('0' <= *Byte && *Byte <= '9') ||
|
('0' <= *Byte && *Byte <= '9') ||
|
||||||
(UTF8Lengths[*Byte>>3] > 1) ||
|
(UTF8Lengths[*Byte>>3] > 1) ||
|
||||||
*Byte == '_'))
|
*Byte == '_'))
|
||||||
{
|
{
|
||||||
|
TokenEnd = Byte;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -641,12 +641,6 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(Box->Flags & UI_BoxFlag_Clip)
|
|
||||||
{
|
|
||||||
range2_r32 Rect = Intersection(Group->ClipStack[Group->ClipStackUsed], Box->Rect);
|
|
||||||
PushClip(Group, Rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(ui_box *Child = Box->First;
|
for(ui_box *Child = Box->First;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
|
@ -654,18 +648,22 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
||||||
if(Child->Flags & UI_BoxFlag_DrawDropShadow)
|
if(Child->Flags & UI_BoxFlag_DrawDropShadow)
|
||||||
{
|
{
|
||||||
r32 ShadowRadius = 10;
|
r32 ShadowRadius = 10;
|
||||||
v2 P = Child->Rect.Min - V2(ShadowRadius, ShadowRadius);
|
|
||||||
v2 Dim = Child->ComputedDim + V2(ShadowRadius, ShadowRadius)*2;
|
|
||||||
|
|
||||||
range2_r32 Rect = Range2R32(Box->Rect.Min - V2R32(ShadowRadius, ShadowRadius),
|
range2_r32 Rect = Range2R32(Child->Rect.Min - V2R32(ShadowRadius, ShadowRadius),
|
||||||
Box->Rect.Min + V2R32(ShadowRadius, ShadowRadius));
|
Child->Rect.Max + V2R32(ShadowRadius, ShadowRadius));
|
||||||
|
|
||||||
v4 ShadowColor = V4(0, 0, 0, 0.7);
|
v4 ShadowColor = V4(0, 0, 0, 0.3);
|
||||||
|
|
||||||
PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0);
|
PushQuad(Group, Rect, ShadowColor, 0, ShadowRadius, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(Box->Flags & UI_BoxFlag_Clip)
|
||||||
|
{
|
||||||
|
range2_r32 Rect = Intersection(Group->ClipStack[Group->ClipStackUsed], Box->Rect);
|
||||||
|
PushClip(Group, Rect);
|
||||||
|
}
|
||||||
|
|
||||||
for(ui_box *Child = Box->First;
|
for(ui_box *Child = Box->First;
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
|
@ -682,6 +680,7 @@ static void UI_DrawBox(ui_box *Box, render_group *Group, glyph_atlas *GlyphAtlas
|
||||||
{
|
{
|
||||||
PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness);
|
PushQuad(Group, Box->Rect, Box->BorderColor, Box->CornerRadius, 0.8, Box->BorderThickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static r32 UI_CalculateChildrenSum(ui_box *Box, axis2 Axis);
|
static r32 UI_CalculateChildrenSum(ui_box *Box, axis2 Axis);
|
||||||
|
@ -783,9 +782,19 @@ static void UI_LayoutBox(ui_box *Box)
|
||||||
Child != 0;
|
Child != 0;
|
||||||
Child = Child->Next)
|
Child = Child->Next)
|
||||||
{
|
{
|
||||||
Child->Rect.Min = Box->Rect.Min + Child->ComputedRelativeP + Box->Offset;
|
if(Child->Flags & UI_BoxFlag_AnimatePosition)
|
||||||
Child->Rect.Max = Child->Rect.Min + Child->ComputedDim;
|
{
|
||||||
|
Child->ApproachingRelativeP.x = AnimationCurve_AnimateValueF(Child->ComputedRelativeP.x, Child->ComputedRelativeP.x, 0.1, "Box P.X %p", Child);
|
||||||
|
Child->ApproachingRelativeP.y = AnimationCurve_AnimateValueF(Child->ComputedRelativeP.y, Child->ComputedRelativeP.y, 0.1, "Box P.Y %p", Child);
|
||||||
|
|
||||||
|
Child->Rect.Min = Box->Rect.Min + Child->ApproachingRelativeP + Box->Offset;
|
||||||
|
Child->Rect.Max = Child->Rect.Min + Child->ComputedDim;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Child->Rect.Min = Box->Rect.Min + Child->ComputedRelativeP + Box->Offset;
|
||||||
|
Child->Rect.Max = Child->Rect.Min + Child->ComputedDim;
|
||||||
|
}
|
||||||
UI_LayoutBox(Child);
|
UI_LayoutBox(Child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ enum
|
||||||
UI_BoxFlag_FloatingX = (1 << 10),
|
UI_BoxFlag_FloatingX = (1 << 10),
|
||||||
UI_BoxFlag_FloatingY = (1 << 11),
|
UI_BoxFlag_FloatingY = (1 << 11),
|
||||||
UI_BoxFlag_Scrollable = (1 << 12),
|
UI_BoxFlag_Scrollable = (1 << 12),
|
||||||
|
UI_BoxFlag_AnimatePosition = (1 << 13),
|
||||||
};
|
};
|
||||||
typedef u32 ui_box_flags;
|
typedef u32 ui_box_flags;
|
||||||
|
|
||||||
|
@ -61,19 +62,21 @@ typedef UI_CUSTOM_DRAW_CALLBACK(ui_custom_draw_callback);
|
||||||
|
|
||||||
struct ui_box
|
struct ui_box
|
||||||
{
|
{
|
||||||
|
// sixten: building
|
||||||
ui_box *First;
|
ui_box *First;
|
||||||
ui_box *Last;
|
ui_box *Last;
|
||||||
ui_box *Next;
|
ui_box *Next;
|
||||||
ui_box *Prev;
|
ui_box *Prev;
|
||||||
ui_box *Parent;
|
ui_box *Parent;
|
||||||
|
|
||||||
|
// sixten: hashing
|
||||||
ui_box *HashNext;
|
ui_box *HashNext;
|
||||||
ui_box *HashPrev;
|
ui_box *HashPrev;
|
||||||
|
|
||||||
ui_key Key;
|
ui_key Key;
|
||||||
ui_key Seed;
|
ui_key Seed;
|
||||||
u64 LastFrameTouched;
|
u64 LastFrameTouched;
|
||||||
|
|
||||||
|
// sixten: per-frame data
|
||||||
ui_box_flags Flags;
|
ui_box_flags Flags;
|
||||||
string String;
|
string String;
|
||||||
ui_size SemanticSize[Axis2_Count];
|
ui_size SemanticSize[Axis2_Count];
|
||||||
|
@ -87,17 +90,17 @@ struct ui_box
|
||||||
font_id Font;
|
font_id Font;
|
||||||
r32 FontSize;
|
r32 FontSize;
|
||||||
v2 Offset;
|
v2 Offset;
|
||||||
|
|
||||||
ui_custom_draw_callback *DrawCallback;
|
ui_custom_draw_callback *DrawCallback;
|
||||||
void *DrawCallbackData;
|
void *DrawCallbackData;
|
||||||
|
|
||||||
v2 ComputedRelativeP;
|
v2 ComputedRelativeP;
|
||||||
v2 ComputedDim;
|
v2 ComputedDim;
|
||||||
|
|
||||||
|
// sixten: retained data
|
||||||
range2_r32 Rect;
|
range2_r32 Rect;
|
||||||
|
|
||||||
r32 HotTransition;
|
r32 HotTransition;
|
||||||
r32 ActiveTransition;
|
r32 ActiveTransition;
|
||||||
|
v2 ApproachingRelativeP;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ui_box_bucket
|
struct ui_box_bucket
|
||||||
|
|
|
@ -23,13 +23,40 @@ static void MutableStringReplaceRange(mutable_string *MutString, string ReplaceS
|
||||||
if(NewCount > MutString->String.Count)
|
if(NewCount > MutString->String.Count)
|
||||||
{
|
{
|
||||||
s64 ToAllocate = NewCount-MutString->String.Count;
|
s64 ToAllocate = NewCount-MutString->String.Count;
|
||||||
PushArray(MutString->Arena, u8, ToAllocate);
|
PushArrayNoClear(MutString->Arena, u8, ToAllocate);
|
||||||
|
}
|
||||||
|
else if(NewCount < MutString->String.Count)
|
||||||
|
{
|
||||||
|
ArenaPopTo(MutString->Arena, sizeof(memory_arena)+NewCount+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range));
|
Move(MutString->String.Data+Range.Min+ReplaceString.Count, MutString->String.Data+Range.Max, MutString->String.Count-DimOfRange(Range));
|
||||||
Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count);
|
Copy(MutString->String.Data+Range.Min, ReplaceString.Data, ReplaceString.Count);
|
||||||
|
|
||||||
MutString->String.Count = NewCount;
|
MutString->String.Count = NewCount;
|
||||||
|
MutString->String.Data[NewCount] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ sixten: History & Undo Functions
|
||||||
|
|
||||||
|
static history_entry HistoryEntry(memory_arena *Arena, string ReplaceString, range1_s64 Range)
|
||||||
|
{
|
||||||
|
// sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory
|
||||||
|
history_entry Entry = {};
|
||||||
|
Entry.ReplaceString = PushString(Arena, ReplaceString);
|
||||||
|
Entry.Range = Range;
|
||||||
|
return(Entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AppendToHistory(memory_arena *Arena, history_list *List, history_entry Forward, history_entry Backward)
|
||||||
|
{
|
||||||
|
// sixten(TODO): proper memory management, right now we just keep appending to the arena, which works but never frees any memory
|
||||||
|
history_node *Node = PushStructNoClear(Arena, history_node);
|
||||||
|
Node->Forward = Forward;
|
||||||
|
Node->Backward = Backward;
|
||||||
|
|
||||||
|
SenDLLInsertLast(&List->Sentinel, Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -39,6 +66,9 @@ static workspace_text_data Workspace_TextDataFromStringChunkList(memory_arena *A
|
||||||
{
|
{
|
||||||
temporary_memory Scratch = GetScratch(&Arena, 1);
|
temporary_memory Scratch = GetScratch(&Arena, 1);
|
||||||
|
|
||||||
|
//- sixten: clear the target arena
|
||||||
|
ArenaClear(Arena);
|
||||||
|
|
||||||
//- sixten: tokenize the text
|
//- sixten: tokenize the text
|
||||||
tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text);
|
tokenize_result TokenizeResult = T_TokenizeFromText(Arena, StrLit("*scratch*"), Text);
|
||||||
token_array Tokens = TokenizeResult.Tokens;
|
token_array Tokens = TokenizeResult.Tokens;
|
||||||
|
@ -164,9 +194,10 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||||
{
|
{
|
||||||
string TokenString = T_StringFromToken(Editor->Text.String, *Token);
|
string TokenString = T_StringFromToken(Editor->Text.String, *Token);
|
||||||
|
|
||||||
//- sixten: get color from token
|
//- sixten: get color & font from token
|
||||||
|
font_id Font = Font_Monospace;
|
||||||
v4 Color = Color_Magenta;
|
v4 Color = Color_Magenta;
|
||||||
if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; }
|
if(Token->Flags & TokenGroup_Comment) { Color = Color_Grey; Font = Font_MonospaceOblique; }
|
||||||
else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; }
|
else if(Token->Flags & TokenFlag_Reserved) { Color = Color_Grey; }
|
||||||
else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); }
|
else if(Token->Flags & TokenFlag_Symbol) { Color = ColorFromHex(0xbd2d2dff); }
|
||||||
else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); }
|
else if(Token->Flags & TokenFlag_StringLiteral) { Color = ColorFromHex(0xffa900ff); }
|
||||||
|
@ -174,13 +205,16 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||||
else if(Token->Flags & TokenFlag_Identifier)
|
else if(Token->Flags & TokenFlag_Identifier)
|
||||||
{
|
{
|
||||||
//- sixten: check for keywords
|
//- sixten: check for keywords
|
||||||
if(AreEqual(TokenString, StrLit("var")) ||
|
if(AreEqual(TokenString, StrLit("true")) ||
|
||||||
AreEqual(TokenString, StrLit("proc")) ||
|
|
||||||
AreEqual(TokenString, StrLit("branch")) ||
|
|
||||||
AreEqual(TokenString, StrLit("jump")) ||
|
|
||||||
AreEqual(TokenString, StrLit("if")) ||
|
|
||||||
AreEqual(TokenString, StrLit("true")) ||
|
|
||||||
AreEqual(TokenString, StrLit("false")))
|
AreEqual(TokenString, StrLit("false")))
|
||||||
|
{
|
||||||
|
Color = ColorFromHex(0xffa900ff);
|
||||||
|
}
|
||||||
|
else if(AreEqual(TokenString, StrLit("var")) ||
|
||||||
|
AreEqual(TokenString, StrLit("proc")) ||
|
||||||
|
AreEqual(TokenString, StrLit("branch")) ||
|
||||||
|
AreEqual(TokenString, StrLit("jump")) ||
|
||||||
|
AreEqual(TokenString, StrLit("if")))
|
||||||
{
|
{
|
||||||
Color = ColorFromHex(0xf0c674ff);
|
Color = ColorFromHex(0xf0c674ff);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +226,7 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||||
//- sixten: render & advance by token
|
//- sixten: render & advance by token
|
||||||
if(!(Token->Flags & TokenGroup_Whitespace))
|
if(!(Token->Flags & TokenGroup_Whitespace))
|
||||||
{
|
{
|
||||||
TokenP.x += PushText(Group, Atlas, Font_Monospace, TokenP, FontSize, Color, TokenString);
|
TokenP.x += PushText(Group, Atlas, Font, TokenP, FontSize, Color, TokenString);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -216,9 +250,14 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//- sixten: render cursor
|
|
||||||
{
|
{
|
||||||
v2 TargetCursorP = Box->Rect.Min+V2(LineMarginDim.x+(CursorTextP.Column-1)*GlyphAdvance,(CursorTextP.Line-1)*LineHeight);
|
//- 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(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor),
|
v2 CursorP = V2(AnimationCurve_AnimateValueF(TargetCursorP.x, TargetCursorP.x, 0.1, "Workspace Text Editor Cursor X %p", Editor),
|
||||||
AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor));
|
AnimationCurve_AnimateValueF(TargetCursorP.y, TargetCursorP.y, 0.1, "Workspace Text Editor Cursor Y %p", Editor));
|
||||||
v2 CursorDim = V2(2, LineHeight);
|
v2 CursorDim = V2(2, LineHeight);
|
||||||
|
@ -231,28 +270,33 @@ static UI_CUSTOM_DRAW_CALLBACK(Workspace_TextEditorDrawCallback)
|
||||||
{
|
{
|
||||||
text_range Selection = TextRange(CursorTextP, MarkTextP);
|
text_range Selection = TextRange(CursorTextP, MarkTextP);
|
||||||
range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line);
|
range1_s64 LineRange = Range1S64(Selection.Min.Line, Selection.Max.Line);
|
||||||
for(s64 Line = TopMostLine; Line < TopMostLine + LinesOnScreen; Line += 1)
|
for(s64 LineIndex = TopMostLine; LineIndex < TopMostLine + LinesOnScreen; LineIndex += 1)
|
||||||
{
|
{
|
||||||
r32 LineY = Box->Rect.Min.y + Line*LineHeight;
|
r32 LineY = Box->Rect.Min.y + LineIndex*LineHeight;
|
||||||
if(Contains(LineRange, Line + 1))
|
if(Contains(LineRange, LineIndex + 1))
|
||||||
{
|
{
|
||||||
range1_s64 ColumnRange = Lines->Ranges[Line];
|
range1_s64 ColumnRange = Lines->Ranges[LineIndex];
|
||||||
range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange));
|
range1_s64 NormalizedColumnRange = Range1S64(0, DimOfRange(ColumnRange));
|
||||||
if(Line + 1 == LineRange.Min && Line + 1 == LineRange.Max)
|
if(LineIndex+1 == LineRange.Min && LineIndex+1 == LineRange.Max)
|
||||||
{
|
{
|
||||||
NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min);
|
NormalizedColumnRange = Range1S64(Editor->EditState.Cursor - ColumnRange.Min, Editor->EditState.Mark - ColumnRange.Min);
|
||||||
}
|
}
|
||||||
else if(Line + 1 == LineRange.Min)
|
else if(LineIndex+1 == LineRange.Min)
|
||||||
{
|
{
|
||||||
NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange));
|
NormalizedColumnRange = Range1S64(Min(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min, DimOfRange(ColumnRange));
|
||||||
}
|
}
|
||||||
else if(Line + 1 == LineRange.Max)
|
else if(LineIndex+1 == LineRange.Max)
|
||||||
{
|
{
|
||||||
NormalizedColumnRange = Range1S64(0, Max(Editor->EditState.Mark, Editor->EditState.Cursor) - ColumnRange.Min);
|
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));
|
||||||
|
|
||||||
v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C);
|
v4_r32 LineHighlightColor = ColorFromHex(0x66B3CC4C);
|
||||||
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+NormalizedColumnRange.Min*GlyphAdvance, LineY),
|
range2_r32 LineHighlightBox = Range2R32(V2(LineMarginDim.x+ColumnOffsetRange.Min*GlyphAdvance, LineY),
|
||||||
V2(LineMarginDim.x+NormalizedColumnRange.Max*GlyphAdvance, LineY+LineHeight));
|
V2(LineMarginDim.x+ColumnOffsetRange.Max*GlyphAdvance, LineY+LineHeight));
|
||||||
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0);
|
PushQuad(Group, LineHighlightBox, LineHighlightColor, LineHighlightColor, LineHighlightColor, LineHighlightColor, 4, 1.4, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,9 +337,59 @@ static void Workspace_BuildTextEditor(workspace_view *View)
|
||||||
|
|
||||||
b32 CursorHasBeenModified = false;
|
b32 CursorHasBeenModified = false;
|
||||||
|
|
||||||
//- sixten: keyboard input -> text op plus handling
|
|
||||||
if(Workspace_ViewIsCurrent(View))
|
if(Workspace_ViewIsCurrent(View))
|
||||||
{
|
{
|
||||||
|
//- sixten: handle history
|
||||||
|
{
|
||||||
|
history_list *List = &Editor->History;
|
||||||
|
|
||||||
|
//- sixten: undo
|
||||||
|
if(Platform_KeyPress(UI_EventList(), Key_Z, PlatformModifier_Ctrl))
|
||||||
|
{
|
||||||
|
history_node *Node = List->At;
|
||||||
|
if(Node != &List->Sentinel)
|
||||||
|
{
|
||||||
|
//- sixten: get entry & apply
|
||||||
|
history_entry Entry = Node->Backward;
|
||||||
|
MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range);
|
||||||
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
||||||
|
Editor->Tokens = TextData.Tokens;
|
||||||
|
Editor->Lines = TextData.Lines;
|
||||||
|
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
|
||||||
|
CursorHasBeenModified = true;
|
||||||
|
|
||||||
|
List->At = Node->Prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: redo
|
||||||
|
if(Platform_KeyPress(UI_EventList(), Key_Y, PlatformModifier_Ctrl))
|
||||||
|
{
|
||||||
|
history_node *Node = List->At->Next;
|
||||||
|
if(Node != &List->Sentinel)
|
||||||
|
{
|
||||||
|
//- sixten: get entry & apply
|
||||||
|
history_entry Entry = Node->Forward;
|
||||||
|
MutableStringReplaceRange(&Editor->Text, Entry.ReplaceString, Entry.Range);
|
||||||
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
||||||
|
Editor->Tokens = TextData.Tokens;
|
||||||
|
Editor->Lines = TextData.Lines;
|
||||||
|
Editor->EditState.Cursor = Editor->EditState.Mark = Entry.Range.Min+Entry.ReplaceString.Count;
|
||||||
|
CursorHasBeenModified = true;
|
||||||
|
|
||||||
|
List->At = Node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: select all
|
||||||
|
if(Platform_KeyPress(UI_EventList(), Key_A, PlatformModifier_Ctrl))
|
||||||
|
{
|
||||||
|
Editor->EditState.Mark = 0;
|
||||||
|
Editor->EditState.Cursor = Editor->Text.String.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- sixten: keyboard input -> text op
|
||||||
for(platform_event *Event = UI_EventList()->First;
|
for(platform_event *Event = UI_EventList()->First;
|
||||||
Event != 0;
|
Event != 0;
|
||||||
Event = Event->Next)
|
Event = Event->Next)
|
||||||
|
@ -307,16 +401,35 @@ static void Workspace_BuildTextEditor(workspace_view *View)
|
||||||
{
|
{
|
||||||
text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1);
|
text_op Op = TextOpFromAction(Scratch.Arena, Editor->Text.String, &Editor->EditState, &Action, &Editor->Lines, Editor->LastTextPoint.Column - 1);
|
||||||
|
|
||||||
CursorHasBeenModified = true;
|
if(DimOfRange(Op.Range) != 0 || !AreEqual(StrLit(""), Op.ReplaceString))
|
||||||
|
|
||||||
{
|
{
|
||||||
|
//- sixten: append to the history
|
||||||
|
{
|
||||||
|
history_list *List = &Editor->History;
|
||||||
|
|
||||||
|
//- sixten: remove the pre-existing history if needed
|
||||||
|
if(List->Sentinel.Prev != List->At)
|
||||||
|
{
|
||||||
|
// sixten(TODO): instead of just removing the links to the old memory, find some way to manage it.
|
||||||
|
List->Sentinel.Prev->Next = List->At;
|
||||||
|
List->Sentinel.Prev = List->At;
|
||||||
|
}
|
||||||
|
|
||||||
|
range1_s64 Selection = Range1S64(Editor->EditState.Cursor, Editor->EditState.Mark);
|
||||||
|
AppendToHistory(Editor->HistoryArena, List,
|
||||||
|
HistoryEntry(Editor->HistoryArena, Op.ReplaceString, Op.Range),
|
||||||
|
HistoryEntry(Editor->HistoryArena, Substring(Editor->Text.String, Op.Range), Range1S64(Op.Range.Min, Op.Range.Min+Op.ReplaceString.Count)));
|
||||||
|
|
||||||
|
List->At = List->Sentinel.Prev;
|
||||||
|
}
|
||||||
|
//- sixten: apply the text action
|
||||||
MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range);
|
MutableStringReplaceRange(&Editor->Text, Op.ReplaceString, Op.Range);
|
||||||
ArenaClear(Editor->ProcessingArena);
|
|
||||||
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
||||||
Editor->Tokens = TextData.Tokens;
|
Editor->Tokens = TextData.Tokens;
|
||||||
Editor->Lines = TextData.Lines;
|
Editor->Lines = TextData.Lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CursorHasBeenModified = true;
|
||||||
Editor->EditState.Cursor = Op.NewCursor;
|
Editor->EditState.Cursor = Op.NewCursor;
|
||||||
Editor->EditState.Mark = Op.NewMark;
|
Editor->EditState.Mark = Op.NewMark;
|
||||||
}
|
}
|
||||||
|
@ -331,13 +444,23 @@ static void Workspace_BuildTextEditor(workspace_view *View)
|
||||||
{
|
{
|
||||||
//- sixten: translate mouse position to text point
|
//- sixten: translate mouse position to text point
|
||||||
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
||||||
text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1};
|
s64 LineIndex = (s64)(MouseOffset.y / LineHeight);
|
||||||
|
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);
|
Editor->EditState.Cursor = Editor->EditState.Mark = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
||||||
}
|
}
|
||||||
|
|
||||||
//- sixten: translate mouse position to text point
|
//- sixten: translate mouse position to text point
|
||||||
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
v2 MouseOffset = Signal.MouseP - EditorBox->Rect.Min - V2(LineMarginWidth, 0);
|
||||||
text_point Point = {(s64)(MouseOffset.y / LineHeight) + 1, (s64)(MouseOffset.x / GlyphAdvance) + 1};
|
s64 LineIndex = (s64)(MouseOffset.y / LineHeight);
|
||||||
|
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);
|
Editor->EditState.Cursor = OffsetFromTextPoint(Editor->Text.String, Editor->Lines, Point);
|
||||||
|
|
||||||
CursorHasBeenModified = true;
|
CursorHasBeenModified = true;
|
||||||
|
|
|
@ -12,19 +12,27 @@ struct mutable_string
|
||||||
string String;
|
string String;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct history_node
|
////////////////////////////////
|
||||||
|
//~ sixten: History & Undo Types
|
||||||
|
|
||||||
|
struct history_entry
|
||||||
{
|
{
|
||||||
range1_s64 Range;
|
range1_s64 Range;
|
||||||
string ReplaceString;
|
string ReplaceString;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct history_node
|
||||||
|
{
|
||||||
history_node *Next;
|
history_node *Next;
|
||||||
|
history_node *Prev;
|
||||||
|
history_entry Forward;
|
||||||
|
history_entry Backward;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct history_list
|
struct history_list
|
||||||
{
|
{
|
||||||
memory_arena *HistoryArena;
|
history_node *At;
|
||||||
|
history_node Sentinel;
|
||||||
history_node *First;
|
|
||||||
history_node *Last;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -38,19 +46,23 @@ struct workspace_text_data
|
||||||
|
|
||||||
struct workspace_view_text_editor
|
struct workspace_view_text_editor
|
||||||
{
|
{
|
||||||
//- sixten: processed text
|
// sixten: processed text
|
||||||
memory_arena *ProcessingArena;
|
memory_arena *ProcessingArena;
|
||||||
token_array Tokens;
|
token_array Tokens;
|
||||||
range1_s64_array Lines;
|
range1_s64_array Lines;
|
||||||
|
|
||||||
//- sixten: text being edited
|
// sixten: text being edited
|
||||||
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: ui building & rendering
|
// sixten: history
|
||||||
|
memory_arena *HistoryArena;
|
||||||
|
history_list History;
|
||||||
|
|
||||||
|
// sixten: ui building & rendering
|
||||||
ui_box *ContainerBox;
|
ui_box *ContainerBox;
|
||||||
v2 TextDim;
|
v2 TextDim;
|
||||||
v2 Offset;
|
v2 Offset;
|
||||||
|
@ -63,6 +75,12 @@ static mutable_string MutableStringAllocate(u64 Size);
|
||||||
static void MutableStringRelease(mutable_string *String);
|
static void MutableStringRelease(mutable_string *String);
|
||||||
static void MutableStringReplaceRange(mutable_string *String, string ReplaceString, range1_s64 Range);
|
static void MutableStringReplaceRange(mutable_string *String, string ReplaceString, range1_s64 Range);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ sixten: History & Undo Functions
|
||||||
|
|
||||||
|
static history_entry HistoryEntry(memory_arena *Arena, string ReplaceString, range1_s64 Range);
|
||||||
|
static void AppendToHistory(memory_arena *Arena, history_list *List, history_entry Forward, history_entry Backward);
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ sixten: Workspace Text Editor Functions
|
//~ sixten: Workspace Text Editor Functions
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,35 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa
|
||||||
switch(View->Type)
|
switch(View->Type)
|
||||||
{
|
{
|
||||||
case Workspace_View_Editor:
|
case Workspace_View_Editor:
|
||||||
{ View->Data = PushStruct(View->Arena, workspace_view_editor); } break;
|
{
|
||||||
|
View->Data = PushStruct(View->Arena, workspace_view_editor);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Workspace_View_CommandPalette:
|
case Workspace_View_CommandPalette:
|
||||||
{ View->Data = PushStruct(View->Arena, workspace_view_command_palette); } break;
|
{
|
||||||
|
View->Data = PushStruct(View->Arena, workspace_view_command_palette);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Workspace_View_Settings:
|
case Workspace_View_Settings:
|
||||||
{ View->Data = PushStruct(View->Arena, workspace_view_settings); } break;
|
{
|
||||||
|
View->Data = PushStruct(View->Arena, workspace_view_settings);
|
||||||
|
} break;
|
||||||
|
|
||||||
case Workspace_View_TextEditor:
|
case Workspace_View_TextEditor:
|
||||||
{
|
{
|
||||||
View->Data = PushStruct(View->Arena, workspace_view_text_editor);
|
View->Data = PushStruct(View->Arena, workspace_view_text_editor);
|
||||||
|
|
||||||
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
||||||
Editor->ProcessingArena = ArenaAllocate(Gigabytes(1));
|
Editor->ProcessingArena = ArenaAllocate(Gigabytes(1));
|
||||||
Editor->Text = MutableStringAllocate(Gigabytes(2));
|
Editor->Text = MutableStringAllocate(Gigabytes(1));
|
||||||
|
Editor->HistoryArena = ArenaAllocate(Gigabytes(1));
|
||||||
|
|
||||||
|
SenDLLInit(&Editor->History.Sentinel);
|
||||||
|
Editor->History.At = &Editor->History.Sentinel;
|
||||||
|
|
||||||
|
workspace_text_data TextData = Workspace_TextDataFromStringChunkList(Editor->ProcessingArena, Editor->Text.String);
|
||||||
|
Editor->Tokens = TextData.Tokens;
|
||||||
|
Editor->Lines = TextData.Lines;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +54,17 @@ inline workspace_view *Workspace_CreateNewView(workspace_view_type Type, workspa
|
||||||
|
|
||||||
inline void Workspace_DestroyView(workspace_view *View)
|
inline void Workspace_DestroyView(workspace_view *View)
|
||||||
{
|
{
|
||||||
|
switch(View->Type)
|
||||||
|
{
|
||||||
|
case Workspace_View_TextEditor:
|
||||||
|
{
|
||||||
|
workspace_view_text_editor *Editor = (workspace_view_text_editor *)View->Data;
|
||||||
|
ArenaRelease(Editor->ProcessingArena);
|
||||||
|
MutableStringRelease(&Editor->Text);
|
||||||
|
ArenaRelease(Editor->HistoryArena);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
// sixten(NOTE): This function does not ensure that the view is not being used anywhere else.
|
// sixten(NOTE): This function does not ensure that the view is not being used anywhere else.
|
||||||
ArenaRelease(View->Arena);
|
ArenaRelease(View->Arena);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue