|
|
|
|
@ -1,13 +1,14 @@
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import "core:fmt"
|
|
|
|
|
import "core:log"
|
|
|
|
|
import "core:math"
|
|
|
|
|
import "core:mem"
|
|
|
|
|
import "core:os"
|
|
|
|
|
import "core:strings"
|
|
|
|
|
import "core:slice"
|
|
|
|
|
import "core:unicode/utf8"
|
|
|
|
|
import "core:strconv"
|
|
|
|
|
import "core:math"
|
|
|
|
|
import "core:mem"
|
|
|
|
|
import "core:strings"
|
|
|
|
|
import "core:unicode/utf8"
|
|
|
|
|
|
|
|
|
|
import sdl "vendor:sdl2"
|
|
|
|
|
import ttf "vendor:sdl2/ttf"
|
|
|
|
|
@ -20,58 +21,28 @@ LINE_NUMBER_WIDTH :: 60
|
|
|
|
|
FONT_SIZE :: 14
|
|
|
|
|
TEXT_PADDING :: 10
|
|
|
|
|
|
|
|
|
|
// Colors
|
|
|
|
|
COLOR_BACKGROUND :: sdl.Color{30, 30, 30, 255}
|
|
|
|
|
COLOR_TEXT :: sdl.Color{220, 220, 220, 255}
|
|
|
|
|
COLOR_LINE_NUMBERS :: sdl.Color{150, 150, 150, 255}
|
|
|
|
|
COLOR_TAB_ACTIVE :: sdl.Color{60, 60, 60, 255}
|
|
|
|
|
COLOR_TAB_INACTIVE :: sdl.Color{45, 45, 45, 255}
|
|
|
|
|
COLOR_TAB_BORDER :: sdl.Color{80, 80, 80, 255}
|
|
|
|
|
COLOR_CURSOR :: sdl.Color{255, 255, 255, 255}
|
|
|
|
|
COLOR_LINE_NUMBER_BG :: sdl.Color{40, 40, 40, 255}
|
|
|
|
|
|
|
|
|
|
// Text buffer for a single file
|
|
|
|
|
Text_Buffer :: struct {
|
|
|
|
|
lines: [dynamic]string,
|
|
|
|
|
filename: string,
|
|
|
|
|
modified: bool,
|
|
|
|
|
cursor_line: int,
|
|
|
|
|
cursor_col: int,
|
|
|
|
|
scroll_y: int,
|
|
|
|
|
max_line_width: int,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tab structure
|
|
|
|
|
Tab :: struct {
|
|
|
|
|
name: string,
|
|
|
|
|
buffer: Text_Buffer,
|
|
|
|
|
rect: sdl.Rect,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Application state
|
|
|
|
|
App :: struct {
|
|
|
|
|
window: ^sdl.Window,
|
|
|
|
|
renderer: ^sdl.Renderer,
|
|
|
|
|
font: ^ttf.Font,
|
|
|
|
|
tabs: [dynamic]Tab,
|
|
|
|
|
active_tab: int,
|
|
|
|
|
running: bool,
|
|
|
|
|
char_width: i32,
|
|
|
|
|
char_height: i32,
|
|
|
|
|
text_area_width: i32,
|
|
|
|
|
visible_lines: int,
|
|
|
|
|
cursor_blink_timer: u32,
|
|
|
|
|
show_cursor: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app: App
|
|
|
|
|
|
|
|
|
|
main :: proc() {
|
|
|
|
|
context.logger = log.create_console_logger(.Debug)
|
|
|
|
|
defer log.destroy_console_logger(context.logger)
|
|
|
|
|
|
|
|
|
|
// Get command line arguments
|
|
|
|
|
args := os.args[1:]
|
|
|
|
|
|
|
|
|
|
ok := false
|
|
|
|
|
app.config, ok = parse_config("config.ini")
|
|
|
|
|
if !ok {
|
|
|
|
|
log.fatal("failed to parse config file")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer config_destroy(app.config)
|
|
|
|
|
|
|
|
|
|
log.debug("app config:")
|
|
|
|
|
log.debug(app.config)
|
|
|
|
|
|
|
|
|
|
if init_sdl() != 0 {
|
|
|
|
|
fmt.println("Failed to initialize SDL")
|
|
|
|
|
log.fatal("failed to initialize sdl")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer cleanup_sdl()
|
|
|
|
|
@ -127,7 +98,7 @@ init_sdl :: proc() -> int {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.window = sdl.CreateWindow(
|
|
|
|
|
"Multi-Tab Text Editor",
|
|
|
|
|
"notepad",
|
|
|
|
|
sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
|
|
|
|
|
WINDOW_WIDTH, WINDOW_HEIGHT,
|
|
|
|
|
sdl.WINDOW_SHOWN | sdl.WINDOW_RESIZABLE
|
|
|
|
|
@ -144,18 +115,13 @@ init_sdl :: proc() -> int {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load font - try system fonts first, fallback to a basic one
|
|
|
|
|
font_paths := []string{
|
|
|
|
|
"/usr/share/fonts/TTF/FiraCode-Regular.ttf"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for path in font_paths {
|
|
|
|
|
app.font = ttf.OpenFont(strings.clone_to_cstring(path), FONT_SIZE)
|
|
|
|
|
if app.font != nil do break
|
|
|
|
|
}
|
|
|
|
|
font_cstring := strings.clone_to_cstring(app.config.font)
|
|
|
|
|
defer delete(font_cstring)
|
|
|
|
|
fmt.println(font_cstring)
|
|
|
|
|
app.font = ttf.OpenFont(font_cstring, i32(app.config.font_size))
|
|
|
|
|
|
|
|
|
|
if app.font == nil {
|
|
|
|
|
fmt.println("Could not load any font - text rendering will fail")
|
|
|
|
|
log.fatal("Could not load font")
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -467,7 +433,7 @@ handle_scroll :: proc(y: i32) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render :: proc() {
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, COLOR_BACKGROUND.r, COLOR_BACKGROUND.g, COLOR_BACKGROUND.b, COLOR_BACKGROUND.a)
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, app.config.colours.background.r, app.config.colours.background.g, app.config.colours.background.b, app.config.colours.background.a)
|
|
|
|
|
sdl.RenderClear(app.renderer)
|
|
|
|
|
|
|
|
|
|
render_tabs()
|
|
|
|
|
@ -482,16 +448,16 @@ render :: proc() {
|
|
|
|
|
render_tabs :: proc() {
|
|
|
|
|
for i in 0..<len(app.tabs) {
|
|
|
|
|
tab := &app.tabs[i]
|
|
|
|
|
color := COLOR_TAB_INACTIVE if i != app.active_tab else COLOR_TAB_ACTIVE
|
|
|
|
|
color := app.config.colours.inactive_tab if i != app.active_tab else app.config.colours.active_tab
|
|
|
|
|
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, color.r, color.g, color.b, color.a)
|
|
|
|
|
sdl.RenderFillRect(app.renderer, &tab.rect)
|
|
|
|
|
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, COLOR_TAB_BORDER.r, COLOR_TAB_BORDER.g, COLOR_TAB_BORDER.b, COLOR_TAB_BORDER.a)
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, app.config.colours.tab_border.r, app.config.colours.tab_border.g, app.config.colours.tab_border.b, app.config.colours.tab_border.a)
|
|
|
|
|
sdl.RenderDrawRect(app.renderer, &tab.rect)
|
|
|
|
|
|
|
|
|
|
// Render tab text
|
|
|
|
|
render_text(tab.name, tab.rect.x + 10, tab.rect.y + 5, COLOR_TEXT)
|
|
|
|
|
render_text(tab.name, tab.rect.x + 10, tab.rect.y + 5, app.config.colours.text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -500,11 +466,11 @@ render_text_area :: proc() {
|
|
|
|
|
|
|
|
|
|
// Render line number background
|
|
|
|
|
line_bg_rect := sdl.Rect{0, TAB_HEIGHT, LINE_NUMBER_WIDTH, WINDOW_HEIGHT - TAB_HEIGHT}
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, COLOR_LINE_NUMBER_BG.r, COLOR_LINE_NUMBER_BG.g, COLOR_LINE_NUMBER_BG.b, COLOR_LINE_NUMBER_BG.a)
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, app.config.colours.line_numbers_background.r, app.config.colours.line_numbers_background.g, app.config.colours.line_numbers_background.b, app.config.colours.line_numbers_background.a)
|
|
|
|
|
sdl.RenderFillRect(app.renderer, &line_bg_rect)
|
|
|
|
|
|
|
|
|
|
// Render separator line
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, COLOR_TAB_BORDER.r, COLOR_TAB_BORDER.g, COLOR_TAB_BORDER.b, COLOR_TAB_BORDER.a)
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, app.config.colours.tab_border.r, app.config.colours.tab_border.g, app.config.colours.tab_border.b, app.config.colours.tab_border.a)
|
|
|
|
|
sdl.RenderDrawLine(app.renderer, LINE_NUMBER_WIDTH, TAB_HEIGHT, LINE_NUMBER_WIDTH, WINDOW_HEIGHT)
|
|
|
|
|
|
|
|
|
|
// Adjust scroll if cursor is outside visible area
|
|
|
|
|
@ -523,7 +489,7 @@ render_text_area :: proc() {
|
|
|
|
|
|
|
|
|
|
// Render line number
|
|
|
|
|
line_num_str := fmt.tprintf("%d", i + 1)
|
|
|
|
|
render_text(line_num_str, 5, y, COLOR_LINE_NUMBERS)
|
|
|
|
|
render_text(line_num_str, 5, y, app.config.colours.line_numbers)
|
|
|
|
|
|
|
|
|
|
// Render line text with wrapping
|
|
|
|
|
if i < len(buffer.lines) {
|
|
|
|
|
@ -535,7 +501,7 @@ render_text_area :: proc() {
|
|
|
|
|
cursor_x := LINE_NUMBER_WIDTH + TEXT_PADDING + i32(buffer.cursor_col) * app.char_width
|
|
|
|
|
cursor_y := y
|
|
|
|
|
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, COLOR_CURSOR.r, COLOR_CURSOR.g, COLOR_CURSOR.b, COLOR_CURSOR.a)
|
|
|
|
|
sdl.SetRenderDrawColor(app.renderer, app.config.colours.cursor.r, app.config.colours.cursor.g, app.config.colours.cursor.b, app.config.colours.cursor.a)
|
|
|
|
|
cursor_rect := sdl.Rect{cursor_x, cursor_y, 2, app.char_height}
|
|
|
|
|
sdl.RenderFillRect(app.renderer, &cursor_rect)
|
|
|
|
|
}
|
|
|
|
|
@ -586,7 +552,7 @@ render_wrapped_line :: proc(line: string, x, y: i32, max_width: i32) {
|
|
|
|
|
} else {
|
|
|
|
|
// Render current line and start new one
|
|
|
|
|
if len(current_line) > 0 {
|
|
|
|
|
render_text(current_line, x, line_y, COLOR_TEXT)
|
|
|
|
|
render_text(current_line, x, line_y, app.config.colours.text)
|
|
|
|
|
line_y += app.char_height
|
|
|
|
|
}
|
|
|
|
|
current_line = word
|
|
|
|
|
@ -595,8 +561,7 @@ render_wrapped_line :: proc(line: string, x, y: i32, max_width: i32) {
|
|
|
|
|
|
|
|
|
|
// Render final line
|
|
|
|
|
if len(current_line) > 0 {
|
|
|
|
|
render_text(current_line, x, line_y, COLOR_TEXT)
|
|
|
|
|
render_text(current_line, x, line_y, app.config.colours.text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|