fix memory leaks and non-cached box interactions

rust-rewrite
Patrick Cleavelin 2024-02-15 15:06:02 -06:00
parent c955a2621b
commit 187f48aa87
6 changed files with 200 additions and 40 deletions

View File

@ -1,8 +1,8 @@
all: editor
editor: src/*.odin grep odin_highlighter
odin build src/ -out:bin/editor.o -build-mode:obj -debug
dsymutil bin/editor.o -o bin/editor.dw
odin build src/ -out:bin/editor.o -build-mode:obj -debug -lld
dsymutil bin/editor.o -o bin/editor.dSYM
odin build src/ -out:bin/editor -lld
odin_highlighter:

Binary file not shown.

View File

@ -271,7 +271,6 @@ pub extern "C" fn OnExit(_plugin: Plugin) {
extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) {
let window = Box::leak(unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) });
let screen_width = (plugin.get_screen_width)() as i32;
let screen_height = (plugin.get_screen_height)() as i32;
let font_height = (plugin.get_font_height)() as i32;

View File

@ -8,6 +8,12 @@ local SideBarSmoothedWidth = 128
local SideBarWidth = 128
local SideBarClosed = false
local ActiveCodeView = nil
local CodeViews = {}
local MovingTab = nil
local MovingTabDest = nil
function buffer_list_iter()
local idx = 0
return function ()
@ -36,10 +42,28 @@ function lerp(from, to, rate)
return (1 - rate) * from + rate*to
end
function ui_sidebar(ctx)
SideBarSmoothedWidth = slerp(SideBarSmoothedWidth, SideBarWidth, 0.3)
function add_buffer_to_code_view(code_view_index, file_path, buffer_index)
if code_view_index == nil then
code_view_index = 1
ActiveCodeView = 1
end
tabs = UI.push_rect(ctx, "sidebar", false, false, UI.Vertical, UI.Exact(SideBarSmoothedWidth), UI.Fill)
if CodeViews[code_view_index] == nil then
CodeViews[code_view_index] = {}
CodeViews[code_view_index].tabs = {}
end
ActiveCodeView = code_view_index
CodeViews[code_view_index].tabs[file_path] = {}
CodeViews[code_view_index].tabs[file_path].buffer_index = buffer_index
CodeViews[code_view_index].current_tab = file_path
end
function ui_sidebar(ctx)
SideBarSmoothedWidth = lerp(SideBarSmoothedWidth, SideBarWidth, 0.3)
tabs = UI.push_rect(ctx, "for some reason it chooses this as the parent", false, false, UI.Vertical, UI.Exact(SideBarSmoothedWidth), UI.Fill)
UI.push_parent(ctx, tabs)
UI.push_rect(ctx, "padded top open files", false, false, UI.Horizontal, UI.Fill, UI.Exact(8))
UI.push_parent(ctx, UI.push_rect(ctx, "padded open files", false, false, UI.Horizontal, UI.Fill, UI.ChildrenSum))
@ -58,11 +82,13 @@ function ui_sidebar(ctx)
if UI.advanced_button(ctx, " x ", flags, UI.FitText, UI.FitText).clicked then
print("hahah, you can't close buffers yet silly")
add_buffer_to_code_view(ActiveCodeView+1, buffer_info.file_path, i)
end
tab_button_interaction = UI.advanced_button(ctx, " "..buffer_info.file_path.." ", flags, UI.Fill, UI.FitText)
if tab_button_interaction.clicked then
Editor.set_current_buffer_from_index(i)
add_buffer_to_code_view(ActiveCodeView, buffer_info.file_path, i)
end
if tab_button_interaction.hovering then
CurrentPreviewBufferIndex = i
@ -74,12 +100,75 @@ function ui_sidebar(ctx)
UI.pop_parent(ctx)
end
function ui_tabs(ctx)
UI.buffer(ctx, CurrentPreviewBufferIndex)
function ui_code_view(ctx, code_view_index)
local code_view = CodeViews[code_view_index]
local is_tab_dest = MovingTab ~= nil and ActiveCodeView ~= code_view_index
UI.push_parent(ctx, UI.push_rect(ctx, code_view_index.." code view", ActiveCodeView ~= code_view_index, true, UI.Vertical, UI.Fill, UI.Fill))
if is_tab_dest then
tab_dest_region = UI.push_box(ctx, "code view tab dest", {"Hoverable"}, UI.Vertical, UI.Fill, UI.Fill)
tab_dest_interaction = UI.box_interaction(ctx, tab_dest_region)
UI.push_parent(ctx, tab_dest_region)
-- if tab_dest_interaction
end
UI.push_parent(ctx, UI.push_rect(ctx, "tabs", false, true, UI.Horizontal, UI.Fill, UI.ChildrenSum))
for k,v in pairs(code_view.tabs) do
show_border = v["buffer_index"] ~= code_view.current_buffer_index
background = not show_border
flags = {"Clickable", "Hoverable", "DrawText"}
UI.push_parent(ctx, UI.push_rect(ctx, k.." tab container", background, show_border, UI.Horizontal, UI.ChildrenSum, UI.ChildrenSum))
tab_button = UI.advanced_button(ctx, " "..k.." ", flags, UI.FitText, UI.Exact(32))
if tab_button.clicked then
ActiveCodeView = code_view_index
code_view.current_tab = k
end
local bb = "false"
if is_tab_dest then bb = "true" end
-- print("our code view "..code_view_index.." - "..k.." - is tab dest "..bb)
if tab_button.dragging then
if MovingTab == nil then
MovingTab = {}
MovingTab["code_view_index"] = code_view_index
MovingTab["tab"] = k
end
UI.push_parent(ctx, UI.push_floating(ctx, "dragging tab", x-(96/2), y-(32/2)))
UI.advanced_button(ctx, " "..k.." ", flags, UI.FitText, UI.Exact(32))
UI.pop_parent(ctx)
elseif MovingTab ~= nil and MovingTab["code_view_index"] == code_view_index and MovingTab["tab"] == k then
-- Editor.quit()
--print("attempting to move tab "..MovingTab["code_view_index"].." - "..MovingTab["tab"])
if MovingTabDest ~= nil then
print("attempting to place tab at code view "..MovingTabDest.code_view_index)
MovingTabDest = nil
end
MovingTab = nil
end
UI.pop_parent(ctx)
end
UI.pop_parent(ctx)
current_tab = code_view.current_tab
buffer_index = code_view.tabs[current_tab].buffer_index
UI.buffer(ctx, buffer_index)
if is_tab_dest then
UI.pop_parent(ctx)
end
UI.pop_parent(ctx)
end
function render_ui_window(ctx)
current_buffer_index = Editor.get_current_buffer_index()
x,y = UI.get_mouse_pos(ctx)
numFrames = 7
CurrentPreviewBufferIndex = current_buffer_index
@ -88,7 +177,6 @@ function render_ui_window(ctx)
ui_sidebar(ctx)
end
if UI.advanced_button(ctx, "side bar grab handle", {"DrawBorder", "Hoverable"}, UI.Exact(16), UI.Fill).dragging then
x,y = UI.get_mouse_pos(ctx)
SideBarWidth = x-8
if SideBarWidth < 32 then
@ -104,7 +192,10 @@ function render_ui_window(ctx)
end
end
ui_tabs(ctx)
for k in ipairs(CodeViews) do
ui_code_view(ctx, k)
end
render_buffer_search(ctx)
end

View File

@ -1030,6 +1030,16 @@ main :: proc() {
bbb: [^]lua.L_Reg;
editor_lib := [?]lua.L_Reg {
lua.L_Reg {
"quit",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
state.should_close = true;
return i32(lua.OK);
}
},
lua.L_Reg {
"print",
proc "c" (L: ^lua.State) -> i32 {
@ -1337,6 +1347,50 @@ main :: proc() {
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"push_box",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx != nil {
label := lua.L_checkstring(L, 2);
flags, err := lua_ui_flags(L, 3);
axis := ui.Axis(lua.L_checkinteger(L, 4));
semantic_width := get_lua_semantic_size(L, 5);
semantic_height := get_lua_semantic_size(L, 6);
box := ui.push_box(ui_ctx, strings.clone(string(label), context.temp_allocator), flags, axis, { semantic_width, semantic_height });
lua.pushlightuserdata(L, box);
return 1;
}
return i32(lua.ERRRUN);
}
},
lua.L_Reg {
"box_interaction",
proc "c" (L: ^lua.State) -> i32 {
context = state.ctx;
lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 1);
ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1);
if ui_ctx == nil { return i32(lua.ERRRUN); }
lua.L_checktype(L, 2, i32(lua.TLIGHTUSERDATA));
lua.pushvalue(L, 2);
box := transmute(^ui.Box)lua.touserdata(L, -1);
if box == nil { return i32(lua.ERRRUN); }
interaction := ui.test_box(ui_ctx, box);
push_lua_box_interaction(L, interaction)
return 1;
}
},
lua.L_Reg {
"push_rect",
proc "c" (L: ^lua.State) -> i32 {
@ -1888,8 +1942,6 @@ main :: proc() {
}
}
// ui.debug_print();
draw(&StateWithUi { &state, &ui_context });
ui.prune(&ui_context);

View File

@ -11,7 +11,7 @@ import "../theme"
Context :: struct {
root: ^Box,
current_parent: ^Box,
persistent: map[Key]^Box,
persistent: map[string]^Box,
current_interaction_index: int,
clips: [dynamic]Rect,
@ -103,24 +103,26 @@ Box :: struct {
init :: proc(renderer: ^sdl2.Renderer) -> Context {
root := new(Box);
root.key = gen_key(nil, "root", 69);
root.label = strings.clone("root")
return Context {
root = root,
current_parent = root,
persistent = make(map[Key]^Box),
persistent = make(map[string]^Box),
clips = make([dynamic]Rect),
renderer = renderer,
};
}
gen_key :: proc(ctx: ^Context, label: string, value: int) -> Key {
key_label := ""
key_label: string;
if ctx != nil && (ctx.current_parent == nil || len(ctx.current_parent.key.label) < 1) {
key_label = label;
key_label = strings.clone(label);
} else if ctx != nil {
key_label = fmt.tprintf("%s:%s", ctx.current_parent.key.label, label);
key_label = fmt.aprintf("%s:%s", ctx.current_parent.key.label, label);
} else {
key_label = fmt.tprintf("%s",label);
key_label = strings.clone(label);
}
return Key {
@ -133,23 +135,30 @@ gen_key :: proc(ctx: ^Context, label: string, value: int) -> Key {
make_box :: proc(ctx: ^Context, key: Key, label: string, flags: bit_set[Flag], axis: Axis, semantic_size: [2]SemanticSize) -> ^Box {
box: ^Box = nil;
if cached_box, exists := ctx.persistent[key]; exists {
if cached_box.last_interacted_index < ctx.current_interaction_index {
old_cached_box := ctx.persistent[key];
free(old_cached_box);
box = new(Box);
if cached_box, exists := ctx.persistent[key.label]; exists {
// NOTE(pcleavelin): its important to note that the _cached_ key _is not_ free'd
// as that would invalid the maps reference to the key causing memory leaks because
// the map would think that an entry doesn't exist (in some cases)
delete(key.label)
ctx.persistent[key] = box;
if cached_box.last_interacted_index < ctx.current_interaction_index-1 {
box = cached_box;
box.last_interacted_index = ctx.current_interaction_index;
box.hot = 0;
box.active = 0;
} else {
box = cached_box;
}
} else {
box = new(Box);
ctx.persistent[key] = box;
ctx.persistent[key.label] = box;
box.key = key;
box.last_interacted_index = ctx.current_interaction_index;
}
box.key = key;
box.label = label;
box.label = strings.clone(label, context.temp_allocator);
box.first = nil;
box.last = nil;
@ -206,8 +215,8 @@ Fill :[2]SemanticSize: {
}
};
push_box :: proc(ctx: ^Context, label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText, value: int = 0) -> ^Box {
key := gen_key(ctx, label, value);
push_box :: proc(ctx: ^Context, label: string, flags: bit_set[Flag], axis: Axis = .Horizontal, semantic_size: [2]SemanticSize = FitText) -> ^Box {
key := gen_key(ctx, label, 0);
box := make_box(ctx, key, label, flags, axis, semantic_size);
return box;
@ -246,6 +255,9 @@ test_box :: proc(ctx: ^Context, box: ^Box) -> Interaction {
box.active = 0;
}
if box.hot > 0 || box.active > 0 {
box.last_interacted_index = ctx.current_interaction_index;
}
return Interaction {
hovering = hovering || box.active > 0,
clicked = hovering && mouse_is_clicked,
@ -264,7 +276,11 @@ delete_box_children :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = tr
delete_box :: proc(ctx: ^Context, box: ^Box, keep_persistent: bool = true) {
delete_box_children(ctx, box, keep_persistent);
if !(box.key in ctx.persistent) || !keep_persistent {
if box.last_interacted_index < ctx.current_interaction_index-1 {
delete_key(&ctx.persistent, box.key.label)
}
if !(box.key.label in ctx.persistent) || !keep_persistent {
delete(box.key.label);
free(box);
}
@ -276,21 +292,19 @@ prune :: proc(ctx: ^Context) {
for box in iterate_box(&iter) {
delete_box_children(ctx, box);
if !(box.key in ctx.persistent) {
if !(box.key.label in ctx.persistent) && box != ctx.root {
free(box);
}
}
computed_pos := ctx.root.computed_pos;
computed_size := ctx.root.computed_size;
root_key := ctx.root.key;
ctx.root.first = nil;
ctx.root.last = nil;
ctx.root.next = nil;
ctx.root.prev = nil;
ctx.root.parent = nil;
ctx.current_parent = ctx.root;
ctx.current_interaction_index += 1;
}
// TODO: consider not using `ctx` here
@ -335,7 +349,7 @@ compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font
}
if .Floating in box.flags {
box.computed_pos = {0,0};
// box.computed_pos = {0,0};
} else if box.prev != nil {
prev := prev_non_floating_sibling(ctx, box);
@ -432,6 +446,8 @@ compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font
iter := BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
if .Floating in child.flags { continue; }
switch box.axis {
case .Horizontal: {
box.computed_size[Axis.Horizontal] += child.computed_size[Axis.Horizontal];
@ -449,6 +465,8 @@ compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font
iter := BoxIter { box.first, 0 };
for child in iterate_box(&iter) {
if .Floating in child.flags { continue; }
switch box.axis {
case .Horizontal: {
if child.computed_size[Axis.Vertical] > box.computed_size[Axis.Vertical] {
@ -463,10 +481,10 @@ compute_layout :: proc(ctx: ^Context, canvas_size: [2]int, font_width: int, font
}
}
push_clip :: proc(ctx: ^Context, pos: [2]int, size: [2]int) {
push_clip :: proc(ctx: ^Context, pos: [2]int, size: [2]int, inside_parent: bool = true) {
rect := Rect { pos, size };
if len(ctx.clips) > 0 {
if len(ctx.clips) > 0 && inside_parent {
parent_rect := ctx.clips[len(ctx.clips)-1];
if rect.pos.x >= parent_rect.pos.x &&
@ -515,7 +533,7 @@ pop_clip :: proc(ctx: ^Context) {
draw :: proc(ctx: ^Context, state: ^core.State, font_width: int, font_height: int, box: ^Box) {
if box == nil { return; }
push_clip(ctx, box.computed_pos, box.computed_size);
push_clip(ctx, box.computed_pos, box.computed_size, !(.Floating in box.flags));
{
defer pop_clip(ctx);
@ -597,7 +615,7 @@ debug_print :: proc(ctx: ^Context, box: ^Box, depth: int = 0) {
if depth > 0 {
fmt.print(">");
}
fmt.println(idx, "Box", box.label, "#", box.key.label, "first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size);
fmt.println(idx, "Box _", box.label, "#", box.key.label, "ptr", transmute(rawptr)box); //, "_ first", transmute(rawptr)box.first, "parent", transmute(rawptr)box.parent, box.computed_size);
debug_print(ctx, box, depth+1);
}