floating windows

main
Patrick Cleaveliln 2025-07-22 23:50:39 +00:00
parent 705606e92a
commit 2a4e85c868
6 changed files with 164 additions and 70 deletions

View File

@ -101,6 +101,7 @@ PanelType :: union {
FileBufferPanel :: struct {
buffer: FileBuffer,
viewed_symbol: Maybe(string),
// only used for initialization
file_path: string,

View File

@ -59,39 +59,45 @@ draw :: proc(state: ^State) {
new_ui.max_size.x = state.screen_width
new_ui.max_size.y = state.screen_height
ui.open_element(new_ui, nil, {
dir = .LeftToRight,
kind = {ui.Grow{}, ui.Grow{}},
})
ui.open_element(new_ui, nil,
{
dir = .LeftToRight,
kind = {ui.Grow{}, ui.Grow{}},
},
style = {
background_color = .Background1
}
)
{
floating_panels := [16]int{}
num_floating := 0
for i in 0..<len(state.panels.data) {
if panel, ok := util.get(&state.panels, i).?; ok {
if panel.render != nil {
background_color: theme.PaletteColor = .Background1 if panel.id == state.current_panel else .Background2
ui.open_element(new_ui, nil,
{
dir = .LeftToRight,
kind = {ui.Grow{}, ui.Grow{}},
},
style = {
border = {.Left, .Right, .Top, .Bottom },
border_color = .Green,
background_color = background_color,
if panel.is_floating {
if num_floating < len(floating_panels) {
floating_panels[num_floating] = i
num_floating += 1
}
)
{
} else {
panel->render(state)
}
ui.close_element(new_ui)
}
}
}
for i in 0..<num_floating {
panel_id := floating_panels[i]
if panel, ok := util.get(&state.panels, panel_id).?; ok {
panel->render(state)
}
}
}
ui.close_element(new_ui)
ui.compute_layout_2(new_ui)
ui.compute_layout(new_ui)
ui.draw(new_ui, state)
// TODO: figure out when to not show the input help menu

View File

@ -69,6 +69,24 @@ make_file_buffer_panel :: proc(file_path: string, line: int = 0, col: int = 0) -
s := transmute(^ui.State)state.ui
render_file_buffer(state, s, &panel_state.buffer)
if viewed_symbol, ok := panel_state.viewed_symbol.?; ok {
ui.open_element(s, nil,
{
dir = .TopToBottom,
kind = {ui.Fit{}, ui.Fit{}},
floating = true,
},
style = {
background_color = .Background2,
},
)
{
ui.open_element(s, viewed_symbol, {})
ui.close_element(s)
}
ui.close_element(s)
}
return true
}
}
@ -143,10 +161,12 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB
file_buffer_leader_actions :: proc(input_map: ^core.InputActions) {
core.register_key_action(input_map, .K, proc(state: ^core.State, user_data: rawptr) {
buffer := &(&(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)).buffer
panel := transmute(^core.Panel)user_data
panel_state := &panel.type.(core.FileBufferPanel)
buffer := &panel_state.buffer
ts.update_cursor(&buffer.tree, buffer.history.cursor.line, buffer.history.cursor.col)
ts.print_node_type(&buffer.tree)
panel_state.viewed_symbol = ts.print_node_type(&buffer.tree)
core.reset_input_map(state)
}, "View Symbol")
@ -260,6 +280,13 @@ file_buffer_input_actions :: proc(input_map: ^core.InputActions) {
buffer.selection = core.new_selection(buffer.history.cursor);
}, "enter visual mode");
core.register_key_action(input_map, .ESCAPE, proc(state: ^core.State, user_data: rawptr) {
panel := transmute(^core.Panel)user_data
panel_state := &panel.type.(core.FileBufferPanel)
buffer := &panel_state.buffer
panel_state.viewed_symbol = nil
});
}
file_buffer_visual_actions :: proc(input_map: ^core.InputActions) {

View File

@ -48,6 +48,7 @@ make_grep_panel :: proc() -> core.Panel {
return core.Panel {
type = core.GrepPanel {},
is_floating = true,
drop = proc(panel: ^core.Panel, state: ^core.State) {
panel_state := &panel.type.(core.GrepPanel)
@ -147,10 +148,16 @@ make_grep_panel :: proc() -> core.Panel {
if panel_state, ok := &panel.type.(core.GrepPanel); ok {
s := transmute(^ui.State)state.ui
ui.open_element(s, nil, {
dir = .TopToBottom,
kind = {ui.Grow{}, ui.Grow{}}
})
ui.open_element(s, nil,
{
dir = .TopToBottom,
kind = {ui.Grow{}, ui.Grow{}},
floating = true,
},
style = {
background_color = .Background1,
},
)
{
// query results and file contents side-by-side
ui.open_element(s, nil, {
@ -196,7 +203,7 @@ make_grep_panel :: proc() -> core.Panel {
style.background_color = .Background2
}
ui.open_element(s, result.file_path, {}, style)
ui.open_element(s, result.file_path[len(state.directory):], {}, style)
ui.close_element(s)
}
}
@ -217,9 +224,14 @@ make_grep_panel :: proc() -> core.Panel {
ui.close_element(s)
// text input
ui.open_element(s, nil, {
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
})
ui.open_element(s, nil,
{
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
},
style = {
background_color = .Background2
}
)
{
defer ui.close_element(s)

View File

@ -349,15 +349,15 @@ load_highlights :: proc(state: ^State) {
}
}
print_node_type :: proc(state: ^State) {
print_node_type :: proc(state: ^State) -> Maybe(string) {
if state.tree == nil {
return
return nil
}
current_node := tree_cursor_current_node(&state.cursor)
if node_is_null(current_node) {
log.error("Current node is null after goto_first_child")
return
return nil
}
node_type_str := node_type(current_node)
@ -376,4 +376,6 @@ print_node_type :: proc(state: ^State) {
log.infof("Node position: (%d:%d) to (%d:%d)",
start_point.row+1, start_point.column+1,
end_point.row+1, end_point.column+1)
return string(node_type_str)
}

View File

@ -48,6 +48,8 @@ UI_Layout :: struct {
kind: [2]UI_Size_Kind,
size: [2]int,
pos: [2]int,
floating: bool,
}
UI_Size_Kind :: union {
@ -82,8 +84,11 @@ open_element :: proc(state: ^State, kind: UI_Element_Kind, layout: UI_Layout, st
layout = layout,
style = style,
}
e.layout.pos = state.curr_elements[state.num_curr].layout.pos
e.layout.size = state.curr_elements[state.num_curr].layout.size
if !e.layout.floating {
e.layout.pos = state.curr_elements[state.num_curr].layout.pos
e.layout.size = state.curr_elements[state.num_curr].layout.size
}
if parent, ok := state.current_open_element.?; ok {
e.parent = parent
@ -130,9 +135,9 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
case Exact: { e.layout.size.x = int(v) }
case Fit: {
child_index := e.first
for child_index != nil {
child := &state.curr_elements[child_index.?]
it := e.first
for child in iterate_siblings(state, &it) {
if child.layout.floating { continue }
switch e.layout.dir {
case .RightToLeft: fallthrough
@ -145,8 +150,6 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
e.layout.size.x = math.max(e.layout.size.x, child.layout.size.x)
}
}
child_index = child.next
}
}
case Grow: {
@ -173,9 +176,9 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
case Exact: { e.layout.size.y = int(v) }
case Fit: {
child_index := e.first
for child_index != nil {
child := &state.curr_elements[child_index.?]
it := e.first
for child in iterate_siblings(state, &it) {
if child.layout.floating { continue }
switch e.layout.dir {
case .RightToLeft: fallthrough
@ -188,8 +191,6 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
e.layout.size.y += child.layout.size.y
}
}
child_index = child.next
}
}
case Grow: { /* Done in the Grow pass */ }
@ -206,18 +207,64 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
}
@(private)
non_fit_parent_size :: proc(state: ^State, index: int, axis: int) -> [2]int {
iterate_siblings :: proc(state: ^State, sibling: ^Maybe(int)) -> (e: ^UI_Element, index: int, cond: bool) {
if sibling == nil || sibling^ == nil {
cond = false
return
}
e = &state.curr_elements[sibling.?]
index = sibling.?
cond = true
sibling^ = e.next
return
}
@(private)
iterate_siblings_reverse :: proc(state: ^State, sibling: ^Maybe(int)) -> (e: ^UI_Element, index: int, cond: bool) {
if sibling == nil || sibling^ == nil {
cond = false
return
}
e = &state.curr_elements[sibling.?]
index = sibling.?
cond = true
sibling^ = e.prev
return
}
@(private)
non_fit_parent_size :: proc(state: ^State, index: int, axis: int) -> int {
if _, ok := state.curr_elements[index].layout.kind[axis].(Fit); ok {
if parent_index, ok := state.curr_elements[index].parent.?; ok {
if parent_index, ok := state.curr_elements[index].parent.?; ok && !state.curr_elements[index].layout.floating {
return non_fit_parent_size(state, parent_index, axis)
} else {
return state.max_size
return state.max_size[axis]
}
} else if state.curr_elements[index].layout.floating {
return state.max_size[axis]
} else {
return state.curr_elements[index].layout.size
return state.curr_elements[index].layout.size[axis]
}
}
@(private)
prev_non_floating :: proc(state: ^State, index: Maybe(int)) -> Maybe(int) {
it := index
for sibling, index in iterate_siblings_reverse(state, &it) {
if sibling.layout.floating { continue }
return index
}
return nil
}
@(private)
grow_children :: proc(state: ^State, index: int) {
e := &state.curr_elements[index]
@ -228,9 +275,14 @@ grow_children :: proc(state: ^State, index: int) {
children_size: [2]int
num_growing: [2]int
child_index := e.first
for child_index != nil {
child := &state.curr_elements[child_index.?]
has_floating := false
it := e.first
for child in iterate_siblings(state, &it) {
if child.layout.floating {
has_floating = true
continue
}
if _, ok := child.layout.kind.x.(Grow); ok {
num_growing.x += 1
@ -258,37 +310,33 @@ grow_children :: proc(state: ^State, index: int) {
}
}
}
child_index = child.next
}
if num_growing.x > 0 || num_growing.y > 0 {
remaining_size := [2]int{ x_e.x, y_e.y } - children_size
remaining_size := [2]int{ x_e, y_e } - children_size
to_grow: [2]int
to_grow.x = 0 if num_growing.x < 1 else remaining_size.x/num_growing.x
to_grow.y = 0 if num_growing.y < 1 else remaining_size.y/num_growing.y
child_index := e.first
for child_index != nil {
child := &state.curr_elements[child_index.?]
it := e.first
for child, child_index in iterate_siblings(state, &it) {
switch e.layout.dir {
case .RightToLeft: fallthrough
case .LeftToRight: {
if _, ok := child.layout.kind.x.(Grow); ok {
child.layout.size.x = to_grow.x
child.layout.size.x = state.max_size.x if child.layout.floating else to_grow.x
}
if _, ok := child.layout.kind.y.(Grow); ok {
child.layout.size.y = remaining_size.y
child.layout.size.y = state.max_size.y if child.layout.floating else y_e
}
}
case .BottomToTop: fallthrough
case .TopToBottom: {
if _, ok := child.layout.kind.x.(Grow); ok {
child.layout.size.x = remaining_size.x
child.layout.size.x = state.max_size.x if child.layout.floating else x_e
}
if _, ok := child.layout.kind.y.(Grow); ok {
child.layout.size.y = to_grow.y
child.layout.size.y = state.max_size.y if child.layout.floating else to_grow.y
}
}
}
@ -296,25 +344,23 @@ grow_children :: proc(state: ^State, index: int) {
_, x_growing := child.layout.kind.x.(Grow)
_, y_growing := child.layout.kind.y.(Grow)
if x_growing || y_growing {
grow_children(state, child_index.?)
if x_growing || y_growing || child.layout.floating {
grow_children(state, child_index)
}
child_index = child.next
}
}
}
compute_layout_2 :: proc(state: ^State) {
compute_layout :: proc(state: ^State) {
grow_children(state, 0)
for i in 0..<state.num_curr {
e := &state.curr_elements[i]
if parent_index, ok := e.parent.?; ok {
if parent_index, ok := e.parent.?; ok && !e.layout.floating {
parent := &state.curr_elements[parent_index]
if prev_index, ok := e.prev.?; ok {
if prev_index, ok := prev_non_floating(state, e.prev).?; ok {
prev := &state.curr_elements[prev_index]
switch parent.layout.dir {