added a window api of sorts, got started on grep style tool
parent
ff020fd059
commit
a7fa2f6b1d
|
@ -8,6 +8,36 @@ Mode :: enum {
|
|||
Insert,
|
||||
}
|
||||
|
||||
WindowDrawProc :: proc(win: ^Window, state: ^State);
|
||||
WindowFreeProc :: proc(win: ^Window, state: ^State);
|
||||
WindowGetBufferProc :: proc(win: ^Window) -> ^FileBuffer;
|
||||
Window :: struct {
|
||||
input_map: InputMap,
|
||||
draw: WindowDrawProc,
|
||||
free: WindowFreeProc,
|
||||
|
||||
get_buffer: WindowGetBufferProc,
|
||||
|
||||
// TODO: create hook for when mode changes happen
|
||||
}
|
||||
request_window_close :: proc(state: ^State) {
|
||||
state.should_close_window = true;
|
||||
}
|
||||
|
||||
close_window_and_free :: proc(state: ^State) {
|
||||
if state.window != nil {
|
||||
if state.window.free != nil {
|
||||
state.window->free(state);
|
||||
}
|
||||
|
||||
delete_input_map(&state.window.input_map);
|
||||
free(state.window);
|
||||
|
||||
state.window = nil;
|
||||
state.current_input_map = &state.input_map;
|
||||
}
|
||||
}
|
||||
|
||||
State :: struct {
|
||||
mode: Mode,
|
||||
should_close: bool,
|
||||
|
@ -22,10 +52,8 @@ State :: struct {
|
|||
current_buffer: int,
|
||||
buffers: [dynamic]FileBuffer,
|
||||
|
||||
// TODO: replace this with generic pointer to floating window
|
||||
buffer_list_window_is_visible: bool,
|
||||
buffer_list_window_selected_buffer: int,
|
||||
buffer_list_window_input_map: InputMap,
|
||||
window: ^Window,
|
||||
should_close_window: bool,
|
||||
|
||||
input_map: InputMap,
|
||||
current_input_map: ^InputMap,
|
||||
|
@ -50,6 +78,10 @@ new_input_map :: proc() -> InputMap {
|
|||
|
||||
return input_map;
|
||||
}
|
||||
delete_input_map :: proc(input_map: ^InputMap) {
|
||||
delete(input_map.key_actions);
|
||||
delete(input_map.ctrl_key_actions);
|
||||
}
|
||||
|
||||
// NOTE(pcleavelin): might be a bug in the compiler where it can't coerce
|
||||
// `EditorAction` to `InputGroup` when given as a proc parameter, that is why there
|
||||
|
|
|
@ -86,8 +86,11 @@ iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: FileBuf
|
|||
} else if it.cursor.index.slice_index < len(it.buffer.content_slices)-1 {
|
||||
it.cursor.index.content_index = 0;
|
||||
it.cursor.index.slice_index += 1;
|
||||
} else {
|
||||
} else if it.hit_end {
|
||||
return character, it.cursor.index, false;
|
||||
} else {
|
||||
it.hit_end = true;
|
||||
return character, it.cursor.index, true;
|
||||
}
|
||||
|
||||
if character == '\n' {
|
||||
|
@ -531,6 +534,31 @@ move_cursor_backward_end_of_word :: proc(buffer: ^FileBuffer) {
|
|||
update_file_buffer_scroll(buffer);
|
||||
}
|
||||
|
||||
new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
|
||||
context.allocator = allocator;
|
||||
width := 256;
|
||||
height := 256;
|
||||
|
||||
buffer := FileBuffer {
|
||||
allocator = allocator,
|
||||
file_path = "virtual_buffer",
|
||||
|
||||
original_content = slice.clone_to_dynamic([]u8{'\n'}),
|
||||
added_content = make([dynamic]u8, 0, 1024*1024),
|
||||
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
||||
|
||||
glyph_buffer_width = width,
|
||||
glyph_buffer_height = height,
|
||||
glyph_buffer = make([dynamic]Glyph, width*height, width*height),
|
||||
|
||||
input_buffer = make([dynamic]u8, 0, 1024),
|
||||
};
|
||||
|
||||
append(&buffer.content_slices, buffer.original_content[:]);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuffer, Error) {
|
||||
context.allocator = allocator;
|
||||
|
||||
|
@ -567,6 +595,14 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuf
|
|||
}
|
||||
}
|
||||
|
||||
free_file_buffer :: proc(buffer: ^FileBuffer) {
|
||||
delete(buffer.original_content);
|
||||
delete(buffer.added_content);
|
||||
delete(buffer.content_slices);
|
||||
delete(buffer.glyph_buffer);
|
||||
delete(buffer.input_buffer);
|
||||
}
|
||||
|
||||
is_keyword :: proc(start: FileBufferIter, end: FileBufferIter) -> (matches: bool) {
|
||||
keywords := []string {
|
||||
"using",
|
||||
|
|
|
@ -19,8 +19,8 @@ FileBuffer :: core.FileBuffer;
|
|||
// TODO: use buffer list in state
|
||||
do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
||||
if state.current_input_map != nil {
|
||||
if raylib.IsKeyDown(.ESCAPE) {
|
||||
state.current_input_map = &state.input_map;
|
||||
if raylib.IsKeyPressed(.ESCAPE) {
|
||||
core.request_window_close(state);
|
||||
} else if raylib.IsKeyDown(.LEFT_CONTROL) {
|
||||
for key, action in &state.current_input_map.ctrl_key_actions {
|
||||
if raylib.IsKeyPressed(key) {
|
||||
|
@ -87,9 +87,14 @@ switch_to_buffer :: proc(state: ^State, item: ^ui.MenuBarItem) {
|
|||
|
||||
register_default_leader_actions :: proc(input_map: ^core.InputMap) {
|
||||
core.register_key_action(input_map, .B, proc(state: ^State) {
|
||||
state.buffer_list_window_is_visible = true;
|
||||
state.current_input_map = &state.buffer_list_window_input_map;
|
||||
state.window = ui.create_buffer_list_window();
|
||||
state.current_input_map = &state.window.input_map;
|
||||
}, "show list of open buffers");
|
||||
core.register_key_action(input_map, .R, proc(state: ^State) {
|
||||
state.window = ui.create_grep_window();
|
||||
state.current_input_map = &state.window.input_map;
|
||||
state.mode = .Insert;
|
||||
}, "live grep");
|
||||
core.register_key_action(input_map, .Q, proc(state: ^State) {
|
||||
state.current_input_map = &state.input_map;
|
||||
}, "close this help");
|
||||
|
@ -107,6 +112,8 @@ register_default_go_actions :: proc(input_map: ^core.InputMap) {
|
|||
}
|
||||
|
||||
register_default_input_actions :: proc(input_map: ^core.InputMap) {
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
core.move_cursor_forward_start_of_word(&state.buffers[state.current_buffer]);
|
||||
}, "move forward one word");
|
||||
|
@ -137,8 +144,10 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) {
|
|||
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
||||
core.scroll_file_buffer(&state.buffers[state.current_buffer], .Down);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Scale font size
|
||||
{
|
||||
core.register_ctrl_key_action(input_map, .MINUS, proc(state: ^State) {
|
||||
if state.source_font_height > 16 {
|
||||
state.source_font_height -= 2;
|
||||
|
@ -155,7 +164,10 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) {
|
|||
state.font = raylib.LoadFontEx("/System/Library/Fonts/Supplemental/Andale Mono.ttf", i32(state.source_font_height*2), nil, 0);
|
||||
raylib.SetTextureFilter(state.font.texture, .BILINEAR);
|
||||
}, "decrease font size");
|
||||
}
|
||||
|
||||
// Inserting Text
|
||||
{
|
||||
core.register_key_action(input_map, .I, proc(state: ^State) {
|
||||
state.mode = .Insert;
|
||||
}, "enter insert mode");
|
||||
|
@ -163,6 +175,7 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) {
|
|||
core.move_cursor_right(&state.buffers[state.current_buffer], false);
|
||||
state.mode = .Insert;
|
||||
}, "enter insert mode after character (append)");
|
||||
}
|
||||
|
||||
core.register_key_action(input_map, .SPACE, core.new_input_map(), "leader commands");
|
||||
register_default_leader_actions(&(&input_map.key_actions[.SPACE]).action.(core.InputMap));
|
||||
|
@ -171,44 +184,15 @@ register_default_input_actions :: proc(input_map: ^core.InputMap) {
|
|||
register_default_go_actions(&(&input_map.key_actions[.G]).action.(core.InputMap));
|
||||
}
|
||||
|
||||
register_buffer_list_input_actions :: proc(input_map: ^core.InputMap) {
|
||||
core.register_key_action(input_map, .K, proc(state: ^State) {
|
||||
if state.buffer_list_window_selected_buffer > 0 {
|
||||
state.buffer_list_window_selected_buffer -= 1;
|
||||
} else {
|
||||
state.buffer_list_window_selected_buffer = len(state.buffers)-1;
|
||||
}
|
||||
}, "move selection up");
|
||||
core.register_key_action(input_map, .J, proc(state: ^State) {
|
||||
if state.buffer_list_window_selected_buffer >= len(state.buffers)-1 {
|
||||
state.buffer_list_window_selected_buffer = 0;
|
||||
} else {
|
||||
state.buffer_list_window_selected_buffer += 1;
|
||||
}
|
||||
}, "move selection down");
|
||||
core.register_key_action(input_map, .ENTER, proc(state: ^State) {
|
||||
state.current_buffer = state.buffer_list_window_selected_buffer;
|
||||
|
||||
state.buffer_list_window_is_visible = false;
|
||||
state.current_input_map = &state.input_map;
|
||||
}, "switch to file");
|
||||
|
||||
core.register_key_action(input_map, .Q, proc(state: ^State) {
|
||||
state.buffer_list_window_is_visible = false;
|
||||
state.current_input_map = &state.input_map;
|
||||
}, "close window");
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
state := State {
|
||||
source_font_width = 8,
|
||||
source_font_height = 16,
|
||||
input_map = core.new_input_map(),
|
||||
buffer_list_window_input_map = core.new_input_map(),
|
||||
window = nil,
|
||||
};
|
||||
state.current_input_map = &state.input_map;
|
||||
register_default_input_actions(&state.input_map);
|
||||
register_buffer_list_input_actions(&state.buffer_list_window_input_map);
|
||||
|
||||
for arg in os.args[1:] {
|
||||
buffer, err := core.new_file_buffer(context.allocator, arg);
|
||||
|
@ -323,8 +307,8 @@ main :: proc() {
|
|||
0,
|
||||
theme.get_palette_raylib_color(.Background1));
|
||||
|
||||
if state.buffer_list_window_is_visible {
|
||||
ui.draw_buffer_list_window(&state);
|
||||
if state.window != nil && state.window.draw != nil {
|
||||
state.window->draw(&state);
|
||||
}
|
||||
|
||||
if state.current_input_map != &state.input_map {
|
||||
|
@ -377,10 +361,23 @@ main :: proc() {
|
|||
|
||||
switch state.mode {
|
||||
case .Normal:
|
||||
if state.window != nil && state.window.get_buffer != nil {
|
||||
do_normal_mode(&state, state.window->get_buffer());
|
||||
} else {
|
||||
do_normal_mode(&state, buffer);
|
||||
}
|
||||
case .Insert:
|
||||
if state.window != nil && state.window.get_buffer != nil {
|
||||
do_insert_mode(&state, state.window->get_buffer());
|
||||
} else {
|
||||
do_insert_mode(&state, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if state.should_close_window {
|
||||
state.should_close_window = false;
|
||||
core.close_window_and_free(&state);
|
||||
}
|
||||
|
||||
ui.test_menu_bar(&state, &menu_bar_state, 0,0, mouse_pos, raylib.IsMouseButtonReleased(.LEFT), state.source_font_height);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,60 @@ import "vendor:raylib"
|
|||
import "../core"
|
||||
import "../theme"
|
||||
|
||||
draw_buffer_list_window :: proc(state: ^core.State) {
|
||||
BufferListWindow :: struct {
|
||||
using window: core.Window,
|
||||
|
||||
selected_buffer: int,
|
||||
}
|
||||
|
||||
create_buffer_list_window :: proc() -> ^BufferListWindow {
|
||||
input_map := core.new_input_map();
|
||||
|
||||
core.register_key_action(&input_map, .K, proc(state: ^core.State) {
|
||||
win := cast(^BufferListWindow)(state.window);
|
||||
|
||||
if win.selected_buffer > 0 {
|
||||
win.selected_buffer -= 1;
|
||||
} else {
|
||||
win.selected_buffer = len(state.buffers)-1;
|
||||
}
|
||||
|
||||
}, "move selection up");
|
||||
core.register_key_action(&input_map, .J, proc(state: ^core.State) {
|
||||
win := cast(^BufferListWindow)(state.window);
|
||||
|
||||
if win.selected_buffer >= len(state.buffers)-1 {
|
||||
win.selected_buffer = 0;
|
||||
} else {
|
||||
win.selected_buffer += 1;
|
||||
}
|
||||
}, "move selection down");
|
||||
core.register_key_action(&input_map, .ENTER, proc(state: ^core.State) {
|
||||
win := cast(^BufferListWindow)(state.window);
|
||||
|
||||
state.current_buffer = win.selected_buffer;
|
||||
|
||||
core.request_window_close(state);
|
||||
}, "switch to file");
|
||||
|
||||
core.register_key_action(&input_map, .Q, proc(state: ^core.State) {
|
||||
core.request_window_close(state);
|
||||
}, "close window");
|
||||
|
||||
list_window := new(BufferListWindow);
|
||||
list_window^ = BufferListWindow {
|
||||
window = core.Window {
|
||||
input_map = input_map,
|
||||
draw = draw_buffer_list_window,
|
||||
},
|
||||
};
|
||||
|
||||
return list_window;
|
||||
}
|
||||
|
||||
draw_buffer_list_window :: proc(win: ^core.Window, state: ^core.State) {
|
||||
win := cast(^BufferListWindow)(win);
|
||||
|
||||
win_rec := raylib.Rectangle {
|
||||
x = f32(state.screen_width/8),
|
||||
y = f32(state.screen_height/8),
|
||||
|
@ -37,7 +90,7 @@ draw_buffer_list_window :: proc(state: ^core.State) {
|
|||
text := raylib.TextFormat("%s:%d", buffer.file_path, buffer.cursor.line+1);
|
||||
text_width := raylib.MeasureTextEx(state.font, text, f32(state.source_font_height), 0);
|
||||
|
||||
if index == state.buffer_list_window_selected_buffer {
|
||||
if index == win.selected_buffer {
|
||||
buffer.glyph_buffer_height = glyph_buffer_height;
|
||||
buffer.glyph_buffer_width = glyph_buffer_width;
|
||||
core.draw_file_buffer(
|
|
@ -0,0 +1,97 @@
|
|||
package ui;
|
||||
|
||||
import "core:math"
|
||||
import "vendor:raylib"
|
||||
|
||||
import "../core"
|
||||
import "../theme"
|
||||
|
||||
GrepWindow :: struct {
|
||||
using window: core.Window,
|
||||
|
||||
input_buffer: core.FileBuffer,
|
||||
}
|
||||
|
||||
create_grep_window :: proc() -> ^GrepWindow {
|
||||
input_map := core.new_input_map();
|
||||
|
||||
core.register_key_action(&input_map, .ENTER, proc(state: ^core.State) {
|
||||
win := cast(^GrepWindow)(state.window);
|
||||
|
||||
core.request_window_close(state);
|
||||
}, "jump to location");
|
||||
|
||||
core.register_key_action(&input_map, .I, proc(state: ^core.State) {
|
||||
state.mode = .Insert;
|
||||
}, "enter insert mode");
|
||||
|
||||
grep_window := new(GrepWindow);
|
||||
grep_window^ = GrepWindow {
|
||||
window = core.Window {
|
||||
input_map = input_map,
|
||||
draw = draw_grep_window,
|
||||
get_buffer = grep_window_get_buffer,
|
||||
free = free_grep_window,
|
||||
},
|
||||
|
||||
input_buffer = core.new_virtual_file_buffer(context.allocator),
|
||||
};
|
||||
|
||||
return grep_window;
|
||||
}
|
||||
|
||||
free_grep_window :: proc(win: ^core.Window, state: ^core.State) {
|
||||
win := cast(^GrepWindow)(win);
|
||||
|
||||
core.free_file_buffer(&win.input_buffer);
|
||||
}
|
||||
|
||||
grep_window_get_buffer :: proc(win: ^core.Window) -> ^core.FileBuffer {
|
||||
win := cast(^GrepWindow)(win);
|
||||
|
||||
return &win.input_buffer;
|
||||
}
|
||||
|
||||
@private
|
||||
grep_files :: proc(win: ^core.Window, state: ^core.State) {
|
||||
// TODO: use rip-grep to search through files
|
||||
}
|
||||
|
||||
draw_grep_window :: proc(win: ^core.Window, state: ^core.State) {
|
||||
win := cast(^GrepWindow)(win);
|
||||
|
||||
win_rec := raylib.Rectangle {
|
||||
x = f32(state.screen_width/8),
|
||||
y = f32(state.screen_height/8),
|
||||
width = f32(state.screen_width - state.screen_width/4),
|
||||
height = f32(state.screen_height - state.screen_height/4),
|
||||
};
|
||||
raylib.DrawRectangleRec(
|
||||
win_rec,
|
||||
theme.get_palette_raylib_color(.Background4));
|
||||
|
||||
win_margin := raylib.Vector2 { f32(state.source_font_width), f32(state.source_font_height) };
|
||||
|
||||
buffer_prev_width := (win_rec.width - win_margin.x*2) / 2;
|
||||
buffer_prev_height := win_rec.height - win_margin.y*2;
|
||||
|
||||
glyph_buffer_width := int(buffer_prev_width) / state.source_font_width - 1;
|
||||
glyph_buffer_height := 1;
|
||||
|
||||
raylib.DrawRectangle(
|
||||
i32(win_rec.x + win_margin.x),
|
||||
i32(win_rec.y + win_rec.height - win_margin.y * 2),
|
||||
i32(buffer_prev_width),
|
||||
i32(state.source_font_height),
|
||||
theme.get_palette_raylib_color(.Background2));
|
||||
|
||||
win.input_buffer.glyph_buffer_height = glyph_buffer_height;
|
||||
win.input_buffer.glyph_buffer_width = glyph_buffer_width;
|
||||
core.draw_file_buffer(
|
||||
state,
|
||||
&win.input_buffer,
|
||||
int(win_rec.x + win_margin.x),
|
||||
int(win_rec.y + win_rec.height - win_margin.y * 2),
|
||||
state.font,
|
||||
show_line_numbers = false);
|
||||
}
|
Loading…
Reference in New Issue