port simple cursor movement
parent
c74fa02e7b
commit
83204be7c3
168
src/main.odin
168
src/main.odin
|
@ -78,7 +78,7 @@ FileBuffer :: struct {
|
||||||
|
|
||||||
original_content: [dynamic]u8,
|
original_content: [dynamic]u8,
|
||||||
added_content: [dynamic]u8,
|
added_content: [dynamic]u8,
|
||||||
content_slices: [dynamic]ContentSlice,
|
content_slices: [dynamic][]u8,
|
||||||
|
|
||||||
glyph_buffer_width: int,
|
glyph_buffer_width: int,
|
||||||
glyph_buffer_height: int,
|
glyph_buffer_height: int,
|
||||||
|
@ -92,19 +92,28 @@ FileBufferIter :: struct {
|
||||||
buffer: ^FileBuffer,
|
buffer: ^FileBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mode :: enum {
|
||||||
|
Normal,
|
||||||
|
Insert,
|
||||||
|
}
|
||||||
|
|
||||||
|
State :: struct {
|
||||||
|
mode: Mode,
|
||||||
|
}
|
||||||
|
|
||||||
new_file_buffer_iter :: proc(file_buffer: ^FileBuffer) -> FileBufferIter {
|
new_file_buffer_iter :: proc(file_buffer: ^FileBuffer) -> FileBufferIter {
|
||||||
return FileBufferIter { buffer = file_buffer };
|
return FileBufferIter { buffer = file_buffer };
|
||||||
}
|
}
|
||||||
iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: FileBufferIndex, cond: bool) {
|
iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: FileBufferIndex, cond: bool) {
|
||||||
if it.index.slice_index >= len(it.buffer.content_slices) || it.index.content_index >= len(it.buffer.content_slices[it.index.slice_index].slice) {
|
if it.index.slice_index >= len(it.buffer.content_slices) || it.index.content_index >= len(it.buffer.content_slices[it.index.slice_index]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cond = true;
|
cond = true;
|
||||||
|
|
||||||
character = it.buffer.content_slices[it.index.slice_index].slice[it.index.content_index];
|
character = it.buffer.content_slices[it.index.slice_index][it.index.content_index];
|
||||||
|
|
||||||
it.index.content_index += 1;
|
it.index.content_index += 1;
|
||||||
if it.index.content_index >= len(it.buffer.content_slices[it.index.slice_index].slice) {
|
if it.index.content_index >= len(it.buffer.content_slices[it.index.slice_index]) {
|
||||||
it.index.content_index = 0;
|
it.index.content_index = 0;
|
||||||
it.index.slice_index += 1;
|
it.index.slice_index += 1;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +121,106 @@ iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: FileBuf
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_file_buffer_index_from_cursor :: proc(buffer: ^FileBuffer) {
|
||||||
|
it := new_file_buffer_iter(buffer);
|
||||||
|
before_it := new_file_buffer_iter(buffer);
|
||||||
|
|
||||||
|
line_length := 0;
|
||||||
|
rendered_line := 0;
|
||||||
|
|
||||||
|
for character in iterate_file_buffer(&it) {
|
||||||
|
if line_length == buffer.cursor.col && rendered_line == buffer.cursor.line {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if character == '\n' {
|
||||||
|
rendered_line += 1;
|
||||||
|
line_length = 0;
|
||||||
|
} else {
|
||||||
|
line_length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
before_it = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.cursor.buffer_index = before_it.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_buffer_line_length :: proc(buffer: ^FileBuffer) -> int {
|
||||||
|
line_length := 0;
|
||||||
|
rendered_line := 0;
|
||||||
|
|
||||||
|
for i in 0..<len(buffer.content_slices) {
|
||||||
|
content := buffer.content_slices[i];
|
||||||
|
|
||||||
|
for c in content {
|
||||||
|
if c == '\n' {
|
||||||
|
rendered_line += 1;
|
||||||
|
|
||||||
|
if rendered_line > buffer.cursor.line {
|
||||||
|
return line_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rendered_line == buffer.cursor.line {
|
||||||
|
line_length += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return line_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
move_cursor_up :: proc(buffer: ^FileBuffer) {
|
||||||
|
if buffer.cursor.line > 0 {
|
||||||
|
buffer.cursor.line -= 1;
|
||||||
|
|
||||||
|
if buffer.cursor.line < buffer.top_line + 5 && buffer.cursor.line >= 4 {
|
||||||
|
buffer.top_line = buffer.cursor.line - 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_length := file_buffer_line_length(buffer);
|
||||||
|
if buffer.cursor.col >= line_length {
|
||||||
|
buffer.cursor.col = line_length < 1 ? 0 : line_length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move_cursor_down :: proc(buffer: ^FileBuffer) {
|
||||||
|
buffer.cursor.line += 1;
|
||||||
|
|
||||||
|
if buffer.cursor.line > buffer.top_line + (buffer.glyph_buffer_height - 5) {
|
||||||
|
buffer.top_line = buffer.cursor.line - (buffer.glyph_buffer_height - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_length := file_buffer_line_length(buffer);
|
||||||
|
if buffer.cursor.col >= line_length {
|
||||||
|
buffer.cursor.col = line_length < 1 ? 0 : line_length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
move_cursor_left :: proc(buffer: ^FileBuffer) {
|
||||||
|
if buffer.cursor.col > 0 {
|
||||||
|
buffer.cursor.col -= 1;
|
||||||
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move_cursor_right :: proc(buffer: ^FileBuffer) {
|
||||||
|
line_length := file_buffer_line_length(buffer);
|
||||||
|
|
||||||
|
if line_length > 0 && buffer.cursor.col < line_length-1 {
|
||||||
|
buffer.cursor.col += 1;
|
||||||
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuffer, Error) {
|
new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuffer, Error) {
|
||||||
context.allocator = allocator;
|
context.allocator = allocator;
|
||||||
|
|
||||||
|
@ -131,7 +240,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuf
|
||||||
|
|
||||||
original_content = slice.clone_to_dynamic(original_content),
|
original_content = slice.clone_to_dynamic(original_content),
|
||||||
added_content = make([dynamic]u8, 0, 1024*1024),
|
added_content = make([dynamic]u8, 0, 1024*1024),
|
||||||
content_slices = make([dynamic]ContentSlice, 0, 1024*1024),
|
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
||||||
|
|
||||||
glyph_buffer_width = width,
|
glyph_buffer_width = width,
|
||||||
glyph_buffer_height = height,
|
glyph_buffer_height = height,
|
||||||
|
@ -140,7 +249,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string) -> (FileBuf
|
||||||
input_buffer = make([dynamic]u8, 0, 1024),
|
input_buffer = make([dynamic]u8, 0, 1024),
|
||||||
};
|
};
|
||||||
|
|
||||||
append(&buffer.content_slices, ContentSlice { type = .Original, slice = buffer.original_content[:] });
|
append(&buffer.content_slices, buffer.original_content[:]);
|
||||||
|
|
||||||
return buffer, error();
|
return buffer, error();
|
||||||
} else {
|
} else {
|
||||||
|
@ -162,8 +271,31 @@ update_glyph_buffer :: proc(buffer: ^FileBuffer) {
|
||||||
if character == '\r' { continue; }
|
if character == '\r' { continue; }
|
||||||
|
|
||||||
screen_line := rendered_line - begin;
|
screen_line := rendered_line - begin;
|
||||||
|
// don't render past the screen
|
||||||
if rendered_line >= begin && screen_line >= buffer.glyph_buffer_height { break; }
|
if rendered_line >= begin && screen_line >= buffer.glyph_buffer_height { break; }
|
||||||
|
|
||||||
|
// render INSERT mode text into glyph buffer
|
||||||
|
if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) {
|
||||||
|
for k in 0..<len(buffer.input_buffer) {
|
||||||
|
screen_line = rendered_line - begin;
|
||||||
|
|
||||||
|
if buffer.input_buffer[k] == '\n' {
|
||||||
|
rendered_col = 0;
|
||||||
|
rendered_line += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rendered_line >= begin && rendered_col < buffer.glyph_buffer_width {
|
||||||
|
buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].color = 0xFFFF;
|
||||||
|
buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].codepoint = buffer.input_buffer[k];
|
||||||
|
|
||||||
|
rendered_col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screen_line = rendered_line - begin;
|
||||||
|
|
||||||
if character == '\n' {
|
if character == '\n' {
|
||||||
rendered_col = 0;
|
rendered_col = 0;
|
||||||
rendered_line += 1;
|
rendered_line += 1;
|
||||||
|
@ -178,7 +310,7 @@ update_glyph_buffer :: proc(buffer: ^FileBuffer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_file_buffer :: proc(buffer: ^FileBuffer, x: int, y: int, font: raylib.Font) {
|
draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, font: raylib.Font) {
|
||||||
update_glyph_buffer(buffer);
|
update_glyph_buffer(buffer);
|
||||||
|
|
||||||
begin := buffer.top_line;
|
begin := buffer.top_line;
|
||||||
|
@ -186,7 +318,12 @@ draw_file_buffer :: proc(buffer: ^FileBuffer, x: int, y: int, font: raylib.Font)
|
||||||
cursor_y := y + buffer.cursor.line * source_font_height;
|
cursor_y := y + buffer.cursor.line * source_font_height;
|
||||||
|
|
||||||
cursor_y -= begin * source_font_height;
|
cursor_y -= begin * source_font_height;
|
||||||
|
if state.mode == .Normal {
|
||||||
raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), source_font_width, source_font_height, raylib.BLUE);
|
raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), source_font_width, source_font_height, raylib.BLUE);
|
||||||
|
} else if state.mode == .Insert {
|
||||||
|
raylib.DrawRectangle(i32(cursor_x), i32(cursor_y), source_font_width, source_font_height, raylib.GREEN);
|
||||||
|
raylib.DrawRectangle(i32(cursor_x + len(buffer.input_buffer) * source_font_width), i32(cursor_y), source_font_width, source_font_height, raylib.BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
for j in 0..<buffer.glyph_buffer_height {
|
for j in 0..<buffer.glyph_buffer_height {
|
||||||
text_y := y + source_font_height * j;
|
text_y := y + source_font_height * j;
|
||||||
|
@ -240,13 +377,13 @@ main :: proc() {
|
||||||
raylib.SetExitKey(.KEY_NULL);
|
raylib.SetExitKey(.KEY_NULL);
|
||||||
|
|
||||||
font := raylib.LoadFont("../c_editor/Mx437_ToshibaSat_8x16.ttf");
|
font := raylib.LoadFont("../c_editor/Mx437_ToshibaSat_8x16.ttf");
|
||||||
|
state: State;
|
||||||
|
|
||||||
buffer, err := new_file_buffer(context.allocator, "./src/main.odin");
|
buffer, err := new_file_buffer(context.allocator, "./src/main.odin");
|
||||||
if err.type != .None {
|
if err.type != .None {
|
||||||
fmt.println("Failed to create file buffer:", err);
|
fmt.println("Failed to create file buffer:", err);
|
||||||
os.exit(1);
|
os.exit(1);
|
||||||
}
|
}
|
||||||
update_glyph_buffer(&buffer);
|
|
||||||
|
|
||||||
for !raylib.WindowShouldClose() {
|
for !raylib.WindowShouldClose() {
|
||||||
{
|
{
|
||||||
|
@ -254,8 +391,21 @@ main :: proc() {
|
||||||
defer raylib.EndDrawing();
|
defer raylib.EndDrawing();
|
||||||
|
|
||||||
raylib.ClearBackground(raylib.GetColor(0x232136ff));
|
raylib.ClearBackground(raylib.GetColor(0x232136ff));
|
||||||
|
draw_file_buffer(&state, &buffer, 0, 32, font);
|
||||||
|
raylib.DrawTextEx(font, raylib.TextFormat("Line: %d, Col: %d", buffer.cursor.line + 1, buffer.cursor.col + 1), raylib.Vector2 { 0, 0 }, source_font_height, 0, raylib.DARKGRAY);
|
||||||
|
}
|
||||||
|
|
||||||
draw_file_buffer(&buffer, 0, 0, font);
|
if raylib.IsKeyPressed(.K) {
|
||||||
|
move_cursor_up(&buffer);
|
||||||
|
}
|
||||||
|
if raylib.IsKeyPressed(.J) {
|
||||||
|
move_cursor_down(&buffer);
|
||||||
|
}
|
||||||
|
if raylib.IsKeyPressed(.H) {
|
||||||
|
move_cursor_left(&buffer);
|
||||||
|
}
|
||||||
|
if raylib.IsKeyPressed(.L) {
|
||||||
|
move_cursor_right(&buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if raylib.IsKeyDown(.LEFT_CONTROL) && raylib.IsKeyDown(.U) {
|
if raylib.IsKeyDown(.LEFT_CONTROL) && raylib.IsKeyDown(.U) {
|
||||||
|
|
Loading…
Reference in New Issue