feat: split word processor app out into standalone Window Server app
This commit is contained in:
@@ -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);
|
||||
Reference in New Issue
Block a user