mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
431 lines
14 KiB
C
431 lines
14 KiB
C
#include "libc/syscall.h"
|
|
#include "libc/libui.h"
|
|
#include <stddef.h>
|
|
|
|
#define COLOR_DARK_PANEL 0xFF202020
|
|
#define COLOR_DARK_TEXT 0xFFE0E0E0
|
|
#define COLOR_DARK_BORDER 0xFF404040
|
|
#define COLOR_RED 0xFFFF4444
|
|
#define COLOR_DARK_BG 0xFF121212
|
|
#define COLOR_DKGRAY 0xFF808080
|
|
#define COLOR_WHITE 0xFFFFFFFF
|
|
|
|
#define EDITOR_MAX_LINES 128
|
|
#define EDITOR_MAX_LINE_LEN 256
|
|
#define EDITOR_LINE_HEIGHT 16
|
|
#define EDITOR_CHAR_WIDTH 8
|
|
|
|
typedef struct {
|
|
char content[EDITOR_MAX_LINE_LEN];
|
|
int length;
|
|
} EditorLine;
|
|
|
|
static EditorLine lines[EDITOR_MAX_LINES];
|
|
static int line_count = 1;
|
|
static int cursor_line = 0;
|
|
static int cursor_col = 0;
|
|
static int scroll_top = 0;
|
|
static char open_filename[256] = "";
|
|
static _Bool file_modified = 0;
|
|
|
|
static int win_w = 700;
|
|
static int win_h = 450;
|
|
|
|
static void editor_strcpy(char *dest, const char *src) {
|
|
while (*src) *dest++ = *src++;
|
|
*dest = 0;
|
|
}
|
|
|
|
static void editor_clear_all(void) {
|
|
for (int i = 0; i < EDITOR_MAX_LINES; i++) {
|
|
for (int j = 0; j < EDITOR_MAX_LINE_LEN; j++) {
|
|
lines[i].content[j] = 0;
|
|
}
|
|
lines[i].length = 0;
|
|
}
|
|
line_count = 1;
|
|
cursor_line = 0;
|
|
cursor_col = 0;
|
|
scroll_top = 0;
|
|
open_filename[0] = 0;
|
|
file_modified = 0;
|
|
}
|
|
|
|
static void editor_ensure_cursor_visible(void) {
|
|
int visible_lines = 22;
|
|
if (cursor_line < scroll_top) {
|
|
scroll_top = cursor_line;
|
|
}
|
|
if (cursor_line >= scroll_top + visible_lines) {
|
|
scroll_top = cursor_line - visible_lines + 1;
|
|
}
|
|
}
|
|
|
|
void editor_open_file(const char *filename) {
|
|
editor_clear_all();
|
|
editor_strcpy(open_filename, filename);
|
|
|
|
int fd = sys_open(filename, "r");
|
|
if (fd < 0) {
|
|
file_modified = 0;
|
|
return;
|
|
}
|
|
|
|
static char buffer[16384];
|
|
int bytes_read = sys_read(fd, buffer, sizeof(buffer));
|
|
sys_close(fd);
|
|
|
|
if (bytes_read <= 0) {
|
|
file_modified = 0;
|
|
return;
|
|
}
|
|
|
|
int line = 0;
|
|
int col = 0;
|
|
|
|
for (int i = 0; i < bytes_read && line < EDITOR_MAX_LINES; i++) {
|
|
char ch = buffer[i];
|
|
if (ch == '\n') {
|
|
lines[line].content[col] = 0;
|
|
lines[line].length = col;
|
|
line++;
|
|
col = 0;
|
|
} else if (ch != '\r') {
|
|
if (col < EDITOR_MAX_LINE_LEN - 1) {
|
|
lines[line].content[col] = ch;
|
|
col++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (col > 0) {
|
|
lines[line].content[col] = 0;
|
|
lines[line].length = col;
|
|
line++;
|
|
}
|
|
|
|
line_count = (line > 0) ? line : 1;
|
|
file_modified = 0;
|
|
}
|
|
|
|
static void editor_save_file(void) {
|
|
if (!open_filename[0]) return;
|
|
|
|
int fd = sys_open(open_filename, "w");
|
|
if (fd < 0) return;
|
|
|
|
for (int i = 0; i < line_count; i++) {
|
|
sys_write_fs(fd, lines[i].content, lines[i].length);
|
|
sys_write_fs(fd, "\n", 1);
|
|
}
|
|
sys_close(fd);
|
|
file_modified = 0;
|
|
}
|
|
|
|
static void editor_insert_char(char ch) {
|
|
if (cursor_line >= EDITOR_MAX_LINES) return;
|
|
EditorLine *line = &lines[cursor_line];
|
|
|
|
if (ch == '\n') {
|
|
if (line_count >= EDITOR_MAX_LINES) return;
|
|
for (int j = line_count; j > cursor_line; j--) {
|
|
lines[j] = lines[j - 1];
|
|
}
|
|
line_count++;
|
|
|
|
for (int k = 0; k < EDITOR_MAX_LINE_LEN; k++) {
|
|
lines[cursor_line + 1].content[k] = 0;
|
|
}
|
|
lines[cursor_line + 1].length = 0;
|
|
|
|
int current_len = lines[cursor_line].length;
|
|
int new_len = current_len - cursor_col;
|
|
|
|
for (int i = 0; i < new_len; i++) {
|
|
lines[cursor_line + 1].content[i] = lines[cursor_line].content[cursor_col + i];
|
|
}
|
|
lines[cursor_line + 1].content[new_len] = 0;
|
|
lines[cursor_line + 1].length = new_len;
|
|
|
|
lines[cursor_line].content[cursor_col] = 0;
|
|
lines[cursor_line].length = cursor_col;
|
|
|
|
cursor_line++;
|
|
cursor_col = 0;
|
|
} else if (ch == '\b') {
|
|
if (cursor_col > 0) {
|
|
for (int i = cursor_col - 1; i < line->length; i++) {
|
|
line->content[i] = line->content[i + 1];
|
|
}
|
|
line->length--;
|
|
cursor_col--;
|
|
} else if (cursor_line > 0) {
|
|
EditorLine *prev = &lines[cursor_line - 1];
|
|
int merge_point = prev->length;
|
|
|
|
int i = 0;
|
|
while (i < line->length && (merge_point + i) < EDITOR_MAX_LINE_LEN - 1) {
|
|
prev->content[merge_point + i] = line->content[i];
|
|
i++;
|
|
}
|
|
prev->content[merge_point + i] = 0;
|
|
prev->length = merge_point + i;
|
|
|
|
for (int j = cursor_line; j < line_count - 1; j++) {
|
|
lines[j] = lines[j + 1];
|
|
}
|
|
lines[line_count - 1].length = 0;
|
|
lines[line_count - 1].content[0] = 0;
|
|
|
|
cursor_line--;
|
|
cursor_col = merge_point;
|
|
line_count--;
|
|
}
|
|
} else if (ch >= 32 && ch <= 126) {
|
|
if (cursor_col < EDITOR_MAX_LINE_LEN - 1) {
|
|
for (int i = line->length; i > cursor_col; i--) {
|
|
line->content[i] = line->content[i - 1];
|
|
}
|
|
line->content[cursor_col] = ch;
|
|
line->length++;
|
|
cursor_col++;
|
|
}
|
|
}
|
|
file_modified = 1;
|
|
editor_ensure_cursor_visible();
|
|
}
|
|
|
|
static void editor_paint(ui_window_t win) {
|
|
int offset_x = 4;
|
|
int offset_y = 0;
|
|
int content_width = win_w - 8;
|
|
int content_height = win_h - 28;
|
|
|
|
// Top content bar
|
|
ui_draw_rounded_rect_filled(win, offset_x, offset_y, content_width, 25, 6, COLOR_DARK_PANEL);
|
|
ui_draw_string(win, offset_x + 10, offset_y + 6, "File", COLOR_DARK_TEXT);
|
|
ui_draw_string(win, offset_x + 55, offset_y + 6, open_filename, COLOR_DARK_TEXT);
|
|
|
|
// Save button
|
|
int save_btn_x = offset_x + content_width - 80;
|
|
int save_btn_y = offset_y + 3;
|
|
ui_draw_rounded_rect_filled(win, save_btn_x, save_btn_y, 70, 20, 6, COLOR_DARK_BORDER);
|
|
ui_draw_string(win, save_btn_x + 20, save_btn_y + 6, "Save", COLOR_DARK_TEXT);
|
|
|
|
if (file_modified) {
|
|
ui_draw_string(win, offset_x + content_width - 200, offset_y + 5, "[Modified]", COLOR_RED);
|
|
}
|
|
|
|
// Editor background
|
|
ui_draw_rect(win, 4, 54, win_w - 8, win_h - 58, COLOR_DARK_BG);
|
|
|
|
int text_start_x = offset_x + 40;
|
|
int available_width = content_width - 40;
|
|
int max_chars_per_line = available_width / EDITOR_CHAR_WIDTH;
|
|
if (max_chars_per_line < 1) max_chars_per_line = 1;
|
|
|
|
int display_line = 0;
|
|
int visible_lines = (content_height - 55) / EDITOR_LINE_HEIGHT;
|
|
int max_display_lines = visible_lines;
|
|
|
|
int line_idx = scroll_top;
|
|
while (line_idx < line_count && display_line < max_display_lines) {
|
|
int display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT;
|
|
|
|
if (display_line == 0 || line_idx < line_count) {
|
|
char line_num_str[16];
|
|
int temp = line_idx + 1;
|
|
int str_len = 0;
|
|
if (temp == 0) {
|
|
line_num_str[0] = '0';
|
|
str_len = 1;
|
|
} else {
|
|
while (temp > 0) {
|
|
line_num_str[str_len++] = (temp % 10) + '0';
|
|
temp /= 10;
|
|
}
|
|
for (int j = 0; j < str_len / 2; j++) {
|
|
char t = line_num_str[j];
|
|
line_num_str[j] = line_num_str[str_len - 1 - j];
|
|
line_num_str[str_len - 1 - j] = t;
|
|
}
|
|
}
|
|
line_num_str[str_len] = 0;
|
|
ui_draw_string(win, offset_x + 4, display_y, line_num_str, COLOR_DKGRAY);
|
|
}
|
|
|
|
const char *text = lines[line_idx].content;
|
|
int text_len = lines[line_idx].length;
|
|
int char_idx = 0;
|
|
int local_display_line = 0;
|
|
_Bool first_pass = 1;
|
|
|
|
while ((char_idx < text_len || (text_len == 0 && first_pass)) && display_line < max_display_lines) {
|
|
first_pass = 0;
|
|
int current_display_y = offset_y + 35 + display_line * EDITOR_LINE_HEIGHT;
|
|
|
|
char segment[256];
|
|
int segment_len = 0;
|
|
int segment_start = char_idx;
|
|
|
|
while (char_idx < text_len && segment_len < max_chars_per_line) {
|
|
segment[segment_len++] = text[char_idx++];
|
|
}
|
|
segment[segment_len] = 0;
|
|
|
|
if (char_idx < text_len && segment_len > 0) {
|
|
int last_space = -1;
|
|
for (int i = segment_len - 1; i >= 0; i--) {
|
|
if (segment[i] == ' ') {
|
|
last_space = i;
|
|
break;
|
|
}
|
|
}
|
|
if (last_space > 0) {
|
|
segment_len = last_space;
|
|
segment[segment_len] = 0;
|
|
char_idx = segment_start + last_space + 1;
|
|
while (char_idx < text_len && text[char_idx] == ' ') {
|
|
char_idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (segment_len > 0) {
|
|
ui_draw_string(win, text_start_x, current_display_y, segment, COLOR_DARK_TEXT);
|
|
}
|
|
|
|
if (line_idx == cursor_line) {
|
|
int segment_end = segment_start + segment_len;
|
|
_Bool draw_cursor = 0;
|
|
if (cursor_col >= segment_start && cursor_col < segment_end) {
|
|
draw_cursor = 1;
|
|
} else if (cursor_col == text_len && segment_end == text_len) {
|
|
draw_cursor = 1;
|
|
}
|
|
if (draw_cursor) {
|
|
int cursor_x = text_start_x + ((cursor_col - segment_start) * EDITOR_CHAR_WIDTH);
|
|
ui_draw_rect(win, cursor_x, current_display_y, 2, 10, COLOR_WHITE);
|
|
}
|
|
}
|
|
|
|
display_line++;
|
|
local_display_line++;
|
|
if (char_idx >= text_len) break;
|
|
}
|
|
line_idx++;
|
|
}
|
|
|
|
int status_y = offset_y + content_height - 20;
|
|
ui_draw_rounded_rect_filled(win, offset_x, status_y, content_width, 20, 6, COLOR_DARK_PANEL);
|
|
ui_draw_string(win, offset_x + 10, status_y + 5, "Line: ", COLOR_DARK_TEXT);
|
|
|
|
char line_str[32];
|
|
int temp = cursor_line + 1;
|
|
int idx = 0;
|
|
while (temp > 0) {
|
|
line_str[idx++] = (temp % 10) + '0';
|
|
temp /= 10;
|
|
}
|
|
for (int j = 0; j < idx / 2; j++) {
|
|
char t = line_str[j];
|
|
line_str[j] = line_str[idx - 1 - j];
|
|
line_str[idx - 1 - j] = t;
|
|
}
|
|
line_str[idx] = 0;
|
|
|
|
ui_draw_string(win, offset_x + 60, status_y + 5, line_str, COLOR_DARK_TEXT);
|
|
ui_draw_string(win, offset_x + 100, status_y + 5, " Col: ", COLOR_DARK_TEXT);
|
|
|
|
char col_str[32];
|
|
temp = cursor_col + 1;
|
|
idx = 0;
|
|
while (temp > 0) {
|
|
col_str[idx++] = (temp % 10) + '0';
|
|
temp /= 10;
|
|
}
|
|
for (int j = 0; j < idx / 2; j++) {
|
|
char t = col_str[j];
|
|
col_str[j] = col_str[idx - 1 - j];
|
|
col_str[idx - 1 - j] = t;
|
|
}
|
|
col_str[idx] = 0;
|
|
|
|
ui_draw_string(win, offset_x + 170, status_y + 5, col_str, COLOR_DARK_TEXT);
|
|
}
|
|
|
|
static void editor_handle_key(char c) {
|
|
if (c == 17) { // UP
|
|
if (cursor_line > 0) {
|
|
cursor_line--;
|
|
if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length;
|
|
if (cursor_line < scroll_top) scroll_top = cursor_line;
|
|
}
|
|
} else if (c == 18) { // DOWN
|
|
if (cursor_line < line_count - 1) {
|
|
cursor_line++;
|
|
if (cursor_col > lines[cursor_line].length) cursor_col = lines[cursor_line].length;
|
|
int visible_lines = 20;
|
|
if (cursor_line >= scroll_top + visible_lines) scroll_top = cursor_line - visible_lines + 1;
|
|
}
|
|
} else if (c == 19) { // LEFT
|
|
if (cursor_col > 0) {
|
|
cursor_col--;
|
|
} else if (cursor_line > 0) {
|
|
cursor_line--;
|
|
cursor_col = lines[cursor_line].length;
|
|
}
|
|
} else if (c == 20) { // RIGHT
|
|
if (cursor_col < lines[cursor_line].length) {
|
|
cursor_col++;
|
|
} else if (cursor_line < line_count - 1) {
|
|
cursor_line++;
|
|
cursor_col = 0;
|
|
}
|
|
} else {
|
|
editor_insert_char(c);
|
|
}
|
|
}
|
|
|
|
static void editor_handle_click(int x, int y) {
|
|
int content_width = win_w - 8;
|
|
int button_x = 4 + content_width - 80;
|
|
int button_y = 3;
|
|
|
|
if (x >= button_x && x < button_x + 70 && y >= button_y && y < button_y + 20) {
|
|
editor_save_file();
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
ui_window_t win = ui_window_create("Text Editor", 100, 150, win_w, win_h);
|
|
if (!win) return 1;
|
|
|
|
editor_clear_all();
|
|
if (argc > 1) {
|
|
editor_open_file(argv[1]);
|
|
} else {
|
|
editor_strcpy(open_filename, "untitled.txt");
|
|
}
|
|
|
|
gui_event_t ev;
|
|
while (1) {
|
|
if (ui_get_event(win, &ev)) {
|
|
if (ev.type == GUI_EVENT_PAINT) {
|
|
editor_paint(win);
|
|
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
|
|
} else if (ev.type == GUI_EVENT_CLICK) {
|
|
editor_handle_click(ev.arg1, ev.arg2);
|
|
editor_paint(win);
|
|
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
|
|
} else if (ev.type == GUI_EVENT_KEY) {
|
|
editor_handle_key((char)ev.arg1);
|
|
editor_paint(win);
|
|
ui_mark_dirty(win, 0, 0, win_w, win_h - 20);
|
|
} else if (ev.type == GUI_EVENT_CLOSE) {
|
|
sys_exit(0);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|