Updated tests for strings, and added cursor to widget strings if the widget is currently being edited

This commit is contained in:
PS 2021-03-18 00:18:58 -07:00
parent bf72a52142
commit 3a04aab4fd
7 changed files with 231 additions and 110 deletions

View File

@ -6,12 +6,12 @@
#ifndef FOLDHAUS_EDITOR_DRAW_H #ifndef FOLDHAUS_EDITOR_DRAW_H
internal void internal void
Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color) Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color, s32 CursorPosition)
{ {
gs_string Temp = PushString(State->Transient, 256); gs_string Temp = PushString(State->Transient, 256);
PrintF(&Temp, "%d", Widget.Id.Id); PrintF(&Temp, "%d", Widget.Id.Id);
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
Widget.String.Length, Widget.String.Length + 1,
State->Interface.Style.Font->BitmapMemory, State->Interface.Style.Font->BitmapMemory,
State->Interface.Style.Font->BitmapTextureHandle, State->Interface.Style.Font->BitmapTextureHandle,
State->Interface.Style.Font->BitmapWidth, State->Interface.Style.Font->BitmapWidth,
@ -25,7 +25,8 @@ Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffe
{ {
case Align_Left: case Align_Left:
{ {
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color); RegisterPosition = DrawStringLeftAligned(RenderBuffer,
&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color, CursorPosition, GreenV4);
}break; }break;
case Align_Right: case Align_Right:
@ -82,6 +83,8 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
rect2 WidgetParentUnion = Widget.Bounds; rect2 WidgetParentUnion = Widget.Bounds;
WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds); WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds);
bool IsActiveWidget = ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget);
;
if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0)) if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0))
{ {
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground)) if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
@ -101,7 +104,13 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0)
{ {
v4 Color = State->Interface.Style.TextColor; v4 Color = State->Interface.Style.TextColor;
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color); s32 CursorPosition = -1;
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget)
{
CursorPosition = State->Interface.CursorPosition;
}
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color, CursorPosition);
} }
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) ||
@ -122,11 +131,14 @@ Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* Ren
{ {
// TODO(pjs): add this color to the style // TODO(pjs): add this color to the style
v4 TextColor = BlackV4; v4 TextColor = BlackV4;
Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor); Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, ClippedFillBounds, TextColor, -1);
} }
} }
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline)) bool DrawOutline = ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline);
DrawOutline |= ui_WidgetIsFlagSet(Widget, UIWidgetFlag_Typable) && IsActiveWidget;
if (DrawOutline)
{ {
// TODO(pjs): replace these with values from the style // TODO(pjs): replace these with values from the style
r32 Thickness = 1.0f; r32 Thickness = 1.0f;

View File

@ -335,7 +335,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames); r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
r32 FrameX = LerpR32(FramePercent, BarBounds.Min.x, BarBounds.Max.x); r32 FrameX = LerpR32(FramePercent, BarBounds.Min.x, BarBounds.Max.x);
v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2}; v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2};
DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4); DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, FrameTextPos, WhiteV4, -1, GreenV4);
} }
// Time Slider // Time Slider
@ -352,7 +352,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y}; v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y}; v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y};
PushRenderQuad2D(Interface.RenderBuffer, HeadMin, HeadMax, TimeSliderColor); PushRenderQuad2D(Interface.RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4); DrawString(Interface.RenderBuffer, TempString, Interface.Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4);
} }
} }
@ -465,7 +465,7 @@ DrawLayerMenu(animation_system* AnimationSystem, animation ActiveAnim, ui_interf
{ {
PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4); PushRenderBoundingBox2D(Interface.RenderBuffer, LayerBounds.Min, LayerBounds.Max, 1, WhiteV4);
} }
DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4); DrawString(Interface.RenderBuffer, Layer->Name, Interface.Style.Font, LayerTextPos, WhiteV4, -1, GreenV4);
} }
} }
@ -573,7 +573,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim
r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames); r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames);
r32 FrameX = LerpR32(FramePercent, Bounds.Min.x, Bounds.Max.x); r32 FrameX = LerpR32(FramePercent, Bounds.Min.x, Bounds.Max.x);
v2 FrameTextPos = v2{FrameX, Bounds.Min.y + 2}; v2 FrameTextPos = v2{FrameX, Bounds.Min.y + 2};
DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4); DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, FrameTextPos, WhiteV4, -1, GreenV4);
} }
// Time Slider // Time Slider
@ -591,7 +591,7 @@ FrameCount_Render(animation_timeline_state* TimelineState, animation* ActiveAnim
v2 HeadMin = v2{SliderX - SliderHalfWidth, Bounds.Min.y}; v2 HeadMin = v2{SliderX - SliderHalfWidth, Bounds.Min.y};
v2 HeadMax = v2{SliderX + SliderHalfWidth, Bounds.Max.y}; v2 HeadMax = v2{SliderX + SliderHalfWidth, Bounds.Max.y};
PushRenderQuad2D(Interface->RenderBuffer, HeadMin, HeadMax, TimeSliderColor); PushRenderQuad2D(Interface->RenderBuffer, HeadMin, HeadMax, TimeSliderColor);
DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4); DrawString(Interface->RenderBuffer, TempString, Interface->Style.Font, HeadMin + v2{6, 4}, WhiteV4, -1, GreenV4);
} }
// Interaction // Interaction
@ -620,7 +620,7 @@ LayerList_DrawLayerButton (ui_interface* Interface, gs_string Name, rect2 Bounds
{ {
PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, 1, BoxColor); PushRenderBoundingBox2D(Interface->RenderBuffer, Bounds.Min, Bounds.Max, 1, BoxColor);
} }
DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4); DrawString(Interface->RenderBuffer, Name, Interface->Style.Font, TextPos, WhiteV4, -1, GreenV4);
return Result; return Result;
} }

View File

@ -234,7 +234,7 @@ SculptureView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
gs_string Tempgs_string = PushString(State->Transient, 256); gs_string Tempgs_string = PushString(State->Transient, 256);
PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id, PrintF(&Tempgs_string, "Hot Id: %u, ZIndex: %u | Active Id: %u", State->Interface.HotWidget.Id,
State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id); State->Interface.HotWidget.ZIndex,State->Interface.ActiveWidget.Id);
DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4); DrawString(RenderBuffer, Tempgs_string, State->Interface.Style.Font, v2{PanelBounds.Min.x + 100, PanelBounds.Max.y - 200}, WhiteV4, -1, GreenV4);
} }
Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext); Context.GeneralWorkQueue->CompleteQueueWork(Context.GeneralWorkQueue, Context.ThreadContext);

View File

@ -487,6 +487,13 @@ PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, v2 Min, v2 Max, v
v2{0, 0}, v2{1, 1}, Color); v2{0, 0}, v2{1, 1}, Color);
} }
internal void
PushQuad2DOnBatch (render_quad_batch_constructor* Constructor, rect2 Rect, v4 Color)
{
PushQuad2DOnBatch(Constructor, v2{Rect.Min.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Min.y}, v2{Rect.Max.x, Rect.Max.y}, v2{Rect.Min.x, Rect.Max.y},
v2{0, 0}, v2{1, 1}, Color);
}
internal void internal void
PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color) PushLine2DOnBatch (render_quad_batch_constructor* Constructor, v2 P0, v2 P1, r32 Thickness, v4 Color)
{ {

View File

@ -99,17 +99,35 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char
return PointAfterCharacter; return PointAfterCharacter;
} }
internal void
DrawCursor (render_command_buffer* RenderBuffer, v2 RegisterPosition, bitmap_font* Font, v4 Color)
{
rect2 CursorRect = {};
CursorRect.Min = RegisterPosition;
CursorRect.Max = CursorRect.Min + v2{5, (r32)Font->Ascent};
PushRenderQuad2D(RenderBuffer, CursorRect, Color);
}
internal v2 internal v2
DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color) DrawStringLeftAligned (render_command_buffer* RenderBuffer, render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color, s32 CursorBeforeIndex, v4 CursorColor)
{ {
v2 RegisterPosition = InitialRegisterPosition; v2 RegisterPosition = InitialRegisterPosition;
char* C = gs_string; char* C = gs_string;
for (s32 i = 0; i < Length; i++) for (s32 i = 0; i < Length; i++)
{ {
if (i == CursorBeforeIndex)
{
DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor);
}
v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color);
RegisterPosition.x = PositionAfterCharacter.x; RegisterPosition.x = PositionAfterCharacter.x;
C++; C++;
} }
if (CursorBeforeIndex == Length)
{
DrawCursor(RenderBuffer, RegisterPosition, Font, CursorColor);
}
return RegisterPosition; return RegisterPosition;
} }
@ -128,12 +146,12 @@ DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Len
} }
internal v2 internal v2
DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, gs_string_alignment Alignment = Align_Left) DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* Font, v2 Position, v4 Color, s32 CursorPosition, v4 CursorColor, gs_string_alignment Alignment = Align_Left)
{ {
DEBUG_TRACK_FUNCTION; DEBUG_TRACK_FUNCTION;
v2 LowerRight = Position; v2 LowerRight = Position;
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length, render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer, String.Length + 1,
Font->BitmapMemory, Font->BitmapMemory,
Font->BitmapTextureHandle, Font->BitmapTextureHandle,
Font->BitmapWidth, Font->BitmapWidth,
@ -152,7 +170,7 @@ DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* F
v2 RegisterPosition = Position; v2 RegisterPosition = Position;
if (Alignment == Align_Left) if (Alignment == Align_Left)
{ {
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); RegisterPosition = DrawStringLeftAligned(RenderBuffer, &BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color, CursorPosition, CursorColor);
} }
else if (Alignment == Align_Right) else if (Alignment == Align_Right)
{ {
@ -359,6 +377,7 @@ struct ui_interface
// A per-frame string of the characters which have been typed // A per-frame string of the characters which have been typed
gs_const_string TempInputString; gs_const_string TempInputString;
u64 CursorPosition;
render_command_buffer* RenderBuffer; render_command_buffer* RenderBuffer;
@ -980,6 +999,11 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
{ {
Result.Clicked = true; Result.Clicked = true;
Interface->ActiveWidget = Widget->Id; Interface->ActiveWidget = Widget->Id;
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable))
{
Interface->CursorPosition = Widget->String.Length;
}
} }
} }
@ -1013,11 +1037,12 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
{ {
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
Interface->CursorPosition = Clamp(0, Interface->CursorPosition, State->EditString.Length);
for (u32 i = 0; i < Interface->TempInputString.Length; i++) for (u32 i = 0; i < Interface->TempInputString.Length; i++)
{ {
if (Interface->TempInputString.Str[i] == '\b') if (Interface->TempInputString.Str[i] == '\b')
{ {
if (State->EditString.Length > 0) if (Interface->CursorPosition > 0)
{ {
State->EditString.Length -= 1; State->EditString.Length -= 1;
} }
@ -1030,71 +1055,6 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
} }
} }
#if 0
// if you can click it
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable))
{
// updating hot widget, and handling mouse clicks
if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) &&
PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
{
if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState))
{
Result.Clicked = true;
Interface->ActiveWidget = Widget->Id;
}
Interface->HotWidget = Widget->Id;
}
// click and drag
if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState) &&
PointIsInRect(Widget->Bounds, Interface->Mouse.DownPos))
{
Result.Held = true;
Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos;
}
// if this is the active widget (its been clicked)
if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id))
{
// if you can select it
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Selectable))
{
//
if (MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState) &&
!PointIsInRect(Widget->Bounds, Interface->Mouse.Pos))
{
Interface->ActiveWidget = {};
}
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Typable) &&
Interface->TempInputString.Length > 0)
{
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
// TODO(pjs): Backspace?
for (u32 i = 0; i < Interface->TempInputString.Length; i++)
{
if (Interface->TempInputString.Str[i] == '\b')
{
State->EditString.Length -= 1;
}
else
{
OutChar(&State->EditString, Interface->TempInputString.Str[i]);
}
}
}
}
else if (MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState))
{
Interface->ActiveWidget = {};
}
}
}
#endif
Assert(Widget->Parent != 0); Assert(Widget->Parent != 0);
return Result; return Result;
} }

View File

@ -1522,54 +1522,91 @@ Substring(gs_const_string String, u64 First, u64 Last)
Result.Length = Min(Last - First, String.Length); Result.Length = Min(Last - First, String.Length);
return Result; return Result;
} }
internal u64 internal gs_const_string
Substring(gs_string String, u64 First, u64 Last)
{
return Substring(String.ConstString, First, Last);
}
internal s64
FindFirst(gs_const_string String, u64 StartIndex, char C) FindFirst(gs_const_string String, u64 StartIndex, char C)
{ {
u64 Result = StartIndex; s64 Result = -1;
for(; Result < String.Length && C != String.Str[Result]; Result++); for(u64 i = StartIndex; i < String.Length; i++)
{
if (String.Str[i] == C) {
Result = (s64)i;
break;
}
}
return Result; return Result;
} }
internal u64 internal s64
FindFirst(gs_const_string String, char C) FindFirst(gs_const_string String, char C)
{ {
return FindFirst(String, 0, C); return FindFirst(String, 0, C);
} }
internal s64
internal u64 FindFirst(gs_string String, u64 StartIndex, char C)
FindLast(gs_const_string String, u64 StartIndex, char C)
{ {
s64 Result = StartIndex; return FindFirst(String.ConstString, StartIndex, C);
for(; Result >= 0 && C != String.Str[Result]; Result--); }
return (u64)Result; internal s64
FindFirst(gs_string String, char C)
{
return FindFirst(String.ConstString, 0, C);
} }
internal u64 internal s64
FindLast(gs_const_string String, u64 StartIndex, char C)
{
s64 Result = -1;
for(s64 i= StartIndex; i >= 0; i--)
{
if (String.Str[i] == C) {
Result = i;
break;
}
}
return (u64)Result;
}
internal s64
FindLast(gs_const_string String, char C) FindLast(gs_const_string String, char C)
{ {
return FindLast(String, String.Length - 1, C); return FindLast(String, String.Length - 1, C);
} }
internal s64
FindLast(gs_string String, u64 StartIndex, char C)
{
return FindLast(String.ConstString, StartIndex, C);
}
internal s64
FindLast(gs_string String, char C)
{
return FindLast(String.ConstString, String.Length - 1, C);
}
internal u64 internal s64
FindFirstFromSet(gs_const_string String, char* SetArray) FindFirstFromSet(gs_const_string String, char* SetArray)
{ {
gs_const_string Set = ConstString(SetArray); gs_const_string Set = ConstString(SetArray);
u64 Result = String.Length - 1; s64 Result = -1;
for(u64 At = 0; At < String.Length; At++)
{ s64 CurrMin = String.Length;
char CharAt = String.Str[At];
for (u64 SetAt = 0; SetAt < Set.Length; SetAt++) for (u64 SetAt = 0; SetAt < Set.Length; SetAt++)
{ {
if (CharAt == Set.Str[SetAt]) s64 Index = FindFirst(String, Set.Str[SetAt]);
if (Index >= 0 && Index < CurrMin)
{ {
Result = At; CurrMin = Index;
// NOTE(Peter): The alternative to this goto is a break in the inner loop
// followed by an if check in the outer loop, that must be evaluated
// every character you check. This is more efficient
goto find_last_from_set_complete;
} }
} }
if (CurrMin < (s64)String.Length)
{
Result = CurrMin;
} }
find_last_from_set_complete:
return Result; return Result;
} }
@ -1648,6 +1685,12 @@ StringEqualsCharArray(gs_const_string A, char* B, u64 Length)
return StringsEqual(A, BStr); return StringsEqual(A, BStr);
} }
internal bool internal bool
StringEqualsCharArray(gs_const_string A, char* B)
{
u64 Length = CStringLength(B);
return StringEqualsCharArray(A, B, Length);
}
internal bool
StringsEqualUpToLength(gs_string A, gs_string B, u64 Length) StringsEqualUpToLength(gs_string A, gs_string B, u64 Length)
{ {
return StringsEqualUpToLength(A.ConstString, B.ConstString, Length); return StringsEqualUpToLength(A.ConstString, B.ConstString, Length);
@ -1660,8 +1703,12 @@ StringsEqual(gs_string A, gs_string B)
internal bool internal bool
StringEqualsCharArray(gs_string A, char* B, u64 Length) StringEqualsCharArray(gs_string A, char* B, u64 Length)
{ {
gs_string BStr = MakeString(B, Length); return StringEqualsCharArray(A.ConstString, B, Length);
return StringsEqual(A, BStr); }
internal bool
StringEqualsCharArray(gs_string A, char* B)
{
return StringEqualsCharArray(A.ConstString, B);
} }
internal u64 internal u64
@ -1907,6 +1954,39 @@ AppendString(gs_string* Base, gs_string Appendix)
{ {
return AppendString(Base, Appendix.ConstString); return AppendString(Base, Appendix.ConstString);
} }
internal void
InsertAt(gs_string* Str, u64 Index, char C)
{
if (Str->Length > Index)
{
for (u64 i = Str->Length; i > Index; i--)
{
Str->Str[i] = Str->Str[i - 1];
}
}
if (Index < Str->Size)
{
Str->Str[Index] = C;
Str->Length += 1;
Assert(Str->Length < Str->Size);
}
}
internal void
RemoveAt(gs_string* Str, u64 Index)
{
if (Str->Length > 0 && Index < Str->Length)
{
for (u64 i = Index; i < Str->Length - 1; i++)
{
Str->Str[i] = Str->Str[i + 1];
}
Str->Length -= 1;
}
}
internal void internal void
NullTerminate(gs_string* String) NullTerminate(gs_string* String)
{ {

View File

@ -16,6 +16,15 @@ gs_memory_arena Scratch = {};
void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); } void* Alloc(u64 Size, u64* ResultSize) { *ResultSize = Size; return malloc(Size); }
void Free(void* Ptr, u64 Size) { return free(Ptr); } void Free(void* Ptr, u64 Size) { return free(Ptr); }
bool StringTest (gs_const_string StrA, gs_const_string StrB)
{
return StringsEqual(StrA, StrB);
}
bool StringTest (gs_string StrA, gs_string StrB)
{
return StringsEqual(StrA, StrB);
}
bool PathTest (char* In, char* Out) { bool PathTest (char* In, char* Out) {
return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out)); return StringsEqual(SanitizePath(ConstString(In), &Scratch), ConstString(Out));
} }
@ -24,6 +33,59 @@ int main (int ArgCount, char** Args)
{ {
Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free)); Scratch = CreateMemoryArena(CreateAllocator(Alloc, Free));
Test("gs_string")
{
gs_string TestString = PushStringF(&Scratch, 256, "Hello there, Sailor!");
NullTerminate(&TestString);
TestResult(IsNullTerminated(TestString));
TestResult(StringTest(GetStringPrefix(TestString.ConstString, 5), ConstString("Hello")));
TestResult(StringTest(GetStringPostfix(TestString.ConstString, 5), ConstString("ilor!")));
TestResult(StringTest(GetStringAfter(TestString.ConstString, 13), ConstString("Sailor!")));
TestResult(StringTest(GetStringBefore(TestString.ConstString, 5), ConstString("Hello")));
TestResult(StringTest(Substring(TestString.ConstString, 5, 11), ConstString(" there")));
TestResult(FindFirst(TestString, 5, 'l') == 16);
TestResult(FindFirst(TestString, 0, 'k') == -1);
TestResult(FindLast(TestString, 10, 'l') == 3);
TestResult(FindLast(TestString, 'k') == -1);
TestResult(FindFirstFromSet(TestString.ConstString, "re") == 1);
TestResult(FindFirstFromSet(TestString.ConstString, "er") == 1);
TestResult(FindFirstFromSet(TestString.ConstString, "bk") == -1);
TestResult(FindFirstFromSet(TestString.ConstString, "ek") == 1);
TestResult(FindLastFromSet(TestString.ConstString, "re") == 18);
TestResult(FindLastFromSet(TestString.ConstString, "er") == 18);
TestResult(FindLastFromSet(TestString.ConstString, "bk") == -1);
TestResult(FindLastFromSet(TestString.ConstString, "rk") == 18);
TestResult(StringContains(TestString.ConstString, ','));
TestResult(!StringContains(TestString.ConstString, '@'));
TestResult(StringsEqual(TestString, TestString));
TestResult(StringEqualsCharArray(TestString, "Hello there, Sailor!"));
TestResult(!StringEqualsCharArray(TestString, "Hello there, Sailor"));
TestResult(!StringEqualsCharArray(TestString, "Foobar"));
ReverseStringInPlace(&TestString);
TestResult(StringTest(TestString, MakeString("!roliaS ,ereht olleH")));
ReverseStringInPlace(&TestString);
TestResult(ParseUInt(ConstString("532")) == 532);
TestResult(ParseInt(ConstString("-1234567890")) == -1234567890);
TestResult(ParseFloat(ConstString("-12345.6789")) == -12345.6789);
TestString.Length = 0;
U64ToASCII(&TestString, 53298, 10);
TestResult(StringTest(TestString.ConstString, ConstString("53298")));
TestString.Length = 0;
R64ToASCII(&TestString, 145732.321, 2);
TestResult(StringTest(TestString.ConstString, ConstString("145732.32")));
}
Test("gs_path.h") Test("gs_path.h")
{ {
TestResult(PathTest(".", ".")); TestResult(PathTest(".", "."));