add command palette

This commit is contained in:
Matthew 2025-09-27 17:34:22 +10:00
parent 1becb705be
commit 54619b52d3
8 changed files with 549 additions and 192 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,4 @@
# Editor (Real Name Pending) # PadScratch
A text editor I'm developing for personal use as well as getting used to 2D development with Vulkan. Will be updated as development continues. A text editor I'm developing for personal use as well as getting used to 2D development with Vulkan. Will be updated as development continues.
![Screenshot](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor-screenshot.jpg) ![Screenshot](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor-screenshot.jpg)

View File

@ -15,22 +15,33 @@ import std.format;
import std.stdio; import std.stdio;
import std.exception; import std.exception;
import std.file; import std.file;
import std.string;
f32 g_delta = 0.0; f32 g_delta = 0.0;
debug bool g_frame_step = false; debug bool g_frame_step = false;
debug bool g_frame_continue = false; debug bool g_frame_continue = false;
struct EditorCtx struct EditorCtx
{
Arena arena;
UIPanel* base_panel;
u64 panel_id;
EditState state;
u8[128] input_buf;
u32 icount;
Timer timer;
CmdPalette cmd;
}
struct CmdPalette
{ {
Arena arena; Arena arena;
UIPanel* base_panel; u8[] buffer;
u64 panel_id;
EditState state;
u8[128] input_buf;
u32 icount; u32 icount;
u8[] cmd_buffer; Command[] commands;
u32 cmd_count; u8[][] opt_strs;
Timer timer; i64 selected;
Command current;
} }
struct Editor struct Editor
@ -50,6 +61,22 @@ struct Editor
u64 line_offset; u64 line_offset;
} }
struct Command
{
u8[] name;
CmdType type;
}
enum CmdType
{
None,
OpenFile,
SaveFile,
CreateFile,
}
alias CT = CmdType;
enum EditState enum EditState
{ {
NormalMode, NormalMode,
@ -62,6 +89,11 @@ alias ES = EditState;
bool g_input_mode = false; bool g_input_mode = false;
const Command NO_CMD = {
name: [],
type: CT.None,
};
void void
Cycle(EditorCtx* ctx, Inputs* inputs) Cycle(EditorCtx* ctx, Inputs* inputs)
{ {
@ -88,7 +120,12 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
if(ctx.state == ES.CmdOpen) if(ctx.state == ES.CmdOpen)
{ {
CommandPalette(ctx.cmd_buffer[0 .. ctx.cmd_count]); if(ctx.cmd.commands.length == 0 && ctx.cmd.icount == 0)
{
GetCommands(&ctx.cmd);
}
CommandPalette(&ctx.cmd);
} }
DrawPanels(ctx.base_panel); DrawPanels(ctx.base_panel);
@ -106,12 +143,15 @@ InitEditorCtx(PlatformWindow* window)
EditorCtx ctx = { EditorCtx ctx = {
arena: CreateArena(MB(2)), arena: CreateArena(MB(2)),
cmd: {
arena: CreateArena(KB(512)),
buffer: MAllocArray!(u8)(1024),
},
}; };
ctx.base_panel = CreatePanel(&ctx); ctx.base_panel = CreatePanel(&ctx);
ctx.base_panel.ed = CreateEditor(&ctx); ctx.base_panel.ed = CreateEditor(&ctx);
ctx.timer = CreateTimer(); ctx.timer = CreateTimer();
ctx.cmd_buffer = MAllocArray!(u8)(1024);
SetFocusedPanel(ctx.base_panel); SetFocusedPanel(ctx.base_panel);
return ctx; return ctx;
@ -265,6 +305,8 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
if(key == Input.Escape) if(key == Input.Escape)
{ {
ctx.state = ES.NormalMode; ctx.state = ES.NormalMode;
ctx.cmd.icount = 0;
ctx.cmd.commands = [];
InsertInputToBuf(ctx); InsertInputToBuf(ctx);
taken = true; taken = true;
} }
@ -289,7 +331,6 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
{ {
if(node.value.md & (MD.LeftShift | MD.RightShift)) if(node.value.md & (MD.LeftShift | MD.RightShift))
{ {
Logf("cmd open");
ctx.state = ES.CmdOpen; ctx.state = ES.CmdOpen;
taken = true; taken = true;
} }
@ -415,7 +456,7 @@ TextLineCharCases()
foreach(input; EnumMembers!Input) foreach(input; EnumMembers!Input)
{ {
u8 ch = InputToChar(input); u8 ch = InputToChar(input);
if(ch > 0 && ch != '\n') if(ch > 0 && ch != '\n' && ch != '\t' && ch != ' ')
{ {
if(ch == '\'' || ch == '\\') if(ch == '\'' || ch == '\\')
{ {
@ -431,39 +472,148 @@ TextLineCharCases()
return result; return result;
} }
void
GetCommands(CmdPalette* cmd)
{
const Command[] cmd_list = [
{
name: CastStr!(u8)("open"),
type: CT.OpenFile,
},
{
name: CastStr!(u8)("save"),
type: CT.SaveFile,
},
{
name: CastStr!(u8)("create"),
type: CT.CreateFile,
},
];
Reset(&cmd.arena);
cmd.commands = AllocArray!(Command)(&cmd.arena, cmd_list.length);
u8[] str = cmd.buffer[0 .. cmd.icount];
u64 count = 0;
if(str.length > 0)
{
for(u64 i = 0; i < cmd_list.length; i += 1)
{
u64 match = 0;
for(u64 j = 0; j < str.length && j < cmd_list[i].name.length; j += 1)
{
if(str[j] == cmd_list[i].name[j] || (str[j]-32) == cmd_list[i].name[j])
{
match += 1;
}
}
if(match == str.length)
{
cmd.commands[count] = cast(Command)cmd_list[i];
count += 1;
}
}
}
if(count == 0 && cmd.icount == 0)
{
cmd.commands[] = cast(Command[])cmd_list[];
}
else
{
cmd.commands = cmd.commands[0 .. count];
}
cmd.opt_strs = AllocArray!(u8[])(&cmd.arena, cmd.commands.length);
for(u64 i = 0; i < cmd.commands.length; i += 1)
{
cmd.opt_strs[i] = cmd.commands[i].name;
}
}
bool bool
HandleCmdMode(EditorCtx* ctx, InputEvent ev) HandleCmdMode(EditorCtx* ctx, InputEvent ev)
{ {
u8 result = 0; u8 result = 0;
bool taken = false; bool taken = false;
CmdPalette* cmd = &ctx.cmd;
u64 prev_count = cmd.icount;
switch(ev.key) with(Input) switch(ev.key) with(Input)
{ {
mixin(TextLineCharCases()); mixin(TextLineCharCases());
case Backspace: case Backspace:
{
if(ctx.cmd_count > 0)
{ {
ctx.cmd_count -= 1; if(cmd.icount > 0)
} {
} break; cmd.icount -= 1;
case Escape:
{ if(cmd.buffer[cmd.icount] == ' ')
ctx.cmd_count = 0; {
ctx.state = ES.NormalMode; cmd.current = cast(Command)NO_CMD;
} break; }
}
} break;
case Space:
{
Check(cmd, 1);
cmd.buffer[cmd.icount++] = ' ';
} goto case Tab;
case Tab:
{
if(cmd.commands.length > 0)
{
cmd.current = cmd.commands[cmd.selected];
cmd.buffer[0 .. cmd.current.name.length] = cmd.current.name[0 .. $];
cmd.icount = cast(u32)cmd.current.name.length;
cmd.buffer[cmd.icount++] = ' ';
}
} break;
case Up:
{
if(cmd.selected > 0)
{
cmd.selected -= 1;
}
} break;
case Down:
{
if(cmd.selected < cmd.commands.length)
{
cmd.selected += 1;
}
} break;
default: break; default: break;
} }
if(result != 0) if(result != 0)
{ {
Logf("test"); Check(cmd, 1);
ctx.cmd_buffer[ctx.cmd_count++] = result; cmd.buffer[cmd.icount++] = result;
}
if(cmd.commands.length == 0 || prev_count != cmd.icount)
{
GetCommands(cmd);
cmd.selected = 0;
Logf("%s", cmd.current.type);
} }
return taken; return taken;
} }
pragma(inline) void
Check(CmdPalette* cmd, u64 length)
{
if(cmd.icount+length >= cmd.buffer.length)
{
cmd.buffer = ReallocArray!(u8)(cmd.buffer, cmd.buffer.length*2);
}
}
/* /*
void void
DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb)

View File

@ -37,23 +37,38 @@ alias A2D = Axis2D;
enum UIFlags enum UIFlags
{ {
None = 0x00, None = 0x0000,
DrawBackground = 0x01, DrawBackground = 0x0001,
DrawText = 0x02, DrawText = 0x0002,
DrawBorder = 0x04, DrawBorder = 0x0004,
Clickable = 0x08, Clickable = 0x0008,
Draggable = 0x10, Draggable = 0x0010,
TextInput = 0x20, DragBorder = 0x0020,
Window = 0x40, ClickBorder = 0x0040,
BorderX0 = 0x0080,
BorderX1 = 0x0100,
BorderY0 = 0x0200,
BorderY1 = 0x0400,
TextInput = 0x0800,
Window = 0x1000,
DeferredBorder = 0x2000,
} }
alias UIF = UIFlags; alias UIF = UIFlags;
enum UISignal enum UISignal
{ {
None = 0x00, None = 0x00,
Clicked = 0x01, Clicked = 0x01,
Dragged = 0x02, Dragged = 0x02,
ClickedBorder = 0x04,
DraggedBorder = 0x08,
BorderX0 = 0x080,
BorderX1 = 0x100,
BorderY0 = 0x200,
BorderY1 = 0x400,
} }
alias UIS = UISignal; alias UIS = UISignal;
@ -429,6 +444,15 @@ Push(string id, T)(T value)
} }
} }
void
Pop(stacks...)()
{
static foreach(stack; stacks)
{
Pop!(stack);
}
}
auto auto
Pop(string stack)() Pop(string stack)()
{ {
@ -771,7 +795,7 @@ CalcFixedSizes(UIItem* item)
{ {
if(i.size_info[axis].type == ST.Pixels) if(i.size_info[axis].type == ST.Pixels)
{ {
i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis] + (i.padding.v[axis] * 2.0); i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis];
assert(!isNaN(i.size.v[axis])); assert(!isNaN(i.size.v[axis]));
} }
} }
@ -870,7 +894,35 @@ DrawDebugUI(UICtx* ctx, UIItem* item)
void void
DrawUI(UICtx* ctx, UIItem* item) DrawUI(UICtx* ctx, UIItem* item)
{ {
for(UIItem* i = item; !Nil(i) && i != ctx.root.next; i = Recurse(i))
{
if(i.flags & UIF.DrawBackground)
{
DrawRect(ctx, i);
}
if(i.flags & UIF.DrawBorder)
{
DrawBorder(ctx, i);
}
if(i.flags & UIF.DrawText)
{
DrawLine(i);
}
debug ctx.final_count += 1;
}
for(UIItem* i = item; !Nil(i); i = Recurse(i)) for(UIItem* i = item; !Nil(i); i = Recurse(i))
{
if(i.flags & UIF.DeferredBorder)
{
DrawBorder(ctx, i);
}
}
for(UIItem* i = item.next; !Nil(i); i = Recurse(i))
{ {
if(i.flags & UIF.DrawBackground) if(i.flags & UIF.DrawBackground)
{ {
@ -995,39 +1047,46 @@ Signal(UIItem* item)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
item.signal = UIS.None; item.signal &= UIS.BorderX0|UIS.BorderX1|UIS.BorderY0|UIS.BorderY1;
item.dragged = 0.0; item.dragged = 0.0;
for(DNode!(InputEvent)* n = ctx.inputs.list.first; !CheckNil(null, n); n = n.next) for(DNode!(InputEvent)* n = ctx.inputs.list.first; !CheckNil(null, n); n = n.next)
{ {
bool taken = false; bool taken = false;
if(item.flags & UIF.Clickable && Clicked(item, n)) if(BorderClicked(item, n))
{
taken = true;
if(item.signal & UIS.DraggedBorder)
{
ctx.drag_item = item;
}
}
else if(item.flags & UIF.Clickable && Clicked(item, n))
{ {
Logf("Clicked");
item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0; item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0;
item.signal |= UIS.Clicked; item.signal |= UIS.Clicked;
taken = true; taken = true;
} }
if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) else if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item))
{ {
Logf("Dragged");
item.signal |= UIS.Dragged; item.signal |= UIS.Dragged;
ctx.drag_item = item; ctx.drag_item = item;
taken = true; taken = true;
} }
if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) else if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed)
{ {
Logf("Released");
ctx.drag_item = g_UI_NIL; ctx.drag_item = g_UI_NIL;
item.signal = UIS.None;
taken = true; taken = true;
} }
if(ctx.drag_item == item && n.value.key == Input.MouseMotion) else if(ctx.drag_item == item && n.value.key == Input.MouseMotion)
{ {
item.signal |= UIS.Dragged; item.signal |= (item.flags & (UIF.DragBorder)) ? UIS.DraggedBorder : UIS.Dragged;
item.dragged.x += cast(f32)n.value.rel_x; item.dragged.x += cast(f32)n.value.rel_x;
item.dragged.y += cast(f32)n.value.rel_y; item.dragged.y += cast(f32)n.value.rel_y;
taken = true; taken = true;
@ -1263,8 +1322,8 @@ DrawLine(UIItem* item)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
f32 text_size = Get!("text_size")(); f32 text_size = Get!("text_size")();
f32 y = item.rect.y0 + text_size + item.padding.v[A2D.Y]; f32 y = item.rect.y0 + text_size + item.padding.y;
f32 x = item.rect.x0 + item.padding.v[A2D.X]; f32 x = item.rect.x0 + item.padding.x;
FontAtlas* atlas = &ctx.atlas_buf.atlas; FontAtlas* atlas = &ctx.atlas_buf.atlas;
bool edit_mode = EditModeActive(); bool edit_mode = EditModeActive();
@ -1449,6 +1508,14 @@ DrawRect(UICtx* ctx, UIItem* item)
v.edge_softness = 0.0; v.edge_softness = 0.0;
v.raised = 0.0; v.raised = 0.0;
if(item.border_thickness > 0.0)
{
v.dst_start.x += item.border_thickness;
v.dst_start.y += item.border_thickness;
v.dst_end.x -= item.border_thickness;
v.dst_end.y -= item.border_thickness;
}
AddUIIndices(ctx); AddUIIndices(ctx);
} }
@ -1529,6 +1596,73 @@ Clicked(UIItem* item, DNode!(InputEvent)* n)
return result; return result;
} }
bool
BorderClicked(UIItem* item, DNode!(InputEvent)* n)
{
InputEvent* ev = &n.value;
bool taken = false;
if(item.flags & (UIF.ClickBorder | UIF.DragBorder) && ev.key == Input.LeftClick && ev.pressed)
{
taken = true;
UISignal signal = (item.flags & UIF.DragBorder) ? UIS.DraggedBorder : UIS.ClickedBorder;
f32 x0 = item.rect.x0;
f32 x1 = item.rect.x1;
f32 y0 = item.rect.y0;
f32 y1 = item.rect.y1;
f32 b = item.border_thickness*2.0;
if(item.flags & UIF.BorderX0 && ev.x >= x0 && ev.x <= x0+b)
{
item.signal |= (signal | UIS.BorderX0);
}
else if(item.flags & UIF.BorderX1 && ev.x <= x1 && ev.x >= x1-b)
{
item.signal |= (signal | UIS.BorderX1);
}
else if(item.flags & UIF.BorderY0 && ev.y >= y0 && ev.y <= y0+b)
{
item.signal |= (signal | UIS.BorderY0);
}
else if(item.flags & UIF.BorderY1 && ev.y <= y1 && ev.y >= y1-b)
{
item.signal |= (signal | UIS.BorderY1);
}
else taken = false;
if(taken)
{
Logf("%s Clicked or Dragged", cast(char[])item.key.hash_text);
}
}
return taken;
}
u8[]
ScratchName(u64 num_len, u8[] base, u64 iteration)
{
import core.stdc.stdio : sprintf;
char[64] ch_buf;
char[] s = ch_buf.sformat("%%0%sllu%%s", num_len);
u8[] id = ScratchAlloc!(u8)(base.length+num_len);
sprintf(cast(char*)id.ptr, s.ptr, iteration, cast(char*)base.ptr);
return id;
}
u8[]
ScratchName(string fmt, u64 len, u64 iteration)
{
u8[] id = ScratchAlloc!(u8)(len);
(cast(char[])id).sformat(fmt, iteration);
return id;
}
u8[] u8[]
ScratchName(u8[] base, u8[] append) ScratchName(u8[] base, u8[] append)
{ {

View File

@ -12,12 +12,77 @@ import std.conv;
import core.stdc.stdio : sprintf; import core.stdc.stdio : sprintf;
const Vec4[4] DEFAULT_COL = [ const Vec4[4] DEFAULT_COL = [
Vec4(0.3, 0.65, 0.86, 1.0), Vec4(0.13, 0.13, 0.13, 1.0),
Vec4(0.28, 0.63, 0.83, 1.0), Vec4(0.13, 0.13, 0.13, 1.0),
Vec4(0.26, 0.62, 0.82, 1.0), Vec4(0.13, 0.13, 0.13, 1.0),
Vec4(0.25, 0.61, 0.80, 1.0), Vec4(0.13, 0.13, 0.13, 1.0),
]; ];
const Vec4[4] CMD_PALETTE_COL = [
Vec4(0.21, 0.21, 0.21, 1.0),
Vec4(0.21, 0.21, 0.21, 1.0),
Vec4(0.21, 0.21, 0.21, 1.0),
Vec4(0.21, 0.21, 0.21, 1.0),
];
const Vec4[4] DEFAULT_BORDER_COL = [
Vec4(0.254, 0.254, 0.266, 1.0),
Vec4(0.254, 0.254, 0.266, 1.0),
Vec4(0.254, 0.254, 0.266, 1.0),
Vec4(0.254, 0.254, 0.266, 1.0),
];
const Vec4[4] HL_BORDER_COL = [
Vec4(0.035, 0.549, 0.824, 1.0),
Vec4(0.035, 0.549, 0.824, 1.0),
Vec4(0.035, 0.549, 0.824, 1.0),
Vec4(0.035, 0.549, 0.824, 1.0),
];
const Vec4[4] LC_COLOR = [
Vec4(0.12, 0.12, 0.12, 1.0),
Vec4(0.12, 0.12, 0.12, 1.0),
Vec4(0.12, 0.12, 0.12, 1.0),
Vec4(0.12, 0.12, 0.12, 1.0),
];
const Vec4[4] LC_HL_COLOR = [
Vec4(0.012, 0.176, 0.29, 1.0),
Vec4(0.012, 0.176, 0.29, 1.0),
Vec4(0.012, 0.176, 0.29, 1.0),
Vec4(0.012, 0.176, 0.29, 1.0),
];
const Vec4[4] CMD_PALETTE_INPUT_COL = [
Vec4(0.14, 0.14, 0.14, 1.0),
Vec4(0.14, 0.14, 0.14, 1.0),
Vec4(0.17, 0.17, 0.17, 1.0),
Vec4(0.17, 0.17, 0.17, 1.0),
];
const Vec4[4] CMD_PALETTE_INPUT_HL = [
Vec4(0.24, 0.45, 0.81, 1.0),
Vec4(0.24, 0.45, 0.81, 1.0),
Vec4(0.24, 0.45, 0.81, 1.0),
Vec4(0.24, 0.45, 0.81, 1.0),
];
const Vec4[4] CMD_INPUT_BORDER_COL = [
Vec4(0.003, 0.48, 0.68, 1.0),
Vec4(0.003, 0.48, 0.68, 1.0),
Vec4(0.003, 0.48, 0.68, 1.0),
Vec4(0.003, 0.48, 0.68, 1.0),
];
const Vec4[4] CMD_INPUT_BORDER_HL = [
Vec4(0.05, 0.56, 0.76, 1.0),
Vec4(0.05, 0.56, 0.76, 1.0),
Vec4(0.05, 0.56, 0.76, 1.0),
Vec4(0.05, 0.56, 0.76, 1.0),
];
// 9, 141, 211
const UIPanel g_ui_nil_panel; const UIPanel g_ui_nil_panel;
UIPanel* g_UI_NIL_PANEL; UIPanel* g_UI_NIL_PANEL;
@ -97,13 +162,14 @@ Panel(UIPanel* panel, UIFlags flags = UIF.None)
Push!("color")(DEFAULT_COL); Push!("color")(DEFAULT_COL);
Push!("layout_axis")(panel.axis); Push!("layout_axis")(panel.axis);
UIItem* item = Get(panel.id); UIItem* item = Get(panel.id);
UIItem* separator = g_UI_NIL; UIItem* separator = g_UI_NIL;
UIPanel* parent_pn = panel.parent; UIPanel* parent_pn = panel.parent;
UIItem* prev_panel = !Nil(panel.prev) ? Get(panel.prev.id) : g_UI_NIL; UIItem* next = !Nil(panel.next) ? Get(panel.next.id) : g_UI_NIL;
UIItem* parent = !Nil(parent_pn) ? Get(parent_pn.id) : PeekParent(); UIItem* prev = !Nil(panel.prev) ? Get(panel.prev.id) : g_UI_NIL;
UIItem* parent = !Nil(parent_pn) ? Get(parent_pn.id) : PeekParent();
f32 x_pct = 1.0, y_pct = 1.0; f32 x_pct = 1.0, y_pct = 1.0;
if(!Nil(parent_pn)) if(!Nil(parent_pn))
@ -118,50 +184,84 @@ Panel(UIPanel* panel, UIFlags flags = UIF.None)
} }
f32 adj_x = 0.0, adj_y = 0.0; f32 adj_x = 0.0, adj_y = 0.0;
if(!Nil(prev_panel)) if(!Nil(prev) || !Nil(next))
{ {
Separator(panel, parent, &adj_x, &adj_y); A2D p_axis = parent_pn.axis;
UIS prev_signal = p_axis == A2D.X ? UIS.BorderX0 : UIS.BorderY0;
UIS next_signal = p_axis == A2D.X ? UIS.BorderX1 : UIS.BorderY1;
if(p_axis == A2D.X)
{
flags |= !Nil(prev) ? UIF.BorderX0 : UIF.None;
flags |= !Nil(next) ? UIF.BorderX1 : UIF.None;
}
else
{
flags |= !Nil(prev) ? UIF.BorderY0 : UIF.None;
flags |= !Nil(next) ? UIF.BorderY1 : UIF.None;
}
Signal(item);
f32 p_start = parent.rect.vec0.v[p_axis];
f32 p_end = parent.rect.vec1.v[p_axis];
f32 pos = item.dragged.v[p_axis];
if(!Nil(prev) && item.signal & UIS.DraggedBorder && item.signal & prev_signal && pos != 0.0)
{
f32 pct = Remap(pos, 0.0, p_start-p_end, 0.0, 1.0);
if(CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct))
{
panel.pct -= pct;
panel.prev.pct += pct;
}
}
else if(!Nil(next) && item.signal & UIS.DraggedBorder && item.signal & next_signal && pos != 0.0)
{
f32 pct = Remap(pos, 0.0, p_start-p_end, 0.0, 1.0);
if(CheckPanelBounds(panel.next.pct - pct) && CheckPanelBounds(panel.pct + pct))
{
panel.next.pct -= pct;
panel.pct += pct;
}
}
} }
BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground|flags); BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground|UIF.DragBorder|flags);
PushParent(item); PushParent(item);
Pop!("color")(); Pop!("color", "layout_axis");
Pop!("layout_axis")();
return item; return item;
} }
UIItem* UIItem*
LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_row) LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_row, bool focused)
{ {
Push!("padding")(Vec2(4.0, 0.0)); f32 padding = 4.0;
Push!("color")(Vec4(0.2, 0.5, 0.65, 1.0));
Push!("padding")(Vec2(padding, 0.0));
Push!("color")(focused ? LC_HL_COLOR : LC_COLOR);
u8[] id = ScratchName(parent_id, "linec"); u8[] id = ScratchName(parent_id, "linec");
auto end_chars = end_row.toChars();
u64 width = cast(u64)(end_chars.length);
char[20] ch_buf;
ch_buf.sformat("%%0%sllu%%s", width);
u8[] max_row_text; u8[] max_row_text;
u8[][] line_counts = ScratchAlloc!(u8[])(end_row-start_row); u8[][] line_counts = ScratchAlloc!(u8[])(end_row-start_row);
u64 width = u64(end_row.toChars().length);
if(line_counts.length > 0) if(line_counts.length > 0)
{ {
for(u64 i = 0; start_row+i < end_row; i += 1) for(u64 i = 0; start_row+i < end_row; i += 1)
{ {
line_counts[i] = ScratchAlloc!(u8)(width+parent_id.length); line_counts[i] = ScratchName(width, parent_id, start_row+i);
sprintf(cast(char*)line_counts[i].ptr, ch_buf.ptr, start_row+i, cast(char*)parent_id.ptr);
} }
max_row_text = ScratchAlloc!(u8)(width); max_row_text = ScratchAlloc!(u8)(width);
max_row_text[] = cast(u8)'0'; max_row_text[] = cast(u8)'0';
} }
UISize s_x = UISize(ST.Pixels, CalcTextWidth(max_row_text)); UISize s_x = UISize(ST.Pixels, CalcTextWidth(max_row_text) + (padding*2.0));
UISize s_y = UISize(ST.Percentage, 1.0); UISize s_y = UISize(ST.Percentage, 1.0);
UIItem* item = Container(ScratchName(parent_id, "linec"), s_x, s_y, A2D.Y, UIF.DrawBackground); UIItem* item = Container(ScratchName(parent_id, "linec"), s_x, s_y, A2D.Y, UIF.DrawBackground);
@ -169,18 +269,19 @@ LineCounter(u8[] parent_id, FlatBuffer* buf, f32 offset, i64 start_row, i64 end_
UIItem* inner = Container(ScratchName(parent_id, "lcinner"), s_x, s_y, A2D.Y, UIF.None); UIItem* inner = Container(ScratchName(parent_id, "lcinner"), s_x, s_y, A2D.Y, UIF.None);
{ {
Push!("offset")(Vec2(0.0, offset)); Push!("offset")(Vec2(0.0, offset));
for(u64 i = 0; i < line_counts.length; i += 1) for(u64 i = 0; i < line_counts.length; i += 1)
{ {
UIItem* line = Text(line_counts[i]); UIItem* line = Text(line_counts[i]);
} }
Pop!("offset")(); Pop!("offset")();
} }
EndContainer(); EndContainer();
} }
EndContainer(); EndContainer();
Pop!("padding")(); Pop!("padding", "color");
Pop!("color")();
return item; return item;
} }
@ -194,7 +295,7 @@ Container(u8[] text, UISize size_x, UISize size_y, Axis2D axis, UIF flags = UIF.
BuildItem(item, size_x, size_y, flags); BuildItem(item, size_x, size_y, flags);
PushParent(item); PushParent(item);
Pop!("layout_axis")(); Pop!("layout_axis");
return item; return item;
} }
@ -214,61 +315,6 @@ Text(u8[] text)
return item; return item;
} }
void
Separator(UIPanel* panel, UIItem* parent, f32* adj_x, f32* adj_y)
{
Push!("color")(Vec4(0.1, 0.2, 0.6, 1.0));
Axis2D axis = parent.layout_axis;
f32 sep_y = 1.0, sep_x = 1.0, parent_start = 0.0, parent_end = 0.0;
if(axis == A2D.X)
{
sep_x = 5.0;
*adj_x = -5.0;
parent_start = parent.rect.vec0.x;
parent_end = parent.rect.vec1.x;
}
else
{
sep_y = 5.0;
*adj_y = -5.0;
parent_start = parent.rect.vec0.y;
parent_end = parent.rect.vec1.y;
}
u8[] buf = ScratchAlloc!(u8)(panel.id.length + 5);
(cast(char[])buf).sformat("%s_sep", cast(char[])panel.id);
SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage;
SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage;
UIItem* item = Get(buf);
BuildItem(item, UISize(x_t, sep_x), UISize(y_t, sep_y), UIF.DrawBackground|UIF.Draggable);
Signal(item);
f32 pos = item.dragged.v[parent.layout_axis];
if(item.signal & UIS.Dragged && pos != 0.0)
{
f32 pct = Remap(pos, 0.0, parent_start-parent_end, 0.0, 1.0);
if(CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct))
{
panel.pct -= pct;
panel.prev.pct += pct;
}
}
Pop!("color");
}
void
TextLine(u8[] text)
{
UIItem* parent = PeekParent();
UIItem* item = Get(text);
}
void void
SetPanelScroll(UIPanel* panel, i64 rows, f32 text_size) SetPanelScroll(UIPanel* panel, i64 rows, f32 text_size)
{ {
@ -316,8 +362,13 @@ EditorView(UIPanel* panel)
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Editor* ed = panel.ed; Editor* ed = panel.ed;
UIItem* item = Panel(panel, UIF.Clickable); Push!("border_thickness")(2.0);
Push!("border_color")(focused ? HL_BORDER_COL : DEFAULT_BORDER_COL);
UIItem* item = Panel(panel, UIF.Clickable|UIF.DeferredBorder);
{ {
Pop!("border_thickness", "border_color");
Container(ScratchName(panel.id, "cntr"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.X); Container(ScratchName(panel.id, "cntr"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.X);
{ {
f32 text_size = 16.0; f32 text_size = 16.0;
@ -362,11 +413,12 @@ EditorView(UIPanel* panel)
GetLines(&ed.buf, &ed.linebufs, rows); GetLines(&ed.buf, &ed.linebufs, rows);
} }
LineCounter(panel.id, &panel.ed.buf, offset, line_offset, line_offset+line_rows); LineCounter(panel.id, &panel.ed.buf, offset, line_offset, line_offset+line_rows, focused);
Container(ScratchName(panel.id, "lines"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.Y); Container(ScratchName(panel.id, "lines"), UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0), A2D.Y);
{ {
Push!("offset")(Vec2(0.0, offset)); Push!("offset")(Vec2(0.0, offset));
U64Vec2 pos = VecPos(&ed.buf); U64Vec2 pos = VecPos(&ed.buf);
u64 i = 0; u64 i = 0;
for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1) for(LineBuffer* buf = ed.linebufs.first; buf != null; buf = buf.next, i += 1)
@ -396,8 +448,6 @@ EditorView(UIPanel* panel)
} }
EndPanel(); EndPanel();
Signal(item);
if(item.signal & UIS.Clicked) if(item.signal & UIS.Clicked)
{ {
SetFocusedPanel(panel); SetFocusedPanel(panel);
@ -461,20 +511,19 @@ WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no)
} }
UIItem* UIItem*
TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 text_size, Vec4 bg_col) TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 h, f32 text_size, Vec4[4] bg_cols, Vec4[4] border_col)
{ {
UIItem* input = Get(ScratchName(text, hash)); UIItem* input = Get(ScratchName(text, hash));
//Logf("%s %s %s", input.rect.vec0.v, input.rect.vec1.v, cast(char[])input.key.text); Push!("padding")(Vec2(6.0, (h-(text_size))*0.5)-2.0);
Push!("offset")(Vec2(x, y)); Push!("offset")(Vec2(x, y));
Push!("padding")(Vec2(2.0)); Push!("border_color")(border_col);
Push!("color")(bg_col); Push!("color")(bg_cols);
Container(ScratchName(hash, "_cntr"), UISize(ST.Percentage, w_pct), UISize(ST.Pixels, text_size), A2D.Y, UIF.DrawBackground); Container(ScratchName(hash, "_cntr"), UISize(ST.Percentage, w_pct), UISize(ST.Pixels, h), A2D.Y, UIF.DrawBackground|UIF.DrawBorder);
Pop!("color"); Pop!("color", "offset", "border_color");
Pop!("offset");
Push!("color")(Vec4(1.0)); Push!("color")(Vec4(1.0));
Push!("text_size")(text_size); Push!("text_size")(text_size);
@ -483,39 +532,52 @@ TextInput(u8[] text, u8[] hash, f32 x, f32 y, f32 w_pct, f32 text_size, Vec4 bg_
EndContainer(); EndContainer();
Pop!("color"); Pop!("color", "padding");
Pop!("padding");
return input; return input;
} }
void void
CommandPalette(u8[] text) CommandPalette(CmdPalette* cmd)
{ {
u8[] text = cmd.buffer[0 .. cmd.icount];
u8[][] options = cmd.opt_strs;
Vec2 size = RootSize(); Vec2 size = RootSize();
f32 x = size.x*0.3; f32 x = size.x*0.15;
f32 y = size.y*0.2; f32 y = size.y*0.1;
f32 w = size.x*0.4; f32 w = size.x*0.7;
f32 h = size.y*0.3; f32 h = size.y*0.8;
Push!("color")(CMD_PALETTE_COL);
UIItem* window = Window(CastStr!(u8)("##cmd_palette"), x, y, w, 40.0*(options.length+1));
Push!("corner_radius")(2.0);
Push!("edge_softness")(0.1);
Push!("border_thickness")(2.0); Push!("border_thickness")(2.0);
Push!("color")(Vec4(0.2, 0.5, 0.65, 1.0));
Push!("border_color")(Vec4(0.08, 0.3, 0.40, 1.0));
Push!("corner_radius")(4.0);
Push!("edge_softness")(0.08);
UIItem* window = Window(CastStr!(u8)("##cmd_palette"), x, y, w, h); UIItem* text_box = TextInput(text, CastStr!(u8)("###cmd_palette_input"), 0.0, 0.0, 1.0, 40.0, 16.0, CMD_PALETTE_INPUT_COL, CMD_INPUT_BORDER_COL);
UIItem* text_box = TextInput(text, CastStr!(u8)("###cmd_palette_input"), w*0.1, y*0.2, 0.8, 16.0, Vec4(Vec3(0.0), 1.0)); for(u64 i = 0; i < options.length; i += 1)
{
TextInput(
options[i],
ScratchName("###cmd%04s", 10, i),
0.0,
0.0,
1.0,
40.0,
16.0,
i == cmd.selected ? CMD_PALETTE_INPUT_HL : CMD_PALETTE_INPUT_COL,
i == cmd.selected ? CMD_INPUT_BORDER_HL : CMD_INPUT_BORDER_COL
);
}
EndWindow(); EndWindow();
Pop!("border_thickness"); Pop!("border_thickness", "corner_radius", "edge_softness", "color");
Pop!("border_color");
Pop!("corner_radius");
Pop!("edge_softness");
Pop!("color");
} }
UIItem* UIItem*
@ -525,7 +587,7 @@ Window(u8[] id, f32 x, f32 y, f32 w, f32 h)
Push!("offset")(Vec2(x, y)); Push!("offset")(Vec2(x, y));
BuildItem(item, UISize(ST.Pixels, x), UISize(ST.Pixels, y), UIF.Window|UIF.DrawBackground|UIF.DrawBorder); BuildItem(item, UISize(ST.Pixels, w), UISize(ST.Pixels, h), UIF.Window|UIF.DrawBackground|UIF.DrawBorder);
Pop!("offset"); Pop!("offset");

View File

@ -8,11 +8,12 @@
layout (location = 0) flat in uint in_has_texture; layout (location = 0) flat in uint in_has_texture;
layout (location = 1) in struct FragDataIn { layout (location = 1) in struct FragDataIn {
vec4 color; vec4 color;
vec2 uv; vec2 uv;
vec2 dst_pos; vec2 dst_pos;
vec2 dst_center; vec2 dst_center;
vec2 dst_half_size; vec2 dst_half_size;
vec2 sdf_sample_pos;
float corner_radius; float corner_radius;
float softness; float softness;
float raised; float raised;
@ -22,10 +23,9 @@ layout (location = 1) in struct FragDataIn {
layout (location = 0) out vec4 FragColor; layout (location = 0) out vec4 FragColor;
float RoundedRectSDF(vec2 pos, vec2 center, vec2 half_size, float radius) float RectSDF(vec2 pos, vec2 half_size, float radius)
{ {
vec2 dist = (abs(center - pos) - half_size + vec2(radius)); return length(max(abs(pos) - half_size + radius, 0.0)) - radius;
return min(max(dist.x, dist.y), 0.0) + length(max(dist, 0.0)) - radius;
} }
float ToSRGB(float col) float ToSRGB(float col)
@ -48,24 +48,27 @@ void main()
float softness = FD.softness; float softness = FD.softness;
vec2 softness_padding = vec2(max(0, softness*2-1), max(0, softness*2-1)); vec2 softness_padding = vec2(max(0, softness*2-1), max(0, softness*2-1));
float dist = RoundedRectSDF(FD.dst_pos, FD.dst_center, FD.dst_half_size-softness_padding, FD.corner_radius);
//float dist = RoundedRectSDF2(FD.dst_pos, FD.dst_center, vec2(1.0), FD.corner_radius);
float sdf_factor = 1.0 - smoothstep(0, 2*softness, dist);
float border_factor = 1.0; float border_factor = 1.0;
if(FD.border_thickness != 0.0) if(FD.border_thickness > 0.0)
{ {
vec2 interior_half_size = FD.dst_half_size - vec2(FD.border_thickness); float border_sdf = RectSDF(FD.sdf_sample_pos,
FD.dst_half_size - vec2(FD.softness*2.0f) - FD.border_thickness,
max(FD.corner_radius - FD.border_thickness, 0.0f));
border_factor = smoothstep(0.0f, 2.0f * FD.softness, border_sdf);
}
float interior_radius_reduce_f = min(interior_half_size.x/FD.dst_half_size.x, interior_half_size.y/FD.dst_half_size.y); if(border_factor < 0.001f)
{
discard;
}
float interior_corner_radius = FD.corner_radius * interior_radius_reduce_f * interior_radius_reduce_f; float corner_factor = 1.0;
if(FD.corner_radius > 0.0 || FD.softness > 0.0)
float inside_d = RoundedRectSDF(FD.dst_pos, FD.dst_center, interior_half_size-softness_padding, interior_corner_radius); {
float corner_sdf = RectSDF(FD.sdf_sample_pos,
float inside_f = smoothstep(0, 2*softness, inside_d); FD.dst_half_size - vec2(FD.softness * 2.0f),
border_factor = inside_f; FD.corner_radius);
corner_factor = 1.0f - smoothstep(0.0, 2.0f * FD.softness, corner_sdf);
} }
vec4 inv_gamma = vec4(1.4); vec4 inv_gamma = vec4(1.4);
@ -77,8 +80,11 @@ void main()
tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv); tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv);
} }
vec4 color = ToLinear(FD.color); vec4 color = FD.color; // ToLinear(FD.color);
vec4 out_color = color * tex_color * sdf_factor * border_factor; vec4 out_color = color * tex_color;
out_color.a *= corner_factor;
out_color.a *= border_factor;
FragColor = out_color; FragColor = out_color;
} }

View File

@ -22,11 +22,12 @@ layout (location = 13) in uint in_has_texture;
layout (location = 0) flat out uint out_has_texture; layout (location = 0) flat out uint out_has_texture;
layout (location = 1) out struct FragDataOut { layout (location = 1) out struct FragDataOut {
vec4 color; vec4 color;
vec2 uv; vec2 uv;
vec2 dst_pos; vec2 dst_pos;
vec2 dst_center; vec2 dst_center;
vec2 dst_half_size; vec2 dst_half_size;
vec2 sdf_sample_pos;
float corner_radius; float corner_radius;
float softness; float softness;
float raised; float raised;
@ -75,6 +76,9 @@ void main()
in_col_4 in_col_4
); );
vec2 dst_verts_pct = vec2(bool(gl_VertexIndex >> 1) ? 1.0f : 0.0f,
bool(gl_VertexIndex & 1) ? 0.0f : 1.0f);
FragData.color = cols[gl_VertexIndex]; FragData.color = cols[gl_VertexIndex];
FragData.uv = uvs[gl_VertexIndex] / tex_size; FragData.uv = uvs[gl_VertexIndex] / tex_size;
FragData.dst_pos = pos; FragData.dst_pos = pos;
@ -84,6 +88,7 @@ void main()
FragData.softness = edge_softness; FragData.softness = edge_softness;
FragData.raised = raised; FragData.raised = raised;
FragData.border_thickness = border_thickness; FragData.border_thickness = border_thickness;
FragData.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size;
out_has_texture = in_has_texture; out_has_texture = in_has_texture;
vec4 v_pos = PC.projection * vec4(pos.x, pos.y, z_index, 1); vec4 v_pos = PC.projection * vec4(pos.x, pos.y, z_index, 1);