724 lines
26 KiB
Plaintext
724 lines
26 KiB
Plaintext
package main
|
|
|
|
import "core:c"
|
|
import "core:os"
|
|
import "core:path/filepath"
|
|
import "core:math"
|
|
import "core:strings"
|
|
import "base:runtime"
|
|
import "core:fmt"
|
|
import "core:log"
|
|
import "core:mem"
|
|
import "core:slice"
|
|
import "vendor:sdl2"
|
|
import "vendor:sdl2/ttf"
|
|
|
|
import "core"
|
|
import "theme"
|
|
import "ui"
|
|
|
|
HardcodedFontPath :: "bin/JetBrainsMono-Regular.ttf";
|
|
|
|
State :: core.State;
|
|
FileBuffer :: core.FileBuffer;
|
|
|
|
// TODO: should probably go into state
|
|
scratch: mem.Scratch;
|
|
scratch_alloc: runtime.Allocator;
|
|
state := core.State {};
|
|
|
|
do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
|
}
|
|
|
|
do_insert_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
|
key := 0;
|
|
|
|
for key > 0 {
|
|
if key >= 32 && key <= 125 && len(buffer.input_buffer) < 1024-1 {
|
|
append(&buffer.input_buffer, u8(key));
|
|
}
|
|
|
|
key = 0;
|
|
}
|
|
}
|
|
|
|
do_visual_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
|
}
|
|
|
|
register_default_leader_actions :: proc(input_map: ^core.InputActions) {
|
|
core.register_key_action(input_map, .Q, proc(state: ^State) {
|
|
state.current_input_map = &state.input_map.mode[state.mode];
|
|
}, "close this help");
|
|
}
|
|
|
|
register_default_go_actions :: proc(input_map: ^core.InputActions) {
|
|
core.register_key_action(input_map, .H, proc(state: ^State) {
|
|
core.move_cursor_start_of_line(core.current_buffer(state));
|
|
state.current_input_map = &state.input_map.mode[state.mode];
|
|
}, "move to beginning of line");
|
|
core.register_key_action(input_map, .L, proc(state: ^State) {
|
|
core.move_cursor_end_of_line(core.current_buffer(state));
|
|
state.current_input_map = &state.input_map.mode[state.mode];
|
|
}, "move to end of line");
|
|
}
|
|
|
|
register_default_input_actions :: proc(input_map: ^core.InputActions) {
|
|
// Cursor Movement
|
|
{
|
|
core.register_key_action(input_map, .W, proc(state: ^State) {
|
|
core.move_cursor_forward_start_of_word(core.current_buffer(state));
|
|
}, "move forward one word");
|
|
core.register_key_action(input_map, .E, proc(state: ^State) {
|
|
core.move_cursor_forward_end_of_word(core.current_buffer(state));
|
|
}, "move forward to end of word");
|
|
|
|
core.register_key_action(input_map, .B, proc(state: ^State) {
|
|
core.move_cursor_backward_start_of_word(core.current_buffer(state));
|
|
}, "move backward one word");
|
|
|
|
core.register_key_action(input_map, .K, proc(state: ^State) {
|
|
core.move_cursor_up(core.current_buffer(state));
|
|
}, "move up one line");
|
|
core.register_key_action(input_map, .J, proc(state: ^State) {
|
|
core.move_cursor_down(core.current_buffer(state));
|
|
}, "move down one line");
|
|
core.register_key_action(input_map, .H, proc(state: ^State) {
|
|
core.move_cursor_left(core.current_buffer(state));
|
|
}, "move left one char");
|
|
core.register_key_action(input_map, .L, proc(state: ^State) {
|
|
core.move_cursor_right(core.current_buffer(state));
|
|
}, "move right one char");
|
|
|
|
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
|
core.scroll_file_buffer(core.current_buffer(state), .Up);
|
|
}, "scroll buffer up");
|
|
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
|
core.scroll_file_buffer(core.current_buffer(state), .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;
|
|
state.source_font_width = state.source_font_height / 2;
|
|
|
|
state.font_atlas = core.gen_font_atlas(state, HardcodedFontPath);
|
|
}
|
|
log.debug(state.source_font_height);
|
|
}, "increase font size");
|
|
core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) {
|
|
state.source_font_height += 2;
|
|
state.source_font_width = state.source_font_height / 2;
|
|
|
|
state.font_atlas = core.gen_font_atlas(state, HardcodedFontPath);
|
|
}, "decrease font size");
|
|
}
|
|
|
|
core.register_key_action(input_map, .SPACE, core.new_input_actions(), "leader commands");
|
|
register_default_leader_actions(&(&input_map.key_actions[.SPACE]).action.(core.InputActions));
|
|
|
|
core.register_key_action(input_map, .G, core.new_input_actions(), "Go commands");
|
|
register_default_go_actions(&(&input_map.key_actions[.G]).action.(core.InputActions));
|
|
|
|
core.register_key_action(&state.input_map.mode[.Normal], .V, proc(state: ^State) {
|
|
state.mode = .Visual;
|
|
state.current_input_map = &state.input_map.mode[.Visual];
|
|
|
|
core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).cursor);
|
|
}, "enter visual mode");
|
|
|
|
}
|
|
|
|
register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
|
core.register_key_action(input_map, .ESCAPE, proc(state: ^State) {
|
|
state.mode = .Normal;
|
|
state.current_input_map = &state.input_map.mode[.Normal];
|
|
|
|
core.current_buffer(state).selection = nil;
|
|
core.update_file_buffer_scroll(core.current_buffer(state))
|
|
}, "exit visual mode");
|
|
|
|
// Cursor Movement
|
|
{
|
|
core.register_key_action(input_map, .W, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_forward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move forward one word");
|
|
core.register_key_action(input_map, .E, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_forward_end_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move forward to end of word");
|
|
|
|
core.register_key_action(input_map, .B, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_backward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move backward one word");
|
|
|
|
core.register_key_action(input_map, .K, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_up(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move up one line");
|
|
core.register_key_action(input_map, .J, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_down(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move down one line");
|
|
core.register_key_action(input_map, .H, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_left(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move left one char");
|
|
core.register_key_action(input_map, .L, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.move_cursor_right(core.current_buffer(state), cursor = &sel_cur.end);
|
|
}, "move right one char");
|
|
|
|
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.scroll_file_buffer(core.current_buffer(state), .Up, cursor = &sel_cur.end);
|
|
}, "scroll buffer up");
|
|
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.scroll_file_buffer(core.current_buffer(state), .Down, cursor = &sel_cur.end);
|
|
}, "scroll buffer up");
|
|
}
|
|
|
|
// Text Modification
|
|
{
|
|
core.register_key_action(input_map, .D, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.delete_content(core.current_buffer(state), sel_cur);
|
|
core.current_buffer(state).selection = nil;
|
|
core.update_file_buffer_scroll(core.current_buffer(state))
|
|
|
|
state.mode = .Normal
|
|
state.current_input_map = &state.input_map.mode[.Normal];
|
|
}, "delete selection");
|
|
|
|
core.register_key_action(input_map, .C, proc(state: ^State) {
|
|
sel_cur := &(core.current_buffer(state).selection.?);
|
|
|
|
core.delete_content(core.current_buffer(state), sel_cur);
|
|
core.current_buffer(state).selection = nil;
|
|
core.update_file_buffer_scroll(core.current_buffer(state))
|
|
|
|
state.mode = .Insert
|
|
state.current_input_map = &state.input_map.mode[.Normal];
|
|
sdl2.StartTextInput();
|
|
}, "change selection");
|
|
}
|
|
}
|
|
|
|
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
|
core.register_key_action(input_map, .I, proc(state: ^State) {
|
|
state.mode = .Insert;
|
|
sdl2.StartTextInput();
|
|
}, "enter insert mode");
|
|
core.register_key_action(input_map, .A, proc(state: ^State) {
|
|
core.move_cursor_right(core.current_buffer(state), false);
|
|
state.mode = .Insert;
|
|
sdl2.StartTextInput();
|
|
}, "enter insert mode after character (append)");
|
|
|
|
// TODO: add shift+o to insert newline above current one
|
|
|
|
core.register_key_action(input_map, .O, proc(state: ^State) {
|
|
core.move_cursor_end_of_line(core.current_buffer(state), false);
|
|
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
|
state.mode = .Insert;
|
|
|
|
sdl2.StartTextInput();
|
|
}, "insert mode on newline");
|
|
}
|
|
|
|
ui_font_width :: proc() -> i32 {
|
|
return i32(state.source_font_width);
|
|
}
|
|
ui_font_height :: proc() -> i32 {
|
|
return i32(state.source_font_height);
|
|
}
|
|
|
|
draw :: proc(state: ^State) {
|
|
if buffer := core.current_buffer(state); buffer != nil {
|
|
buffer.glyph_buffer_height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1;
|
|
buffer.glyph_buffer_width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width));
|
|
}
|
|
|
|
render_color := theme.get_palette_color(.Background);
|
|
sdl2.SetRenderDrawColor(state.sdl_renderer, render_color.r, render_color.g, render_color.b, render_color.a);
|
|
sdl2.RenderClear(state.sdl_renderer);
|
|
|
|
new_ui := transmute(^ui.State)state.ui
|
|
new_ui.max_size.x = state.screen_width
|
|
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.compute_layout_2(new_ui)
|
|
ui.draw(new_ui, state)
|
|
|
|
if state.mode != .Insert && state.current_input_map != &state.input_map.mode[state.mode] {
|
|
longest_description := 0;
|
|
for key, action in state.current_input_map.key_actions {
|
|
if len(action.description) > longest_description {
|
|
longest_description = len(action.description);
|
|
}
|
|
}
|
|
for key, action in state.current_input_map.ctrl_key_actions {
|
|
if len(action.description) > longest_description {
|
|
longest_description = len(action.description);
|
|
}
|
|
}
|
|
longest_description += 8;
|
|
|
|
helper_height := state.source_font_height * (len(state.current_input_map.key_actions) + len(state.current_input_map.ctrl_key_actions));
|
|
offset_from_bottom := state.source_font_height * 2;
|
|
|
|
core.draw_rect(
|
|
state,
|
|
state.screen_width - longest_description * state.source_font_width,
|
|
state.screen_height - helper_height - offset_from_bottom,
|
|
longest_description*state.source_font_width,
|
|
helper_height,
|
|
.Background2
|
|
);
|
|
|
|
index := 0;
|
|
for key, action in state.current_input_map.key_actions {
|
|
core.draw_text(
|
|
state,
|
|
fmt.tprintf("%s - %s", key, action.description),
|
|
state.screen_width - longest_description * state.source_font_width,
|
|
state.screen_height - helper_height + index * state.source_font_height - offset_from_bottom
|
|
);
|
|
|
|
index += 1;
|
|
}
|
|
for key, action in state.current_input_map.ctrl_key_actions {
|
|
core.draw_text(
|
|
state,
|
|
fmt.tprintf("<C>-%s - %s", key, action.description),
|
|
state.screen_width - longest_description * state.source_font_width,
|
|
state.screen_height - helper_height + index * state.source_font_height - offset_from_bottom
|
|
);
|
|
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
sdl2.RenderPresent(state.sdl_renderer);
|
|
}
|
|
|
|
expose_event_watcher :: proc "c" (state: rawptr, event: ^sdl2.Event) -> i32 {
|
|
if event.type == .WINDOWEVENT {
|
|
state := transmute(^State)state;
|
|
context = state.ctx;
|
|
|
|
if event.window.event == .EXPOSED {
|
|
//draw(state);
|
|
} else if event.window.event == .SIZE_CHANGED {
|
|
w,h: i32;
|
|
|
|
sdl2.GetRendererOutputSize(state.sdl_renderer, &w, &h);
|
|
|
|
state.screen_width = int(w);
|
|
state.screen_height = int(h);
|
|
state.width_dpi_ratio = f32(w) / f32(event.window.data1);
|
|
state.height_dpi_ratio = f32(h) / f32(event.window.data2);
|
|
|
|
// KDE resizes very slowly on linux if you trigger a re-render
|
|
when ODIN_OS != .Linux {
|
|
draw(state);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ui_file_buffer :: proc(s: ^ui.State, buffer: ^FileBuffer) {
|
|
draw_func := proc(state: ^State, e: ui.UI_Element, user_data: rawptr) {
|
|
buffer := transmute(^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);
|
|
}
|
|
};
|
|
|
|
relative_file_path, _ := filepath.rel(state.directory, buffer.file_path, context.temp_allocator)
|
|
|
|
ui.open_element(s, nil, {
|
|
dir = .TopToBottom,
|
|
kind = {ui.Grow{}, ui.Grow{}},
|
|
})
|
|
{
|
|
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)
|
|
|
|
ui.open_element(s, nil, {
|
|
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
|
|
})
|
|
{
|
|
ui.open_element(s, fmt.tprintf("%s", state.mode), {})
|
|
ui.close_element(s)
|
|
}
|
|
ui.close_element(s)
|
|
}
|
|
ui.close_element(s)
|
|
}
|
|
|
|
main :: proc() {
|
|
_command_arena: mem.Arena
|
|
mem.arena_init(&_command_arena, make([]u8, 1024*1024));
|
|
|
|
state = State {
|
|
ctx = context,
|
|
screen_width = 640,
|
|
screen_height = 480,
|
|
source_font_width = 8,
|
|
source_font_height = 16,
|
|
input_map = core.new_input_map(),
|
|
commands = make(core.EditorCommandList),
|
|
command_arena = mem.arena_allocator(&_command_arena),
|
|
|
|
panel_catalog = make([dynamic]core.PanelId),
|
|
|
|
directory = os.get_current_directory(),
|
|
log_buffer = core.new_virtual_file_buffer(context.allocator),
|
|
};
|
|
|
|
// context.logger = core.new_logger(&state.log_buffer);
|
|
context.logger = log.create_console_logger();
|
|
state.ctx = context;
|
|
|
|
state.ui = &ui.State {
|
|
curr_elements = make([]ui.UI_Element, 8192),
|
|
prev_elements = make([]ui.UI_Element, 8192),
|
|
}
|
|
|
|
// TODO: don't use this
|
|
mem.scratch_allocator_init(&scratch, 1024*1024);
|
|
scratch_alloc = mem.scratch_allocator(&scratch);
|
|
|
|
state.current_input_map = &state.input_map.mode[.Normal];
|
|
register_default_input_actions(&state.input_map.mode[.Normal]);
|
|
register_default_visual_actions(&state.input_map.mode[.Visual]);
|
|
|
|
register_default_text_input_actions(&state.input_map.mode[.Normal]);
|
|
|
|
// core.register_editor_command(
|
|
// &state.commands,
|
|
// "nl.spacegirl.editor.core",
|
|
// "Open New Panel",
|
|
// "Opens a new panel",
|
|
// proc(state: ^State) {
|
|
// Args :: struct {
|
|
// panel_id: string
|
|
// }
|
|
|
|
// if args, ok := core.attempt_read_command_args(Args, state.command_args[:]); ok {
|
|
// log.info("maybe going to open panel with id", args.panel_id)
|
|
|
|
// for p in state.panel_catalog {
|
|
// switch v in p {
|
|
// case core.LuaPanelId:
|
|
// {
|
|
// if v.id == args.panel_id {
|
|
// if index, ok := lua.add_panel(state, v); ok {
|
|
// for i in 0..<len(state.active_panels) {
|
|
// if state.active_panels[i] == nil {
|
|
// state.active_panels[i] = index
|
|
// break;
|
|
// }
|
|
// }
|
|
// } else {
|
|
// log.error("failed to open panel")
|
|
// }
|
|
// }
|
|
// }
|
|
// case core.LibPanelId:
|
|
// {
|
|
// log.warn("lib panels not supported yet")
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// )
|
|
|
|
core.register_editor_command(
|
|
&state.commands,
|
|
"nl.spacegirl.editor.core",
|
|
"New Scratch Buffer",
|
|
"Opens a new scratch buffer",
|
|
proc(state: ^State) {
|
|
buffer := core.new_virtual_file_buffer(context.allocator);
|
|
runtime.append(&state.buffers, buffer);
|
|
}
|
|
)
|
|
core.register_editor_command(
|
|
&state.commands,
|
|
"nl.spacegirl.editor.core",
|
|
"Open File",
|
|
"Opens a file in a new buffer",
|
|
proc(state: ^State) {
|
|
log.info("open file args:");
|
|
|
|
Args :: struct {
|
|
file_path: string
|
|
}
|
|
|
|
if args, ok := core.attempt_read_command_args(Args, state.command_args[:]); ok {
|
|
log.info("attempting to open file", args.file_path)
|
|
|
|
buffer, err := core.new_file_buffer(context.allocator, args.file_path, state.directory);
|
|
if err.type != .None {
|
|
log.error("Failed to create file buffer:", err);
|
|
return;
|
|
}
|
|
|
|
runtime.append(&state.buffers, buffer);
|
|
}
|
|
}
|
|
)
|
|
core.register_editor_command(
|
|
&state.commands,
|
|
"nl.spacegirl.editor.core",
|
|
"Quit",
|
|
"Quits the application",
|
|
proc(state: ^State) {
|
|
state.should_close = true
|
|
}
|
|
)
|
|
|
|
if len(os.args) > 1 {
|
|
for arg in os.args[1:] {
|
|
buffer, err := core.new_file_buffer(context.allocator, arg, state.directory);
|
|
if err.type != .None {
|
|
log.error("Failed to create file buffer:", err);
|
|
continue;
|
|
}
|
|
|
|
runtime.append(&state.buffers, buffer);
|
|
}
|
|
} else {
|
|
buffer := core.new_virtual_file_buffer(context.allocator);
|
|
runtime.append(&state.buffers, buffer);
|
|
}
|
|
|
|
if sdl2.Init({.VIDEO}) < 0 {
|
|
log.error("SDL failed to initialize:", sdl2.GetError());
|
|
return;
|
|
}
|
|
|
|
if ttf.Init() < 0 {
|
|
log.error("SDL_TTF failed to initialize:", ttf.GetError());
|
|
return;
|
|
}
|
|
defer ttf.Quit();
|
|
|
|
sdl_window := sdl2.CreateWindow(
|
|
"odin_editor - [now with more ui]",
|
|
sdl2.WINDOWPOS_UNDEFINED,
|
|
sdl2.WINDOWPOS_UNDEFINED,
|
|
640,
|
|
480,
|
|
{.SHOWN, .RESIZABLE, .ALLOW_HIGHDPI}
|
|
);
|
|
defer if sdl_window != nil {
|
|
sdl2.DestroyWindow(sdl_window);
|
|
}
|
|
|
|
if sdl_window == nil {
|
|
log.error("Failed to create window:", sdl2.GetError());
|
|
return;
|
|
}
|
|
|
|
state.sdl_renderer = sdl2.CreateRenderer(sdl_window, -1, {.ACCELERATED, .PRESENTVSYNC});
|
|
defer if state.sdl_renderer != nil {
|
|
sdl2.DestroyRenderer(state.sdl_renderer);
|
|
}
|
|
|
|
if state.sdl_renderer == nil {
|
|
log.error("Failed to create renderer:", sdl2.GetError());
|
|
return;
|
|
}
|
|
state.font_atlas = core.gen_font_atlas(&state, HardcodedFontPath);
|
|
defer {
|
|
if state.font_atlas.font != nil {
|
|
ttf.CloseFont(state.font_atlas.font);
|
|
}
|
|
if state.font_atlas.texture != nil {
|
|
sdl2.DestroyTexture(state.font_atlas.texture);
|
|
}
|
|
}
|
|
|
|
{
|
|
w,h: i32;
|
|
sdl2.GetRendererOutputSize(state.sdl_renderer, &w, &h);
|
|
|
|
state.width_dpi_ratio = f32(w) / f32(state.screen_width);
|
|
state.height_dpi_ratio = f32(h) / f32(state.screen_height);
|
|
state.screen_width = int(w);
|
|
state.screen_height = int(h);
|
|
}
|
|
|
|
sdl2.SetRenderDrawBlendMode(state.sdl_renderer, .BLEND);
|
|
|
|
// Done to clear the buffer
|
|
sdl2.StartTextInput();
|
|
sdl2.StopTextInput();
|
|
|
|
sdl2.AddEventWatch(expose_event_watcher, &state);
|
|
|
|
control_key_pressed: bool;
|
|
for !state.should_close {
|
|
{
|
|
// 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_x = ui_context.mouse_x;
|
|
// ui_context.last_mouse_y = ui_context.mouse_y;
|
|
|
|
sdl_event: sdl2.Event;
|
|
for(sdl2.PollEvent(&sdl_event)) {
|
|
if sdl_event.type == .QUIT {
|
|
state.should_close = true;
|
|
}
|
|
|
|
if sdl_event.type == .MOUSEMOTION {
|
|
// ui_context.mouse_x = int(f32(sdl_event.motion.x) * state.width_dpi_ratio);
|
|
// ui_context.mouse_y = int(f32(sdl_event.motion.y) * state.height_dpi_ratio);
|
|
}
|
|
|
|
if sdl_event.type == .MOUSEBUTTONDOWN || sdl_event.type == .MOUSEBUTTONUP {
|
|
event := sdl_event.button;
|
|
|
|
if event.button == sdl2.BUTTON_LEFT {
|
|
// ui_context.mouse_left_down = sdl_event.type == .MOUSEBUTTONDOWN;
|
|
}
|
|
if event.button == sdl2.BUTTON_RIGHT {
|
|
// ui_context.mouse_left_down = sdl_event.type == .MOUSEBUTTONDOWN;
|
|
}
|
|
}
|
|
|
|
switch state.mode {
|
|
case .Visual: fallthrough
|
|
case .Normal: {
|
|
if sdl_event.type == .KEYDOWN {
|
|
key := core.Key(sdl_event.key.keysym.sym);
|
|
|
|
if key == .LCTRL {
|
|
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 {
|
|
if action, exists := state.current_input_map.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.key_actions[key]).action.(core.InputActions)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if sdl_event.type == .KEYUP {
|
|
key := core.Key(sdl_event.key.keysym.sym);
|
|
if key == .LCTRL {
|
|
control_key_pressed = false;
|
|
}
|
|
}
|
|
}
|
|
case .Insert: {
|
|
buffer := core.current_buffer(&state);
|
|
|
|
if sdl_event.type == .KEYDOWN {
|
|
key := core.Key(sdl_event.key.keysym.sym);
|
|
|
|
#partial switch key {
|
|
case .ESCAPE: {
|
|
state.mode = .Normal;
|
|
|
|
core.insert_content(buffer, buffer.input_buffer[:]);
|
|
runtime.clear(&buffer.input_buffer);
|
|
|
|
sdl2.StopTextInput();
|
|
}
|
|
case .TAB: {
|
|
// TODO: change this to insert a tab character
|
|
for _ in 0..<4 {
|
|
append(&buffer.input_buffer, ' ');
|
|
}
|
|
}
|
|
case .BACKSPACE: {
|
|
core.delete_content(buffer, 1);
|
|
}
|
|
case .ENTER: {
|
|
append(&buffer.input_buffer, '\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
if sdl_event.type == .TEXTINPUT {
|
|
for char in sdl_event.text.text {
|
|
if char < 1 {
|
|
break;
|
|
}
|
|
|
|
if char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1 {
|
|
append(&buffer.input_buffer, u8(char));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
draw(&state);
|
|
|
|
switch state.mode {
|
|
case .Normal:
|
|
buffer := core.current_buffer(&state);
|
|
do_normal_mode(&state, buffer);
|
|
case .Insert:
|
|
buffer := core.current_buffer(&state);
|
|
do_insert_mode(&state, buffer);
|
|
case .Visual:
|
|
buffer := core.current_buffer(&state);
|
|
do_visual_mode(&state, buffer);
|
|
}
|
|
|
|
runtime.free_all(context.temp_allocator);
|
|
}
|
|
|
|
sdl2.Quit();
|
|
}
|