feat: split word processor app out into standalone Window Server app

This commit is contained in:
2026-03-26 18:05:14 +01:00
parent afe0442d57
commit d0ceefd8ef
12 changed files with 2033 additions and 1774 deletions
@@ -0,0 +1,259 @@
/*
* wordprocessor.hpp
* Shared declarations for the Word Processor app
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/heap.h>
#include <gui/gui.hpp>
#include <gui/standalone.hpp>
#include <gui/truetype.hpp>
#include <gui/svg.hpp>
extern "C" {
#include <string.h>
#include <stdio.h>
}
using namespace gui;
static constexpr int INIT_W = 640;
static constexpr int INIT_H = 480;
static constexpr int WP_TOOLBAR_H = 36;
static constexpr int WP_PATHBAR_H = 32;
static constexpr int WP_STATUS_H = 24;
static constexpr int WP_SCROLLBAR_W = 12;
static constexpr int WP_MARGIN = 16;
static constexpr int WP_MAX_RUNS = 1024;
static constexpr int WP_MAX_TEXT = 262144;
static constexpr int WP_MAX_WRAP_LINES = 4096;
static constexpr int WP_DEFAULT_SIZE = 18;
static constexpr int FONT_ROBOTO = 0;
static constexpr int FONT_NOTOSERIF = 1;
static constexpr int FONT_C059 = 2;
static constexpr int FONT_COUNT = 3;
static constexpr uint8_t STYLE_BOLD = 0x01;
static constexpr uint8_t STYLE_ITALIC = 0x02;
inline constexpr const char* WP_FONT_NAMES[FONT_COUNT] = {
"Roboto",
"NotoSerif",
"C059",
};
inline constexpr int WP_SIZE_OPTIONS[] = { 12, 14, 16, 18, 20, 24, 28, 36 };
static constexpr int WP_SIZE_OPTION_COUNT = 8;
struct WPFontTable {
TrueTypeFont* fonts[FONT_COUNT][4];
bool loaded;
};
struct StyledRun {
char* text;
int len;
int cap;
uint8_t font_id;
uint8_t size;
uint8_t flags;
};
struct WrapLine {
int run_idx;
int run_offset;
int char_count;
int y;
int height;
int baseline;
};
inline bool wp_left_held(uint8_t buttons) {
return (buttons & 0x01) != 0;
}
inline bool wp_left_pressed(uint8_t buttons, uint8_t prev_buttons) {
return (buttons & 0x01) && !(prev_buttons & 0x01);
}
inline bool wp_left_released(uint8_t buttons, uint8_t prev_buttons) {
return !(buttons & 0x01) && (prev_buttons & 0x01);
}
struct WpScrollbar {
Rect bounds;
int content_height;
int view_height;
int scroll_offset;
bool dragging;
int drag_start_y;
int drag_start_offset;
Color bg;
Color fg;
Color hover_fg;
bool hovered;
void init(int x, int y, int w, int h) {
bounds = {x, y, w, h};
content_height = 0;
view_height = h;
scroll_offset = 0;
dragging = false;
drag_start_y = 0;
drag_start_offset = 0;
bg = colors::SCROLLBAR_BG;
fg = colors::SCROLLBAR_FG;
hover_fg = Color::from_rgb(0xA0, 0xA0, 0xA0);
hovered = false;
}
int thumb_height() const {
if (content_height <= view_height) return bounds.h;
int th = (view_height * bounds.h) / content_height;
return th < 20 ? 20 : th;
}
int thumb_y() const {
if (content_height <= view_height) return bounds.y;
int range = bounds.h - thumb_height();
int max_scroll = content_height - view_height;
if (max_scroll <= 0) return bounds.y;
return bounds.y + (scroll_offset * range) / max_scroll;
}
int max_scroll() const {
int ms = content_height - view_height;
return ms > 0 ? ms : 0;
}
void handle_mouse(int x, int y, uint8_t buttons, uint8_t prev_buttons, int scroll) {
if (content_height <= view_height) return;
Rect thumb_rect = {bounds.x, thumb_y(), bounds.w, thumb_height()};
hovered = thumb_rect.contains(x, y);
if (hovered && wp_left_pressed(buttons, prev_buttons)) {
dragging = true;
drag_start_y = y;
drag_start_offset = scroll_offset;
}
if (dragging && wp_left_held(buttons)) {
int dy = y - drag_start_y;
int range = bounds.h - thumb_height();
if (range > 0) {
int ms = max_scroll();
scroll_offset = drag_start_offset + (dy * ms) / range;
if (scroll_offset < 0) scroll_offset = 0;
if (scroll_offset > ms) scroll_offset = ms;
}
}
if (!wp_left_held(buttons)) {
dragging = false;
}
if (bounds.contains(x, y) && scroll != 0) {
scroll_offset += scroll * 20;
int ms = max_scroll();
if (scroll_offset < 0) scroll_offset = 0;
if (scroll_offset > ms) scroll_offset = ms;
}
}
};
struct WordProcessorState {
StyledRun runs[WP_MAX_RUNS];
int run_count;
int total_text_len;
int cursor_run;
int cursor_offset;
int sel_anchor;
int sel_end;
bool has_selection;
bool mouse_selecting;
uint8_t cur_font_id;
uint8_t cur_size;
uint8_t cur_flags;
WpScrollbar scrollbar;
int content_height;
WrapLine* wrap_lines;
int wrap_line_count;
bool wrap_dirty;
int last_wrap_width;
bool modified;
char filepath[256];
char filename[64];
bool show_pathbar;
bool pathbar_save_mode;
char pathbar_text[256];
int pathbar_cursor;
int pathbar_len;
bool font_dropdown_open;
bool size_dropdown_open;
};
extern int g_win_w;
extern int g_win_h;
extern WsWindow g_win;
extern WordProcessorState g_wp;
extern WPFontTable g_wp_fonts;
extern SvgIcon g_icon_folder;
extern SvgIcon g_icon_save;
extern TrueTypeFont* g_ui_font;
extern TrueTypeFont* g_ui_bold;
void wp_load_fonts();
void wp_load_icons();
void wp_init_empty_document(WordProcessorState* wp);
void wp_free_document(WordProcessorState* wp);
void wp_cleanup_state();
TrueTypeFont* wp_get_font(int font_id, uint8_t flags);
int wp_abs_pos(WordProcessorState* wp, int run, int offset);
void wp_pos_to_run(WordProcessorState* wp, int abs_pos, int* out_run, int* out_offset);
char wp_char_at(WordProcessorState* wp, int abs_pos);
void wp_insert_char(WordProcessorState* wp, char c);
void wp_delete_char(WordProcessorState* wp);
void wp_backspace(WordProcessorState* wp);
void wp_cursor_left(WordProcessorState* wp);
void wp_cursor_right(WordProcessorState* wp);
void wp_cursor_up(WordProcessorState* wp);
void wp_cursor_down(WordProcessorState* wp);
void wp_clear_selection(WordProcessorState* wp);
void wp_sel_range(WordProcessorState* wp, int* out_start, int* out_end);
void wp_start_selection(WordProcessorState* wp);
void wp_update_selection_to_cursor(WordProcessorState* wp);
void wp_apply_style_to_selection(WordProcessorState* wp, int mode, int value);
void wp_delete_selection(WordProcessorState* wp);
void wp_recompute_wrap(WordProcessorState* wp, int content_w);
int wp_find_wrap_line(WordProcessorState* wp, int abs_pos);
int wp_wrap_line_start(WordProcessorState* wp, int line_idx);
void wp_ensure_cursor_visible(WordProcessorState* wp, int view_h);
void wp_set_filepath(WordProcessorState* wp, const char* path);
void wp_open_save_pathbar(WordProcessorState* wp);
void wp_save_file(WordProcessorState* wp);
void wp_load_file(WordProcessorState* wp, const char* path);
void wp_render();
void wp_handle_mouse(const Montauk::WinEvent& ev);
void wp_handle_key(const Montauk::KeyEvent& key);