Files
MontaukOS/programs/src/wordprocessor/render.cpp
T

561 lines
23 KiB
C++

/*
* render.cpp
* Rendering for the standalone MontaukOS Word Processor
* Copyright (c) 2026 Daniel Hammer
*/
#include "wordprocessor.hpp"
static int wp_ui_line_height() {
if (g_ui_font && g_ui_font->valid)
return g_ui_font->get_line_height(fonts::UI_SIZE);
return fonts::UI_SIZE;
}
static void wp_draw_ui_text(Canvas& c, int x, int y, const char* text, Color color) {
draw_text(c, g_ui_font, x, y, text, color, fonts::UI_SIZE);
}
static void wp_draw_ui_button(Canvas& c, int x, int y, int w, int h,
const char* label, Color bg, Color fg, int radius = 3) {
c.fill_rounded_rect(x, y, w, h, radius, bg);
int tw = text_width(g_ui_font, label, fonts::UI_SIZE);
int th = wp_ui_line_height();
draw_text(c, g_ui_font, x + (w - tw) / 2, y + (h - th) / 2, label, fg, fonts::UI_SIZE);
}
static void wp_draw_ui_icon_button(Canvas& c, int x, int y, int w, int h,
const SvgIcon& icon, const char* fallback,
Color bg, Color fg, int radius = 3) {
c.fill_rounded_rect(x, y, w, h, radius, bg);
if (icon.pixels) {
c.icon(x + (w - icon.width) / 2, y + (h - icon.height) / 2, icon);
return;
}
wp_draw_ui_button(c, x, y, w, h, fallback, bg, fg, radius);
}
static int wp_special_char_flyout_x() {
int dx = WP_BTN_SECTION_X + 24 - WP_SPECIAL_CHAR_FLYOUT_W;
if (dx + WP_SPECIAL_CHAR_FLYOUT_W > g_win_w) dx = g_win_w - WP_SPECIAL_CHAR_FLYOUT_W;
return dx < 0 ? 0 : dx;
}
static int wp_divider_flyout_x() {
int dx = WP_BTN_DIVIDER_X + 24 - WP_DIVIDER_FLYOUT_W;
if (dx + WP_DIVIDER_FLYOUT_W > g_win_w) dx = g_win_w - WP_DIVIDER_FLYOUT_W;
return dx < 0 ? 0 : dx;
}
static void wp_draw_divider_sample(Canvas& c, int x, int y, int w, int h,
uint8_t divider_type, Color color) {
if (w <= 0 || h <= 0 || divider_type == PARA_DIVIDER_NONE) return;
int cy = y + h / 2;
switch (divider_type) {
case PARA_DIVIDER_SINGLE:
c.fill_rect(x, cy, w, 1, color);
break;
case PARA_DIVIDER_DOUBLE:
c.fill_rect(x, cy - 2, w, 1, color);
c.fill_rect(x, cy + 2, w, 1, color);
break;
case PARA_DIVIDER_DOTTED:
for (int dx = x; dx < x + w; dx += 5)
c.fill_rect(dx, cy, 2, 1, color);
break;
case PARA_DIVIDER_DASHED:
for (int dx = x; dx < x + w; dx += 11) {
int dash_w = (x + w - dx) < 7 ? (x + w - dx) : 7;
if (dash_w > 0) c.fill_rect(dx, cy, dash_w, 1, color);
}
break;
case PARA_DIVIDER_HEAVY:
c.fill_rect(x, cy - 1, w, 3, color);
break;
case PARA_DIVIDER_THIN_THICK:
c.fill_rect(x, cy - 3, w, 1, color);
c.fill_rect(x, cy, w, 3, color);
break;
case PARA_DIVIDER_THICK_THIN:
c.fill_rect(x, cy - 3, w, 3, color);
c.fill_rect(x, cy + 2, w, 1, color);
break;
}
}
static void wp_fit_ui_text(const char* src, char* out, int out_len, int max_w) {
if (!src || !src[0]) {
if (out_len > 0) out[0] = '\0';
return;
}
montauk::strncpy(out, src, out_len - 1);
out[out_len - 1] = '\0';
if (text_width(g_ui_font, out, fonts::UI_SIZE) <= max_w) return;
int slen = montauk::slen(src);
for (int keep = slen; keep > 1; keep--) {
char candidate[256];
candidate[0] = '.';
candidate[1] = '.';
int pos = 2;
const char* tail = src + (slen - keep);
while (*tail && pos < (int)sizeof(candidate) - 1)
candidate[pos++] = *tail++;
candidate[pos] = '\0';
if (text_width(g_ui_font, candidate, fonts::UI_SIZE) <= max_w) {
montauk::strncpy(out, candidate, out_len - 1);
out[out_len - 1] = '\0';
return;
}
}
}
static ParagraphStyle* wp_current_paragraph_style(WordProcessorState* wp) {
if (wp->paragraph_count <= 0) {
static ParagraphStyle fallback = { PARA_ALIGN_LEFT, PARA_LIST_NONE, 100, PARA_DIVIDER_NONE, 0, 0, 0, 0 };
return &fallback;
}
int para = wp_find_paragraph_at(wp, wp_abs_pos(wp, wp->cursor_run, wp->cursor_offset));
if (para < 0) para = 0;
if (para >= wp->paragraph_count) para = wp->paragraph_count - 1;
return &wp->paragraphs[para];
}
static const char* wp_align_label(uint8_t align) {
switch (align) {
case PARA_ALIGN_CENTER: return "Center";
case PARA_ALIGN_RIGHT: return "Right";
default: return "Left";
}
}
static const char* wp_list_label(uint8_t list_type) {
switch (list_type) {
case PARA_LIST_BULLET: return "Bullets";
case PARA_LIST_NUMBER: return "Numbers";
default: return "Plain";
}
}
static const char* wp_divider_label(uint8_t divider_type) {
switch (divider_type) {
case PARA_DIVIDER_SINGLE: return "Single";
case PARA_DIVIDER_DOUBLE: return "Double";
case PARA_DIVIDER_DOTTED: return "Dotted";
case PARA_DIVIDER_DASHED: return "Dashed";
case PARA_DIVIDER_HEAVY: return "Heavy";
case PARA_DIVIDER_THIN_THICK: return "Thin/Thick";
case PARA_DIVIDER_THICK_THIN: return "Thick/Thin";
default: return "None";
}
}
static void wp_draw_list_marker(Canvas& c, WordProcessorState* wp, WrapLine* wl, int py) {
if (!wl->first_in_paragraph || wl->paragraph_idx < 0 || wl->paragraph_idx >= wp->paragraph_count)
return;
ParagraphStyle* para = &wp->paragraphs[wl->paragraph_idx];
if (para->divider_type != PARA_DIVIDER_NONE || para->list_type == PARA_LIST_NONE) return;
int marker_x = WP_MARGIN + wp_scale_layout_units(para->left_indent + para->first_line_indent, WP_SCREEN_DPI);
if (marker_x < 4) marker_x = 4;
if (para->list_type == PARA_LIST_BULLET) {
int bullet_x = marker_x + wp_scale_layout_units(7, WP_SCREEN_DPI);
int bullet_r = wp_scale_layout_units(3, WP_SCREEN_DPI);
if (bullet_r < 2) bullet_r = 2;
fill_circle(c, bullet_x, py + wl->height / 2, bullet_r, colors::TEXT_COLOR);
return;
}
char label[16];
snprintf(label, sizeof(label), "%d.", wl->list_number > 0 ? wl->list_number : 1);
if (wp->run_count <= 0) return;
StyledRun* run = &wp->runs[wl->run_idx < wp->run_count ? wl->run_idx : 0];
TrueTypeFont* font = wp_get_font(run->font_id, run->flags);
int font_px = wp_screen_font_pixels(run->size);
int top_y = py + (wl->height - font_px) / 2;
draw_text(c, font ? font : g_ui_font, marker_x, top_y, label, colors::TEXT_COLOR, font_px);
}
void wp_render() {
if (!g_win.pixels) return;
WordProcessorState* wp = &g_wp;
Canvas c = g_win.canvas();
c.fill(colors::WINDOW_BG);
int sfh = wp_ui_line_height();
int cursor_abs = wp_abs_pos(wp, wp->cursor_run, wp->cursor_offset);
ParagraphStyle* cur_para = wp_current_paragraph_style(wp);
Color toolbar_bg = Color::from_rgb(0xF5, 0xF5, 0xF5);
Color btn_bg = Color::from_rgb(0xE8, 0xE8, 0xE8);
Color btn_active = Color::from_rgb(0xC0, 0xD0, 0xE8);
c.fill_rect(0, 0, c.w, WP_TOOLBAR_H, toolbar_bg);
c.fill_rounded_rect(WP_BTN_OPEN_X, 6, 24, 24, 3, btn_bg);
if (g_icon_folder.pixels)
c.icon(WP_BTN_OPEN_X + 4, 10, g_icon_folder);
c.fill_rounded_rect(WP_BTN_SAVE_X, 6, 24, 24, 3, btn_bg);
if (g_icon_save.pixels)
c.icon(WP_BTN_SAVE_X + 4, 10, g_icon_save);
c.fill_rounded_rect(WP_BTN_PRINT_X, 6, 24, 24, 3, btn_bg);
if (g_icon_print.pixels)
c.icon(WP_BTN_PRINT_X + 4, 10, g_icon_print);
wp_draw_ui_icon_button(c, WP_BTN_EXPORT_X, 6, 24, 24, g_icon_export_pdf, "P",
btn_bg, colors::TEXT_COLOR);
c.vline(WP_BTN_EXPORT_X + 28, 4, 28, colors::BORDER);
wp_draw_ui_icon_button(c, WP_BTN_UNDO_X, 6, 24, 24, g_icon_undo, "U", btn_bg, colors::TEXT_COLOR);
wp_draw_ui_icon_button(c, WP_BTN_REDO_X, 6, 24, 24, g_icon_redo, "R", btn_bg, colors::TEXT_COLOR);
c.vline(WP_BTN_REDO_X + 28, 4, 28, colors::BORDER);
Color bold_bg = (wp->cur_flags & STYLE_BOLD) ? btn_active : btn_bg;
c.fill_rounded_rect(WP_BTN_BOLD_X, 6, 24, 24, 3, bold_bg);
draw_text(c, g_ui_bold ? g_ui_bold : g_ui_font, WP_BTN_BOLD_X + 7, 8, "B", colors::TEXT_COLOR, fonts::UI_SIZE);
Color italic_bg = (wp->cur_flags & STYLE_ITALIC) ? btn_active : btn_bg;
c.fill_rounded_rect(WP_BTN_ITALIC_X, 6, 24, 24, 3, italic_bg);
{
TrueTypeFont* italic_font = wp_get_font(FONT_ROBOTO, STYLE_ITALIC);
draw_text(c, italic_font ? italic_font : g_ui_font, WP_BTN_ITALIC_X + 9, 8, "I", colors::TEXT_COLOR, fonts::UI_SIZE);
}
c.fill_rounded_rect(WP_FONT_DD_X, 6, WP_FONT_DD_W, 24, 3, btn_bg);
wp_draw_ui_text(c, WP_FONT_DD_X + 6, (WP_TOOLBAR_H - sfh) / 2,
WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0],
colors::TEXT_COLOR);
c.fill_rounded_rect(WP_SIZE_DD_X, 6, WP_SIZE_DD_W, 24, 3, btn_bg);
{
char sz[8];
snprintf(sz, sizeof(sz), "%d", (int)wp->cur_size);
wp_draw_ui_text(c, WP_SIZE_DD_X + 8, (WP_TOOLBAR_H - sfh) / 2, sz, colors::TEXT_COLOR);
}
c.vline(WP_SIZE_DD_X + WP_SIZE_DD_W + 6, 4, 28, colors::BORDER);
wp_draw_ui_icon_button(c, WP_BTN_ALIGN_L_X, 6, 24, 24, g_icon_align_left, "L",
cur_para->align == PARA_ALIGN_LEFT ? btn_active : btn_bg, colors::TEXT_COLOR);
wp_draw_ui_icon_button(c, WP_BTN_ALIGN_C_X, 6, 24, 24, g_icon_align_center, "C",
cur_para->align == PARA_ALIGN_CENTER ? btn_active : btn_bg, colors::TEXT_COLOR);
wp_draw_ui_icon_button(c, WP_BTN_ALIGN_R_X, 6, 24, 24, g_icon_align_right, "R",
cur_para->align == PARA_ALIGN_RIGHT ? btn_active : btn_bg, colors::TEXT_COLOR);
c.vline(WP_BTN_ALIGN_R_X + 28, 4, 28, colors::BORDER);
wp_draw_ui_icon_button(c, WP_BTN_BULLET_X, 6, 24, 24, g_icon_list_bullet, "*",
cur_para->list_type == PARA_LIST_BULLET ? btn_active : btn_bg, colors::TEXT_COLOR);
wp_draw_ui_icon_button(c, WP_BTN_NUMBER_X, 6, 24, 24, g_icon_list_number, "1.",
cur_para->list_type == PARA_LIST_NUMBER ? btn_active : btn_bg, colors::TEXT_COLOR);
c.vline(WP_BTN_NUMBER_X + 28, 4, 28, colors::BORDER);
wp_draw_ui_icon_button(c, WP_BTN_OUTDENT_X, 6, 24, 24, g_icon_indent_less, "<", btn_bg, colors::TEXT_COLOR);
wp_draw_ui_icon_button(c, WP_BTN_INDENT_X, 6, 24, 24, g_icon_indent_more, ">", btn_bg, colors::TEXT_COLOR);
c.vline(WP_BTN_INDENT_X + 28, 4, 28, colors::BORDER);
{
char spacing[12];
snprintf(spacing, sizeof(spacing), "%d%%", (int)cur_para->line_spacing);
c.fill_rounded_rect(WP_LINE_DD_X, 6, WP_LINE_DD_W, 24, 3, btn_bg);
wp_draw_ui_text(c, WP_LINE_DD_X + 8, (WP_TOOLBAR_H - sfh) / 2, spacing, colors::TEXT_COLOR);
}
c.vline(WP_BTN_DIVIDER_X - 4, 4, 28, colors::BORDER);
Color divider_bg = (wp->divider_flyout_open || cur_para->divider_type != PARA_DIVIDER_NONE) ? btn_active : btn_bg;
c.fill_rounded_rect(WP_BTN_DIVIDER_X, 6, 24, 24, 3, divider_bg);
uint8_t divider_preview = cur_para->divider_type != PARA_DIVIDER_NONE
? cur_para->divider_type
: (uint8_t)PARA_DIVIDER_SINGLE;
wp_draw_divider_sample(c, WP_BTN_DIVIDER_X + 4, 6, 16, 24,
divider_preview,
colors::TEXT_COLOR);
c.vline(WP_BTN_SECTION_X - 4, 4, 28, colors::BORDER);
Color special_bg = wp->special_char_flyout_open ? btn_active : btn_bg;
c.fill_rounded_rect(WP_BTN_SECTION_X, 6, 24, 24, 3, special_bg);
{
char section[2] = { (char)0xA7, '\0' };
TrueTypeFont* font = wp_get_font(FONT_ROBOTO, 0);
draw_text(c, font ? font : g_ui_font, WP_BTN_SECTION_X + 6, 8, section, colors::TEXT_COLOR, fonts::UI_SIZE);
}
c.hline(0, WP_TOOLBAR_H - 1, c.w, colors::BORDER);
int edit_y = WP_TOOLBAR_H;
if (wp->show_pathbar) {
int pb_y = WP_TOOLBAR_H;
c.fill_rect(0, pb_y, c.w, WP_PATHBAR_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
int inp_x = 8;
int inp_y = pb_y + 4;
int btn_w = 56;
int inp_w = c.w - inp_x - btn_w - 12;
int inp_h = 24;
c.fill_rect(inp_x, inp_y, inp_w, inp_h, colors::WHITE);
c.rect(inp_x, inp_y, inp_w, inp_h, colors::ACCENT);
int text_y = inp_y + (inp_h - sfh) / 2;
wp_draw_ui_text(c, inp_x + 4, text_y, wp->pathbar_text, colors::TEXT_COLOR);
char prefix[256];
int plen = wp->pathbar_cursor;
if (plen > 255) plen = 255;
for (int i = 0; i < plen; i++) prefix[i] = wp->pathbar_text[i];
prefix[plen] = '\0';
int cx = inp_x + 4 + text_width(g_ui_font, prefix, fonts::UI_SIZE);
c.fill_rect(cx, inp_y + 3, 2, inp_h - 6, colors::ACCENT);
int btn_x = inp_x + inp_w + 6;
wp_draw_ui_button(c, btn_x, inp_y, btn_w, inp_h,
wp->pathbar_save_mode ? "Save" : "Open",
colors::ACCENT, colors::WHITE);
c.hline(0, pb_y + WP_PATHBAR_H - 1, c.w, colors::BORDER);
edit_y = WP_TOOLBAR_H + WP_PATHBAR_H;
}
int text_area_h = c.h - edit_y - WP_STATUS_H;
wp_recompute_wrap(wp, c.w, WP_SCREEN_DPI);
wp->scrollbar.bounds = {c.w - WP_SCROLLBAR_W, edit_y, WP_SCROLLBAR_W, text_area_h};
wp->scrollbar.content_height = wp->content_height;
wp->scrollbar.view_height = text_area_h;
{
int ms = wp->scrollbar.max_scroll();
if (wp->scrollbar.scroll_offset > ms) wp->scrollbar.scroll_offset = ms;
if (wp->scrollbar.scroll_offset < 0) wp->scrollbar.scroll_offset = 0;
}
wp_ensure_cursor_visible(wp, text_area_h);
int scroll_y = wp->scrollbar.scroll_offset;
int sel_s = 0;
int sel_e = 0;
if (wp->has_selection) wp_sel_range(wp, &sel_s, &sel_e);
Color sel_bg = Color::from_rgb(0xB0, 0xD0, 0xF0);
for (int li = 0; li < wp->wrap_line_count; li++) {
WrapLine* wl = &wp->wrap_lines[li];
int py = edit_y + wl->y - scroll_y;
if (py + wl->height <= edit_y) continue;
if (py >= edit_y + text_area_h) break;
wp_draw_list_marker(c, wp, wl, py);
if (wl->divider_type != PARA_DIVIDER_NONE)
wp_draw_divider_sample(c, wl->x, py, wl->width, wl->height, wl->divider_type, colors::TEXT_COLOR);
int chars_left = wl->char_count;
int ri = wl->run_idx;
int ro = wl->run_offset;
int x = wl->x;
int line_abs_start = wp_wrap_line_start(wp, li);
int char_idx = 0;
while (chars_left > 0 && ri < wp->run_count) {
StyledRun* r = &wp->runs[ri];
TrueTypeFont* font = wp_get_font(r->font_id, r->flags);
if (!font || !font->valid) {
ri++;
ro = 0;
continue;
}
GlyphCache* gc = font->get_cache(wp_screen_font_pixels(r->size));
int baseline = py + wl->baseline;
int avail = r->len - ro;
int to_draw = avail < chars_left ? avail : chars_left;
for (int ci = 0; ci < to_draw; ci++) {
char ch = r->text[ro + ci];
int abs_ch = line_abs_start + char_idx;
int char_adv = 0;
if (ch != '\n' && (ch >= 32 || ch < 0)) {
CachedGlyph* g = font->get_glyph(gc, (unsigned char)ch);
char_adv = g ? g->advance : 8;
}
if (wp->has_selection && abs_ch >= sel_s && abs_ch < sel_e) {
int sel_w = char_adv > 0 ? char_adv : 6;
c.fill_rect(x, py, sel_w, wl->height, sel_bg);
}
if (abs_ch == cursor_abs) {
int cur_h = wl->height;
if (py >= edit_y && py + cur_h <= edit_y + text_area_h)
c.fill_rect(x, py, 2, cur_h, colors::ACCENT);
}
if (ch != '\n' && (ch >= 32 || ch < 0)) {
if (x < c.w - WP_SCROLLBAR_W - WP_MARGIN) {
Color text_col = (wp->has_selection && abs_ch >= sel_s && abs_ch < sel_e)
? Color::from_rgb(0x10, 0x10, 0x10) : colors::TEXT_COLOR;
int adv = font->draw_char_to_buffer(
c.pixels, c.w, c.h, x, baseline, (unsigned char)ch, text_col, gc);
x += adv;
}
}
char_idx++;
}
chars_left -= to_draw;
ro += to_draw;
if (ro >= r->len) {
ri++;
ro = 0;
}
}
if (line_abs_start + char_idx == cursor_abs && li == wp->wrap_line_count - 1) {
int cur_h = wl->height;
if (py >= edit_y && py + cur_h <= edit_y + text_area_h)
c.fill_rect(x, py, 2, cur_h, colors::ACCENT);
}
}
if (wp->scrollbar.content_height > wp->scrollbar.view_height) {
Color sb_fg = (wp->scrollbar.hovered || wp->scrollbar.dragging)
? wp->scrollbar.hover_fg : wp->scrollbar.fg;
int sbx = wp->scrollbar.bounds.x;
int sby = wp->scrollbar.bounds.y;
int sbw = wp->scrollbar.bounds.w;
int sbh = wp->scrollbar.bounds.h;
c.fill_rect(sbx, sby, sbw, sbh, colors::SCROLLBAR_BG);
int th = wp->scrollbar.thumb_height();
int tty = wp->scrollbar.thumb_y();
c.fill_rect(sbx + 1, tty, sbw - 2, th, sb_fg);
}
int status_y = c.h - WP_STATUS_H;
c.fill_rect(0, status_y, c.w, WP_STATUS_H, Color::from_rgb(0x2B, 0x3E, 0x50));
int status_text_y = status_y + (WP_STATUS_H - sfh) / 2;
char status_left[320];
const char* name = wp->filename[0] ? wp->filename : "Untitled";
if (cur_para->divider_type != PARA_DIVIDER_NONE) {
snprintf(status_left, sizeof(status_left), " %s%s | %s %dpt | Divider: %s | %d%%",
name, wp->modified ? " *" : "",
WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0], (int)wp->cur_size,
wp_divider_label(cur_para->divider_type), (int)cur_para->line_spacing);
} else {
snprintf(status_left, sizeof(status_left), " %s%s | %s %dpt | %s | %s | %d%%",
name, wp->modified ? " *" : "",
WP_FONT_NAMES[wp->cur_font_id < FONT_COUNT ? wp->cur_font_id : 0], (int)wp->cur_size,
wp_align_label(cur_para->align), wp_list_label(cur_para->list_type),
(int)cur_para->line_spacing);
}
char status_right[32];
snprintf(status_right, sizeof(status_right), "%d chars ", wp->total_text_len);
int sr_w = text_width(g_ui_font, status_right, fonts::UI_SIZE);
char status_combined[320];
if (wp->status_msg[0]) snprintf(status_combined, sizeof(status_combined), "%s | %s", status_left, wp->status_msg);
else montauk::strncpy(status_combined, status_left, (int)sizeof(status_combined) - 1);
status_combined[sizeof(status_combined) - 1] = '\0';
char status_fit[320];
wp_fit_ui_text(status_combined, status_fit, sizeof(status_fit), c.w - sr_w - 12);
wp_draw_ui_text(c, 4, status_text_y, status_fit, colors::PANEL_TEXT);
wp_draw_ui_text(c, c.w - sr_w - 4, status_text_y, status_right, colors::PANEL_TEXT);
if (wp->font_dropdown_open) {
int dx = WP_FONT_DD_X;
int dy = WP_TOOLBAR_H;
int dw = 110;
int dh = FONT_COUNT * 26 + 4;
c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
c.rect(dx, dy, dw, dh, colors::BORDER);
for (int i = 0; i < FONT_COUNT; i++) {
int iy = dy + 2 + i * 26;
if (i == wp->cur_font_id)
c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, WP_FONT_NAMES[i], colors::TEXT_COLOR);
}
}
if (wp->size_dropdown_open) {
int dx = WP_SIZE_DD_X;
int dy = WP_TOOLBAR_H;
int dw = 56;
int dh = WP_SIZE_OPTION_COUNT * 26 + 4;
c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
c.rect(dx, dy, dw, dh, colors::BORDER);
for (int i = 0; i < WP_SIZE_OPTION_COUNT; i++) {
int iy = dy + 2 + i * 26;
if (WP_SIZE_OPTIONS[i] == wp->cur_size)
c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
char sz[8];
snprintf(sz, sizeof(sz), "%d", WP_SIZE_OPTIONS[i]);
wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, sz, colors::TEXT_COLOR);
}
}
if (wp->line_spacing_dropdown_open) {
int dx = WP_LINE_DD_X;
int dy = WP_TOOLBAR_H;
int dw = 64;
int dh = WP_LINE_SPACING_OPTION_COUNT * 26 + 4;
c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
c.rect(dx, dy, dw, dh, colors::BORDER);
for (int i = 0; i < WP_LINE_SPACING_OPTION_COUNT; i++) {
int iy = dy + 2 + i * 26;
if (WP_LINE_SPACING_OPTIONS[i] == cur_para->line_spacing)
c.fill_rect(dx + 2, iy, dw - 4, 24, colors::MENU_HOVER);
char spacing[12];
snprintf(spacing, sizeof(spacing), "%d%%", WP_LINE_SPACING_OPTIONS[i]);
wp_draw_ui_text(c, dx + 8, iy + (24 - sfh) / 2, spacing, colors::TEXT_COLOR);
}
}
if (wp->divider_flyout_open) {
int dx = wp_divider_flyout_x();
int dy = WP_TOOLBAR_H;
int dw = WP_DIVIDER_FLYOUT_W;
int dh = WP_DIVIDER_OPTION_COUNT * WP_DIVIDER_ROW_H + 4;
c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
c.rect(dx, dy, dw, dh, colors::BORDER);
for (int i = 0; i < WP_DIVIDER_OPTION_COUNT; i++) {
int iy = dy + 2 + i * WP_DIVIDER_ROW_H;
if (WP_DIVIDER_OPTIONS[i].type == cur_para->divider_type)
c.fill_rect(dx + 2, iy, dw - 4, WP_DIVIDER_ROW_H - 2, colors::MENU_HOVER);
wp_draw_divider_sample(c, dx + 10, iy, 56, WP_DIVIDER_ROW_H,
WP_DIVIDER_OPTIONS[i].type, colors::TEXT_COLOR);
wp_draw_ui_text(c, dx + 78, iy + (WP_DIVIDER_ROW_H - sfh) / 2,
WP_DIVIDER_OPTIONS[i].label, colors::TEXT_COLOR);
}
}
if (wp->special_char_flyout_open) {
int dx = wp_special_char_flyout_x();
int dy = WP_TOOLBAR_H;
int dw = WP_SPECIAL_CHAR_FLYOUT_W;
int dh = WP_SPECIAL_CHAR_OPTION_COUNT * WP_SPECIAL_CHAR_ROW_H + 4;
TrueTypeFont* glyph_font = wp_get_font(FONT_ROBOTO, 0);
c.fill_rect(dx, dy, dw, dh, colors::MENU_BG);
c.rect(dx, dy, dw, dh, colors::BORDER);
for (int i = 0; i < WP_SPECIAL_CHAR_OPTION_COUNT; i++) {
int iy = dy + 2 + i * WP_SPECIAL_CHAR_ROW_H;
char glyph[2] = { (char)WP_SPECIAL_CHAR_OPTIONS[i].code, '\0' };
c.fill_rect(dx + 2, iy, dw - 4, WP_SPECIAL_CHAR_ROW_H - 2, colors::MENU_BG);
draw_text(c, glyph_font ? glyph_font : g_ui_font,
dx + 8, iy + (WP_SPECIAL_CHAR_ROW_H - sfh) / 2,
glyph, colors::TEXT_COLOR, fonts::UI_SIZE);
wp_draw_ui_text(c, dx + 32, iy + (WP_SPECIAL_CHAR_ROW_H - sfh) / 2,
WP_SPECIAL_CHAR_OPTIONS[i].label, colors::TEXT_COLOR);
}
}
}