start work on command palette rework, cursor animations
This commit is contained in:
parent
f13daded03
commit
a2c39e320f
@ -67,20 +67,26 @@ struct Ctx
|
||||
UIKey hover_key;
|
||||
UIKey focus_key;
|
||||
|
||||
UIPanel* base_panel;
|
||||
UIPanel* focused_panel;
|
||||
|
||||
u64 last_hover_frame;
|
||||
|
||||
f32 pos_rate;
|
||||
f32 scroll_rate;
|
||||
f32 animation_rate;
|
||||
f32 fade_rate;
|
||||
|
||||
EditState state;
|
||||
u8[128] input_buf;
|
||||
u32 icount;
|
||||
u64 editor_id_incr;
|
||||
Timer timer;
|
||||
CmdPalette cmd;
|
||||
string[] file_names;
|
||||
u64 panel_id;
|
||||
ConfigValue[CO.max] config_values;
|
||||
|
||||
EditState state;
|
||||
u8[128] input_buf;
|
||||
u32 icount;
|
||||
u64 editor_id_incr;
|
||||
Timer timer;
|
||||
CmdPalette cmd;
|
||||
string[] file_names;
|
||||
u64 panel_id;
|
||||
|
||||
alias rd_ctx this;
|
||||
}
|
||||
@ -111,6 +117,8 @@ struct Editor
|
||||
Vec2 select_end;
|
||||
i64 line_offset;
|
||||
i64 line_height;
|
||||
|
||||
alias buf this;
|
||||
}
|
||||
|
||||
struct ChangeStacks
|
||||
@ -130,7 +138,7 @@ struct EditorChange
|
||||
EditorChange* next;
|
||||
}
|
||||
|
||||
alias CmdFn = bool function(Ctx* ctx);
|
||||
alias CmdFn = bool function(Ctx* ctx); // return true when completed
|
||||
|
||||
struct Command
|
||||
{
|
||||
@ -140,28 +148,56 @@ struct Command
|
||||
CmdType type;
|
||||
union
|
||||
{
|
||||
CmdFn fn;
|
||||
|
||||
CmdFn fn;
|
||||
ConfigOpt opt;
|
||||
}
|
||||
}
|
||||
|
||||
struct Parameter
|
||||
union ConfigValue
|
||||
{
|
||||
string value;
|
||||
bool visible;
|
||||
f32 _f32;
|
||||
u64 _u64;
|
||||
Vec4 _vec4;
|
||||
}
|
||||
|
||||
enum ValueType
|
||||
{
|
||||
F32,
|
||||
U64,
|
||||
Vec4,
|
||||
Max,
|
||||
} alias VT = ValueType;
|
||||
|
||||
enum ConfigInputType
|
||||
{
|
||||
Text,
|
||||
Slider,
|
||||
Dropdown,
|
||||
Max,
|
||||
} alias CIT = ConfigInputType;
|
||||
|
||||
struct ConfigInfo
|
||||
{
|
||||
ValueType value_type;
|
||||
ConfigInputType input_type;
|
||||
}
|
||||
|
||||
enum ConfigOpt
|
||||
{
|
||||
CornerRadius,
|
||||
TabWidth,
|
||||
ScrollSpeed,
|
||||
EditorPadding,
|
||||
Max,
|
||||
} alias CO = ConfigOpt;
|
||||
|
||||
enum CmdType
|
||||
{
|
||||
None,
|
||||
OpenFile,
|
||||
SaveFile,
|
||||
CreateFile,
|
||||
VSplit,
|
||||
HSplit,
|
||||
}
|
||||
|
||||
alias CT = CmdType;
|
||||
Config,
|
||||
Callback,
|
||||
Hotkey,
|
||||
} alias CT = CmdType;
|
||||
|
||||
enum EditState
|
||||
{
|
||||
@ -170,14 +206,12 @@ enum EditState
|
||||
CmdPalette,
|
||||
RunCmd,
|
||||
SetPanelFocus, // if moving left/right move up parent tree until one is found with a2d.x, same thing for up/down a2d.y
|
||||
}
|
||||
} alias ES = EditState;
|
||||
|
||||
alias ES = EditState;
|
||||
|
||||
bool
|
||||
CmdModeActive()
|
||||
struct Parameter
|
||||
{
|
||||
return g_ctx.state == ES.CmdPalette;
|
||||
string value;
|
||||
bool visible;
|
||||
}
|
||||
|
||||
__gshared bool g_input_mode = false;
|
||||
@ -193,34 +227,48 @@ const Command[] CMD_LIST = [
|
||||
name: "Open",
|
||||
cmd: "open",
|
||||
desc: "Open a file in the focused editor view.",
|
||||
type: CT.OpenFile,
|
||||
type: CT.Callback,
|
||||
//fn: &NotImplementedCallback,
|
||||
},
|
||||
{
|
||||
name: "Save",
|
||||
cmd: "save",
|
||||
desc: "Save the current file in the focused editor view.",
|
||||
type: CT.SaveFile,
|
||||
type: CT.Callback,
|
||||
},
|
||||
{
|
||||
name: "New File",
|
||||
cmd: "new file",
|
||||
desc: "Create a new file with a specified name and location.",
|
||||
type: CT.CreateFile,
|
||||
type: CT.Callback,
|
||||
},
|
||||
{
|
||||
name: "Vertical Split",
|
||||
cmd: "vsplit",
|
||||
desc: "Split the current editor view vertically.",
|
||||
type: CT.VSplit,
|
||||
type: CT.Callback,
|
||||
},
|
||||
{
|
||||
name: "Horizontal Split",
|
||||
cmd: "hsplit",
|
||||
desc: "Split the current editor view horizontally.",
|
||||
type: CT.HSplit,
|
||||
type: CT.Callback,
|
||||
},
|
||||
];
|
||||
|
||||
bool
|
||||
NotImplementedCallback(Ctx* ctx)
|
||||
{
|
||||
Logf("Not yet implemented!");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CmdModeActive()
|
||||
{
|
||||
return g_ctx.state == ES.CmdPalette;
|
||||
}
|
||||
|
||||
bool
|
||||
Active(EditState state)
|
||||
{
|
||||
@ -245,71 +293,25 @@ Cycle(Inputs* inputs)
|
||||
{
|
||||
ResetScratch(MB(4));
|
||||
|
||||
g_delta = DeltaTime(&g_ctx.timer);
|
||||
|
||||
g_delta = DeltaTime(&g_ctx.timer);
|
||||
g_input_mode = Active(ES.InputMode);
|
||||
|
||||
BeginUI(inputs);
|
||||
|
||||
Ctx* ctx = GetCtx();
|
||||
|
||||
static UIPanel* panel;
|
||||
if(Nil(panel))
|
||||
{
|
||||
panel = MakePanel();
|
||||
panel.pct = 1.0;
|
||||
|
||||
UIPanel* p0 = MakePanel();
|
||||
UIPanel* p1 = MakePanel();
|
||||
UIPanel* p2 = MakePanel();
|
||||
UIPanel* p3 = MakePanel();
|
||||
UIPanel* p4 = MakePanel();
|
||||
UIPanel* p5 = MakePanel();
|
||||
UIPanel* p6 = MakePanel();
|
||||
|
||||
p0.pct = 0.3;
|
||||
p1.pct = 0.7;
|
||||
p0.parent = panel;
|
||||
p1.parent = panel;
|
||||
|
||||
DLLPush(panel, p0, g_NIL_PANEL);
|
||||
DLLPush(panel, p1, g_NIL_PANEL);
|
||||
|
||||
p0.axis = A2D.Y;
|
||||
|
||||
p2.pct = 0.25;
|
||||
p3.pct = 0.35;
|
||||
p4.pct = 0.40;
|
||||
p2.parent = p3.parent = p4.parent = p0;
|
||||
|
||||
DLLPush(p0, p2, g_NIL_PANEL);
|
||||
DLLPush(p0, p3, g_NIL_PANEL);
|
||||
DLLPush(p0, p4, g_NIL_PANEL);
|
||||
|
||||
p5.pct = 0.2;
|
||||
p6.pct = 0.8;
|
||||
p5.parent = p6.parent = p4;
|
||||
|
||||
DLLPush(p4, p5, g_NIL_PANEL);
|
||||
DLLPush(p4, p6, g_NIL_PANEL);
|
||||
|
||||
p1.ed = CreateEditor();
|
||||
p2.ed = CreateEditor();
|
||||
p3.ed = CreateEditor();
|
||||
p5.ed = CreateEditor();
|
||||
p6.ed = CreateEditor();
|
||||
|
||||
OpenFile(p1.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h");
|
||||
OpenFile(p3.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h");
|
||||
OpenFile(p5.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h");
|
||||
OpenFile(p6.ed, "./src/VulkanRenderer/external/vma/vk_mem_alloc.h");
|
||||
}
|
||||
|
||||
Panel(ctx, panel);
|
||||
Panel(ctx, ctx.base_panel);
|
||||
|
||||
EndUI();
|
||||
}
|
||||
|
||||
void
|
||||
Focus(UIPanel* panel)
|
||||
{
|
||||
g_ctx.focused_panel = panel;
|
||||
g_ctx.focus_key = panel.key;
|
||||
}
|
||||
|
||||
void
|
||||
InitCtx(PlatformWindow* window)
|
||||
{
|
||||
@ -320,12 +322,12 @@ InitCtx(PlatformWindow* window)
|
||||
ctx.cmd.arena = CreateArena(MB(1));
|
||||
ctx.cmd.cmd_arena = CreateArena(MB(1));
|
||||
ctx.cmd.buffer = Alloc!(u8)(1024);
|
||||
//ctx.base_panel = CreatePanel(CreateEditor());
|
||||
ctx.timer = CreateTimer();
|
||||
|
||||
InitUI(ctx);
|
||||
|
||||
//FocusEditor(ctx.base_panel);
|
||||
ctx.base_panel = CreatePanel(CreateEditor());
|
||||
Focus(ctx.base_panel);
|
||||
|
||||
if(getcwd() != "/")
|
||||
{
|
||||
@ -664,13 +666,14 @@ MovePanelFocus(A2D axis, bool prev)(UIPanel* panel)
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focused)
|
||||
{
|
||||
Editor* ed = p.ed;
|
||||
Ctx* ctx = &g_ctx;
|
||||
FlatBuffer* fb = &ed.buf;
|
||||
u8[] cb_text;
|
||||
u64 initial_len = p.ed.buf.length;
|
||||
u8[] cb_text;
|
||||
|
||||
for(auto node = inputs.first; node != null; node = node.next)
|
||||
{
|
||||
@ -808,6 +811,8 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
|
||||
{
|
||||
Insert(fb, cb_text, cb_text.length);
|
||||
}
|
||||
|
||||
return initial_len != p.ed.buf.length;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
144
src/editor/ui.d
144
src/editor/ui.d
@ -168,8 +168,8 @@ struct Style
|
||||
Vec4 border_col;
|
||||
Vec4 border_hl_col;
|
||||
Vec4 corner_radius;
|
||||
f32 border_thickness;
|
||||
f32 edge_softness;
|
||||
f32 border_thickness = 0.0;
|
||||
f32 edge_softness = 0.0;
|
||||
}
|
||||
|
||||
Style SEP_STYLE = {
|
||||
@ -177,6 +177,11 @@ Style SEP_STYLE = {
|
||||
hl_col: Vec4(0.2, 0.2, 0.2, 1.0),
|
||||
};
|
||||
|
||||
Style CURSOR_STYLE = {
|
||||
col: Vec4(1.0),
|
||||
hl_col: Vec4(1.0),
|
||||
};
|
||||
|
||||
Style PANEL_STYLE = {
|
||||
col: BG_COL,
|
||||
hl_col: BG_COL,
|
||||
@ -200,13 +205,13 @@ Style CMD_STYLE = {
|
||||
enum UIFlags
|
||||
{
|
||||
None,
|
||||
Ready = 1<<0,
|
||||
Hot = 1<<1,
|
||||
Active = 1<<2,
|
||||
Click = 1<<3,
|
||||
Drag = 1<<4,
|
||||
Priority = 1<<5,
|
||||
TargetLeniency = 1<<6,
|
||||
Ready = 1<<0,
|
||||
Hot = 1<<1,
|
||||
Active = 1<<2,
|
||||
Click = 1<<3,
|
||||
Drag = 1<<4,
|
||||
Priority = 1<<5,
|
||||
TargetLeniency = 1<<6,
|
||||
} alias UIF = UIFlags;
|
||||
|
||||
struct UIItem
|
||||
@ -218,6 +223,7 @@ struct UIItem
|
||||
Vec2 scroll_offset;
|
||||
Vec2 scroll_target;
|
||||
Vec2 scroll_size;
|
||||
Vec2 target_pos;
|
||||
Axis2D axis;
|
||||
u64 last_frame;
|
||||
f32 ready_t; // Item visible
|
||||
@ -274,6 +280,7 @@ struct UIPanel
|
||||
f32 pct;
|
||||
u32 id;
|
||||
Editor* ed;
|
||||
bool move_text_with_cursor;
|
||||
|
||||
alias item this;
|
||||
}
|
||||
@ -525,6 +532,25 @@ EndScissor(Ctx* ctx)
|
||||
ResetScissor(&ctx.rd);
|
||||
}
|
||||
|
||||
void
|
||||
AnimatePos(UIItem* item)
|
||||
{
|
||||
if(item.p0 != item.target_pos)
|
||||
{
|
||||
item.p0 += g_ctx.pos_rate * (item.target_pos - item.p0);
|
||||
|
||||
static foreach(i; 0 .. 2)
|
||||
{
|
||||
if(fabsf(item.p0.v[i] - item.target_pos.v[i]) < 2.0f)
|
||||
{
|
||||
item.p0.v[i] = item.target_pos.v[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.p1 = item.p0+item.size;
|
||||
}
|
||||
|
||||
void
|
||||
Panel(Ctx* ctx, UIPanel* panel)
|
||||
{
|
||||
@ -560,6 +586,11 @@ Panel(Ctx* ctx, UIPanel* panel)
|
||||
FontGlyphs* fg = GetFontGlyphs(12);
|
||||
FontAtlasBuf* abuf = &fg.abuf;
|
||||
|
||||
// Inputs
|
||||
bool hovered = ctx.hover_key == panel.key;
|
||||
bool focused = ctx.focus_key == panel.key && ctx.focused_panel == panel;
|
||||
panel.move_text_with_cursor |= HandleInputs(panel, &ctx.events, hovered, focused);
|
||||
|
||||
f32 lheight = abuf.atlas.line_height;
|
||||
panel.scroll_target.y = cast(f32)(panel.ed.line_offset)*lheight;
|
||||
|
||||
@ -590,8 +621,8 @@ Panel(Ctx* ctx, UIPanel* panel)
|
||||
|
||||
for(u64 i = start_ln; i < max_ln; i += 1)
|
||||
{
|
||||
string line_num = Scratchf("%s", i);
|
||||
DrawText(ctx, line_num, fg, Vec4(1.0), text_rect, TA.Right);
|
||||
string line_num = Scratchf("%s", i+1);
|
||||
DrawText(ctx, line_num, Vec4(1.0), fg, text_rect, TA.Right);
|
||||
text_rect.p0.y += lheight;
|
||||
}
|
||||
|
||||
@ -608,20 +639,78 @@ Panel(Ctx* ctx, UIPanel* panel)
|
||||
text_rect = Pad(rects[1], PAD);
|
||||
text_rect.p0.y += offset;
|
||||
|
||||
u64 i = start_ln;
|
||||
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
|
||||
I64Vec2 cursor_pos = VecPos(&panel.ed.buf);
|
||||
UIItem* cursor = MakeItem("###cursor_%s", panel.id);
|
||||
if(ctx.focused_panel == panel)
|
||||
{
|
||||
DrawText(ctx, Str(lbuf.text), fg, cast(u8[])lbuf.style, text_rect, TA.Left);
|
||||
text_rect.p0.y += lheight;
|
||||
i64 y_pos = cursor_pos.y - panel.ed.line_offset;
|
||||
|
||||
cursor.size.x = Active(ES.InputMode) ? 2.0 : fg.abuf.atlas.max_advance;
|
||||
cursor.size.y = lheight;
|
||||
|
||||
cursor.target_pos.y = text_rect.p0.y + (y_pos*lheight);
|
||||
cursor.target_pos.x = text_rect.p0.x + (cursor_pos.x*fg.abuf.atlas.max_advance);
|
||||
|
||||
AnimatePos(cursor);
|
||||
|
||||
if(cursor.p0 == cursor.target_pos)
|
||||
{
|
||||
panel.move_text_with_cursor = false;
|
||||
}
|
||||
|
||||
DrawRect(ctx, cursor.rect, &CURSOR_STYLE);
|
||||
}
|
||||
|
||||
u64 i = start_ln;
|
||||
|
||||
if(ctx.focused_panel == panel)
|
||||
{
|
||||
if(Active(ES.InputMode) && panel.move_text_with_cursor)
|
||||
{
|
||||
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
|
||||
{
|
||||
if(i == cursor_pos.y)
|
||||
{
|
||||
string str = Str(lbuf.text[0 .. cursor_pos.x]);
|
||||
u8[] tks = cast(u8[])lbuf.style[0 .. cursor_pos.x];
|
||||
|
||||
DrawText(ctx, str, tks, fg, text_rect, TA.Left);
|
||||
|
||||
Rect split_rect = text_rect;
|
||||
split_rect.p0.x = cursor.p0.x;
|
||||
|
||||
str = Str(lbuf.text[cursor_pos.x .. $]);
|
||||
tks = cast(u8[])lbuf.style[cursor_pos.x .. $];
|
||||
|
||||
DrawText(ctx, str, tks, fg, split_rect, TA.Left);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left);
|
||||
}
|
||||
|
||||
text_rect.p0.y += lheight;
|
||||
}
|
||||
}
|
||||
else if(!Active(ES.InputMode))
|
||||
{
|
||||
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
|
||||
{
|
||||
DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left, cursor_pos.y == i ? cursor_pos.x : -1);
|
||||
text_rect.p0.y += lheight;
|
||||
}
|
||||
}
|
||||
else for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
|
||||
{
|
||||
DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left);
|
||||
text_rect.p0.y += lheight;
|
||||
}
|
||||
}
|
||||
|
||||
ResetBuffer(&panel.ed.buf);
|
||||
|
||||
EndScissor(ctx);
|
||||
DrawBorder(ctx, rects[1], &style);
|
||||
|
||||
// Inputs
|
||||
HandleInputs(panel, &ctx.events, ctx.hover_key == panel.key, ctx.focus_key == panel.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -773,6 +862,10 @@ CreatePanel(Editor* ed)
|
||||
|
||||
p.id = panel_id++;
|
||||
p.item = MakeItem!(PANEL_FLAGS)("###panel_%s", p.id);
|
||||
p.ed = ed;
|
||||
p.pct = 1.0;
|
||||
|
||||
p.first = p.last = p.next = p.prev = p.parent = g_NIL_PANEL;
|
||||
|
||||
return p;
|
||||
}
|
||||
@ -1010,6 +1103,7 @@ BeginUI(Inputs* inputs)
|
||||
ctx.animation_rate = 1.0 - pow(2.0, (-30.0f * g_delta));
|
||||
ctx.fade_rate = 1.0 - pow(2.0, (-50.0f * g_delta));
|
||||
ctx.scroll_rate = 1.0 - pow(2.0, (-60.0f * g_delta));
|
||||
ctx.pos_rate = 1.0 - pow(2.0, (-90.0f * g_delta));
|
||||
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
@ -1252,7 +1346,7 @@ enum TextAlign
|
||||
} alias TA = TextAlign;
|
||||
|
||||
void
|
||||
DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, TextAlign text_align, f32 ready = 1.0)
|
||||
DrawText(T, U)(Ctx* ctx, string text, U col_param, T size_param, Rect rect, TextAlign text_align, i64 hl_char = -1, f32 ready = 1.0)
|
||||
if((is(T == FontGlyphs*) || (is(T: u32)) && is(U == Vec4) || is(U == u8[])))
|
||||
{
|
||||
static if(is(T == FontGlyphs*))
|
||||
@ -1293,6 +1387,10 @@ DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, Text
|
||||
u8 ch = text[j];
|
||||
Glyph* g = ch < glyphs.length ? glyphs.ptr + ch : null;
|
||||
Vec4 col = SYNTAX_COLORS[tks[j]];
|
||||
if(hl_char == j)
|
||||
{
|
||||
col = InvertCol(col);
|
||||
}
|
||||
|
||||
AnimateReady(ready, &col);
|
||||
DrawGlyph(rect, g, &x, rect.p0.y, line_height, col);
|
||||
@ -1307,11 +1405,17 @@ DrawText(T, U)(Ctx* ctx, string text, T size_param, U col_param, Rect rect, Text
|
||||
{
|
||||
u8 ch = text[j];
|
||||
Glyph* g = ch < glyphs.length ? glyphs.ptr + ch : null;
|
||||
DrawGlyph(rect, g, &x, rect.p0.y, line_height, col);
|
||||
DrawGlyph(rect, g, &x, rect.p0.y, line_height, hl_char == j ? InvertCol(col) : col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline) Vec4
|
||||
InvertCol(Vec4 col)
|
||||
{
|
||||
return Vec4(1.0-col.r, 1.0-col.g, 1.0-col.b, col.a);
|
||||
}
|
||||
|
||||
void
|
||||
BeginScissor(Ctx* ctx, UIItem* item, bool scissor_x = true, bool scissor_y = true)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user