add initial grep panel, use input map of currently focused panel
parent
342f31478e
commit
97326b54f8
|
@ -50,6 +50,7 @@ State :: struct {
|
||||||
command_arena: runtime.Allocator,
|
command_arena: runtime.Allocator,
|
||||||
command_args: [dynamic]EditorCommandArgument,
|
command_args: [dynamic]EditorCommandArgument,
|
||||||
|
|
||||||
|
current_panel: Maybe(int),
|
||||||
panels: util.StaticList(Panel),
|
panels: util.StaticList(Panel),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,20 +71,45 @@ EditorCommandArgument :: union #no_nil {
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelRenderProc :: proc(state: ^State, panel_state: ^PanelState) -> (ok: bool)
|
PanelRenderProc :: proc(state: ^State, panel_state: ^PanelState) -> (ok: bool)
|
||||||
|
PanelBufferProc :: proc(state: ^State, panel_state: ^PanelState) -> (buffer: ^FileBuffer, ok: bool)
|
||||||
Panel :: struct {
|
Panel :: struct {
|
||||||
panel_state: PanelState,
|
panel_state: PanelState,
|
||||||
|
input_map: InputMap,
|
||||||
|
buffer_proc: PanelBufferProc,
|
||||||
render_proc: PanelRenderProc,
|
render_proc: PanelRenderProc,
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelState :: union {
|
PanelState :: union {
|
||||||
FileBufferPanel
|
FileBufferPanel,
|
||||||
|
GrepPanel,
|
||||||
}
|
}
|
||||||
|
|
||||||
FileBufferPanel :: struct {
|
FileBufferPanel :: struct {
|
||||||
buffer_index: int,
|
buffer_index: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GrepPanel :: struct {
|
||||||
|
buffer: int,
|
||||||
|
search_query: string,
|
||||||
|
query_results: []GrepQueryResult,
|
||||||
|
selected_result: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
GrepQueryResult :: struct {
|
||||||
|
file_path: string,
|
||||||
|
line: int,
|
||||||
|
col: int,
|
||||||
|
}
|
||||||
|
|
||||||
current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
||||||
|
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||||
|
if current_panel.buffer_proc != nil {
|
||||||
|
if panel_buffer, ok := current_panel.buffer_proc(state, ¤t_panel.panel_state); ok && panel_buffer != nil {
|
||||||
|
return panel_buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if state.current_buffer == -2 {
|
if state.current_buffer == -2 {
|
||||||
return &state.log_buffer;
|
return &state.log_buffer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,11 +264,6 @@ draw :: proc(state: ^State) {
|
||||||
new_ui.max_size.x = state.screen_width
|
new_ui.max_size.x = state.screen_width
|
||||||
new_ui.max_size.y = state.screen_height
|
new_ui.max_size.y = state.screen_height
|
||||||
|
|
||||||
// TODO: use the new panels stuff
|
|
||||||
// if file_buffer := core.current_buffer(state); file_buffer != nil {
|
|
||||||
// ui_file_buffer(new_ui, file_buffer)
|
|
||||||
// }
|
|
||||||
|
|
||||||
ui.open_element(new_ui, nil, {
|
ui.open_element(new_ui, nil, {
|
||||||
dir = .LeftToRight,
|
dir = .LeftToRight,
|
||||||
kind = {ui.Grow{}, ui.Grow{}},
|
kind = {ui.Grow{}, ui.Grow{}},
|
||||||
|
@ -543,6 +538,9 @@ main :: proc() {
|
||||||
runtime.append(&state.buffers, buffer);
|
runtime.append(&state.buffers, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
util.append_static_list(&state.panels, panels.make_grep_panel(&state))
|
||||||
|
state.current_panel = state.panels.len-1
|
||||||
|
|
||||||
if sdl2.Init({.VIDEO}) < 0 {
|
if sdl2.Init({.VIDEO}) < 0 {
|
||||||
log.error("SDL failed to initialize:", sdl2.GetError());
|
log.error("SDL failed to initialize:", sdl2.GetError());
|
||||||
return;
|
return;
|
||||||
|
@ -610,6 +608,10 @@ main :: proc() {
|
||||||
|
|
||||||
control_key_pressed: bool;
|
control_key_pressed: bool;
|
||||||
for !state.should_close {
|
for !state.should_close {
|
||||||
|
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||||
|
state.current_input_map = ¤t_panel.input_map.mode[state.mode]
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// ui_context.last_mouse_left_down = ui_context.mouse_left_down;
|
// ui_context.last_mouse_left_down = ui_context.mouse_left_down;
|
||||||
// ui_context.last_mouse_right_down = ui_context.mouse_right_down;
|
// ui_context.last_mouse_right_down = ui_context.mouse_right_down;
|
||||||
|
@ -639,6 +641,36 @@ main :: proc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_key_action := proc(state: ^core.State, control_key_pressed: bool, key: core.Key) -> bool {
|
||||||
|
if state.current_input_map != nil {
|
||||||
|
if control_key_pressed {
|
||||||
|
if action, exists := state.current_input_map.ctrl_key_actions[key]; exists {
|
||||||
|
switch value in action.action {
|
||||||
|
case core.EditorAction:
|
||||||
|
value(state);
|
||||||
|
return true;
|
||||||
|
case core.InputActions:
|
||||||
|
state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputActions)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if action, exists := state.current_input_map.key_actions[key]; exists {
|
||||||
|
switch value in action.action {
|
||||||
|
case core.EditorAction:
|
||||||
|
value(state);
|
||||||
|
return true;
|
||||||
|
case core.InputActions:
|
||||||
|
state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputActions)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
switch state.mode {
|
switch state.mode {
|
||||||
case .Visual: fallthrough
|
case .Visual: fallthrough
|
||||||
case .Normal: {
|
case .Normal: {
|
||||||
|
@ -647,26 +679,8 @@ main :: proc() {
|
||||||
|
|
||||||
if key == .LCTRL {
|
if key == .LCTRL {
|
||||||
control_key_pressed = true;
|
control_key_pressed = true;
|
||||||
} else if state.current_input_map != nil {
|
|
||||||
if control_key_pressed {
|
|
||||||
if action, exists := state.current_input_map.ctrl_key_actions[key]; exists {
|
|
||||||
switch value in action.action {
|
|
||||||
case core.EditorAction:
|
|
||||||
value(&state);
|
|
||||||
case core.InputActions:
|
|
||||||
state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputActions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if action, exists := state.current_input_map.key_actions[key]; exists {
|
run_key_action(&state, control_key_pressed, key)
|
||||||
switch value in action.action {
|
|
||||||
case core.EditorAction:
|
|
||||||
value(&state);
|
|
||||||
case core.InputActions:
|
|
||||||
state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputActions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sdl_event.type == .KEYUP {
|
if sdl_event.type == .KEYUP {
|
||||||
|
@ -682,6 +696,8 @@ main :: proc() {
|
||||||
if sdl_event.type == .KEYDOWN {
|
if sdl_event.type == .KEYDOWN {
|
||||||
key := core.Key(sdl_event.key.keysym.sym);
|
key := core.Key(sdl_event.key.keysym.sym);
|
||||||
|
|
||||||
|
// TODO: make this work properly
|
||||||
|
if true || !run_key_action(&state, control_key_pressed, key) {
|
||||||
#partial switch key {
|
#partial switch key {
|
||||||
case .ESCAPE: {
|
case .ESCAPE: {
|
||||||
state.mode = .Normal;
|
state.mode = .Normal;
|
||||||
|
@ -705,6 +721,7 @@ main :: proc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if sdl_event.type == .TEXTINPUT {
|
if sdl_event.type == .TEXTINPUT {
|
||||||
for char in sdl_event.text.text {
|
for char in sdl_event.text.text {
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
package panels
|
package panels
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
|
import "vendor:sdl2"
|
||||||
|
|
||||||
import "../core"
|
import "../core"
|
||||||
|
import "../util"
|
||||||
import "../ui"
|
import "../ui"
|
||||||
|
|
||||||
make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBuffer) {
|
||||||
return core.Panel {
|
|
||||||
panel_state = core.FileBufferPanel { buffer_index = buffer_index },
|
|
||||||
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
|
||||||
panel_state := panel_state.(core.FileBufferPanel) or_return;
|
|
||||||
|
|
||||||
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
buffer := transmute(^core.FileBuffer)user_data;
|
buffer := transmute(^core.FileBuffer)user_data;
|
||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
|
@ -22,8 +21,6 @@ make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
s := transmute(^ui.State)state.ui
|
|
||||||
buffer := &state.buffers[panel_state.buffer_index]
|
|
||||||
relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator)
|
relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator)
|
||||||
|
|
||||||
ui.open_element(s, nil, {
|
ui.open_element(s, nil, {
|
||||||
|
@ -46,6 +43,138 @@ make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
}
|
}
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBuffer) {
|
||||||
|
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
|
buffer := transmute(^core.FileBuffer)user_data;
|
||||||
|
if buffer != nil {
|
||||||
|
buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width;
|
||||||
|
buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1;
|
||||||
|
|
||||||
|
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.open_element(s, ui.UI_Element_Kind_Custom{fn = draw_func, user_data = transmute(rawptr)buffer}, {
|
||||||
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
|
})
|
||||||
|
ui.close_element(s)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||||
|
return core.Panel {
|
||||||
|
panel_state = core.FileBufferPanel { buffer_index = buffer_index },
|
||||||
|
// TODO: move the input registration from main.odin to here
|
||||||
|
input_map = core.new_input_map(),
|
||||||
|
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
||||||
|
panel_state := panel_state.(core.FileBufferPanel) or_return;
|
||||||
|
s := transmute(^ui.State)state.ui
|
||||||
|
buffer := &state.buffers[panel_state.buffer_index]
|
||||||
|
|
||||||
|
render_file_buffer(state, s, buffer)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
|
input_map := core.new_input_map()
|
||||||
|
grep_input_buffer := core.new_virtual_file_buffer(context.allocator)
|
||||||
|
runtime.append(&state.buffers, grep_input_buffer)
|
||||||
|
|
||||||
|
core.register_key_action(&input_map.mode[.Normal], .I, proc(state: ^core.State) {
|
||||||
|
state.mode = .Insert;
|
||||||
|
sdl2.StartTextInput();
|
||||||
|
}, "enter insert mode");
|
||||||
|
core.register_key_action(&input_map.mode[.Normal], .K, proc(state: ^core.State) {
|
||||||
|
// NOTE: this is really jank, should probably update the input
|
||||||
|
// action stuff to allow panels to be passed into these handlers
|
||||||
|
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||||
|
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
||||||
|
// TODO: bounds checking
|
||||||
|
panel_state.selected_result -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "move selection up");
|
||||||
|
core.register_key_action(&input_map.mode[.Normal], .J, proc(state: ^core.State) {
|
||||||
|
// NOTE: this is really jank, should probably update the input
|
||||||
|
// action stuff to allow panels to be passed into these handlers
|
||||||
|
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||||
|
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
||||||
|
// TODO: bounds checking
|
||||||
|
panel_state.selected_result += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "move selection down");
|
||||||
|
|
||||||
|
core.register_key_action(&input_map.mode[.Insert], .ESCAPE, proc(state: ^core.State) {
|
||||||
|
state.mode = .Normal;
|
||||||
|
sdl2.StopTextInput();
|
||||||
|
}, "exit insert mode");
|
||||||
|
core.register_key_action(&input_map.mode[.Insert], .ENTER, proc(state: ^core.State) {
|
||||||
|
state.mode = .Normal;
|
||||||
|
sdl2.StopTextInput();
|
||||||
|
}, "search");
|
||||||
|
|
||||||
|
results := make([]core.GrepQueryResult, 4)
|
||||||
|
results[0] = core.GrepQueryResult {
|
||||||
|
file_path = "src/main.odin"
|
||||||
|
}
|
||||||
|
results[1] = core.GrepQueryResult {
|
||||||
|
file_path = "src/core/core.odin"
|
||||||
|
}
|
||||||
|
results[2] = core.GrepQueryResult {
|
||||||
|
file_path = "src/panels/panels.odin"
|
||||||
|
}
|
||||||
|
results[3] = core.GrepQueryResult {
|
||||||
|
file_path = "src/core/gfx.odin"
|
||||||
|
}
|
||||||
|
|
||||||
|
return core.Panel {
|
||||||
|
panel_state = core.GrepPanel {
|
||||||
|
buffer = len(state.buffers)-1,
|
||||||
|
query_results = results,
|
||||||
|
},
|
||||||
|
input_map = input_map,
|
||||||
|
buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) {
|
||||||
|
panel_state := panel_state.(core.GrepPanel) or_return;
|
||||||
|
|
||||||
|
return &state.buffers[panel_state.buffer], true
|
||||||
|
},
|
||||||
|
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
||||||
|
panel_state := panel_state.(core.GrepPanel) or_return;
|
||||||
|
|
||||||
|
s := transmute(^ui.State)state.ui
|
||||||
|
ui.open_element(s, nil, {
|
||||||
|
dir = .TopToBottom,
|
||||||
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
defer ui.close_element(s)
|
||||||
|
|
||||||
|
for result, i in panel_state.query_results {
|
||||||
|
// TODO: when styling is implemented, make this look better
|
||||||
|
if panel_state.selected_result == i {
|
||||||
|
ui.open_element(s, fmt.tprintf("%s <--", result.file_path), {})
|
||||||
|
ui.close_element(s)
|
||||||
|
} else {
|
||||||
|
ui.open_element(s, result.file_path, {})
|
||||||
|
ui.close_element(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.open_element(s, nil, {
|
||||||
|
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
defer ui.close_element(s)
|
||||||
|
|
||||||
|
render_raw_buffer(state, s, &state.buffers[panel_state.buffer])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,13 @@ make_static_list :: proc($T: typeid, len: int) -> StaticList(T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
make :: proc{make_static_list}
|
make :: proc{make_static_list}
|
||||||
|
|
||||||
|
get_static_list_elem :: proc(list: ^StaticList($T), index: int) -> Maybe(^T) {
|
||||||
|
if index >= list.len {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &list.data[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
get :: proc{get_static_list_elem}
|
1
todo.md
1
todo.md
|
@ -1,6 +1,7 @@
|
||||||
# Bugs
|
# Bugs
|
||||||
- Fix crash when cursor is over a new-line
|
- Fix crash when cursor is over a new-line
|
||||||
- Fix jumping forward a word jumping past consecutive brackets
|
- Fix jumping forward a word jumping past consecutive brackets
|
||||||
|
- Odd scrolling behavior on small screen heights
|
||||||
|
|
||||||
# Planned Features
|
# Planned Features
|
||||||
- LSP Integration
|
- LSP Integration
|
||||||
|
|
Loading…
Reference in New Issue