feat: add A4 page option to Word Processor
This commit is contained in:
@@ -152,15 +152,15 @@ static const char* wp_divider_label(uint8_t divider_type) {
|
||||
}
|
||||
}
|
||||
|
||||
static void wp_draw_list_marker(Canvas& c, WordProcessorState* wp, WrapLine* wl, int py) {
|
||||
static void wp_draw_list_marker(Canvas& c, WordProcessorState* wp, WrapLine* wl, int py, int page_off_x) {
|
||||
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;
|
||||
int marker_x = page_off_x + WP_MARGIN + wp_scale_layout_units(para->left_indent + para->first_line_indent, WP_SCREEN_DPI);
|
||||
if (marker_x < page_off_x + 4) marker_x = page_off_x + 4;
|
||||
|
||||
if (para->list_type == PARA_LIST_BULLET) {
|
||||
int bullet_x = marker_x + wp_scale_layout_units(7, WP_SCREEN_DPI);
|
||||
@@ -293,6 +293,22 @@ void wp_render() {
|
||||
draw_text(c, font ? font : g_ui_font, WP_BTN_SECTION_X + 6, 8, section, colors::TEXT_COLOR, fonts::UI_SIZE);
|
||||
}
|
||||
|
||||
c.vline(WP_BTN_PAGEVIEW_X - 4, 4, 28, colors::BORDER);
|
||||
|
||||
Color pageview_bg = wp->page_view_mode ? btn_active : btn_bg;
|
||||
c.fill_rounded_rect(WP_BTN_PAGEVIEW_X, 6, 24, 24, 3, pageview_bg);
|
||||
{
|
||||
// Draw a small page icon: rectangle with lines
|
||||
int px = WP_BTN_PAGEVIEW_X + 4;
|
||||
int py = 9;
|
||||
// Page outline
|
||||
c.rect(px, py, 14, 18, colors::TEXT_COLOR);
|
||||
// Lines inside page
|
||||
c.hline(px + 2, py + 4, 10, colors::TEXT_COLOR);
|
||||
c.hline(px + 2, py + 8, 10, colors::TEXT_COLOR);
|
||||
c.hline(px + 2, py + 12, 6, colors::TEXT_COLOR);
|
||||
}
|
||||
|
||||
c.hline(0, WP_TOOLBAR_H - 1, c.w, colors::BORDER);
|
||||
|
||||
int edit_y = WP_TOOLBAR_H;
|
||||
@@ -330,12 +346,40 @@ void wp_render() {
|
||||
}
|
||||
|
||||
int text_area_h = c.h - edit_y - WP_STATUS_H;
|
||||
wp_recompute_wrap(wp, c.w, WP_SCREEN_DPI);
|
||||
|
||||
// Handle pending page view toggle
|
||||
if (wp->pending_page_view_toggle) {
|
||||
wp->page_view_mode = !wp->page_view_mode;
|
||||
wp->pending_page_view_toggle = false;
|
||||
wp->wrap_dirty = true;
|
||||
}
|
||||
|
||||
// Page view mode: compute A4 page position and use A4 content width
|
||||
int page_x = 0;
|
||||
int page_y = edit_y;
|
||||
int page_content_w = c.w;
|
||||
bool in_page_view = wp->page_view_mode;
|
||||
if (in_page_view) {
|
||||
// Center A4 page horizontally in the window
|
||||
page_x = (c.w - WP_A4_PAGE_W) / 2;
|
||||
if (page_x < 0) page_x = 0;
|
||||
// Page top with margin from edit area top
|
||||
page_y = edit_y + WP_PAGE_TOP_MARGIN;
|
||||
// Use full A4 page width for wrap computation to get proper A4 margins
|
||||
// (WP_MARGIN is used for left margin, right margin comes from WP_A4_PAGE_W)
|
||||
page_content_w = WP_A4_PAGE_W;
|
||||
}
|
||||
|
||||
wp_recompute_wrap(wp, page_content_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;
|
||||
|
||||
// In page view mode, keep scrollbar at right edge but use text_area_h for proper thumb sizing
|
||||
// The scrollbar position doesn't change - it stays at c.w - WP_SCROLLBAR_W
|
||||
// But the content scrolls within the A4 page view
|
||||
|
||||
{
|
||||
int ms = wp->scrollbar.max_scroll();
|
||||
if (wp->scrollbar.scroll_offset > ms) wp->scrollbar.scroll_offset = ms;
|
||||
@@ -350,21 +394,53 @@ void wp_render() {
|
||||
if (wp->has_selection) wp_sel_range(wp, &sel_s, &sel_e);
|
||||
Color sel_bg = Color::from_rgb(0xB0, 0xD0, 0xF0);
|
||||
|
||||
// In page view mode, draw the A4 page with shadow before text rendering
|
||||
if (in_page_view) {
|
||||
// Shadow strips around the page (4 sides + corners, drawn as solid dark rectangles)
|
||||
int so = WP_PAGE_SHADOW_OFFSET;
|
||||
Color shadow_col = Color::from_rgb(0x50, 0x50, 0x50);
|
||||
// Top shadow (above page, full width)
|
||||
c.fill_rect(page_x - so, page_y - so, WP_A4_PAGE_W + so * 2, so, shadow_col);
|
||||
// Bottom shadow
|
||||
c.fill_rect(page_x - so, page_y + WP_A4_PAGE_H, WP_A4_PAGE_W + so * 2, so, shadow_col);
|
||||
// Left shadow (vertical strip, excluding corners already covered)
|
||||
c.fill_rect(page_x - so, page_y, so, WP_A4_PAGE_H, shadow_col);
|
||||
// Right shadow (vertical strip, excluding corners)
|
||||
c.fill_rect(page_x + WP_A4_PAGE_W, page_y, so, WP_A4_PAGE_H, shadow_col);
|
||||
// Corner pieces (top-left, top-right, bottom-left, bottom-right)
|
||||
c.fill_rect(page_x - so, page_y - so, so, so, shadow_col);
|
||||
c.fill_rect(page_x + WP_A4_PAGE_W, page_y - so, so, so, shadow_col);
|
||||
c.fill_rect(page_x - so, page_y + WP_A4_PAGE_H, so, so, shadow_col);
|
||||
c.fill_rect(page_x + WP_A4_PAGE_W, page_y + WP_A4_PAGE_H, so, so, shadow_col);
|
||||
// White page background
|
||||
c.fill_rect(page_x, page_y, WP_A4_PAGE_W, WP_A4_PAGE_H, colors::WHITE);
|
||||
}
|
||||
|
||||
for (int li = 0; li < wp->wrap_line_count; li++) {
|
||||
WrapLine* wl = &wp->wrap_lines[li];
|
||||
int py = edit_y + wl->y - scroll_y;
|
||||
// In page view mode, offset y by page_y
|
||||
if (in_page_view) {
|
||||
py = page_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);
|
||||
// In page view mode, offset x by page_x for all drawing
|
||||
int draw_x = wl->x;
|
||||
if (in_page_view) {
|
||||
draw_x = page_x + wl->x;
|
||||
}
|
||||
|
||||
wp_draw_list_marker(c, wp, wl, py, in_page_view ? page_x : 0);
|
||||
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);
|
||||
wp_draw_divider_sample(c, draw_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 x = draw_x;
|
||||
int line_abs_start = wp_wrap_line_start(wp, li);
|
||||
int char_idx = 0;
|
||||
|
||||
@@ -395,22 +471,56 @@ void wp_render() {
|
||||
|
||||
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);
|
||||
// Clip selection to page bounds in page view mode
|
||||
int sel_x = x;
|
||||
int sel_y = py;
|
||||
int sel_h = wl->height;
|
||||
if (in_page_view) {
|
||||
sel_x = gui_max(x, page_x);
|
||||
int right = gui_min(x + sel_w, page_x + WP_A4_PAGE_W);
|
||||
sel_w = right - sel_x;
|
||||
sel_y = gui_max(py, page_y);
|
||||
int bot = gui_min(py + sel_h, page_y + WP_A4_PAGE_H);
|
||||
sel_h = bot - sel_y;
|
||||
if (sel_w <= 0 || sel_h <= 0) sel_w = 0;
|
||||
}
|
||||
if (sel_w > 0)
|
||||
c.fill_rect(sel_x, sel_y, sel_w, sel_h, 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);
|
||||
int cur_x = x;
|
||||
int cur_y = py;
|
||||
int cur_w = 2;
|
||||
if (in_page_view) {
|
||||
// Clip cursor to page bounds
|
||||
if (cur_x < page_x) { cur_w = cur_x + cur_w - page_x; cur_x = page_x; }
|
||||
if (cur_x + cur_w > page_x + WP_A4_PAGE_W) cur_w = page_x + WP_A4_PAGE_W - cur_x;
|
||||
cur_y = gui_max(cur_y, page_y);
|
||||
int bot = gui_min(cur_y + cur_h, page_y + WP_A4_PAGE_H);
|
||||
cur_h = bot - cur_y;
|
||||
if (cur_h <= 0 || cur_w <= 0) cur_x = -1; // hide
|
||||
}
|
||||
if (cur_x >= 0 && py >= edit_y && py + cur_h <= edit_y + text_area_h)
|
||||
c.fill_rect(cur_x, cur_y, cur_w, cur_h, colors::ACCENT);
|
||||
}
|
||||
|
||||
if (ch != '\n' && (ch >= 32 || ch < 0)) {
|
||||
if (x < c.w - WP_SCROLLBAR_W - WP_MARGIN) {
|
||||
// In page view mode, clip to page bounds
|
||||
bool can_draw = (x < c.w - WP_SCROLLBAR_W - WP_MARGIN);
|
||||
if (in_page_view) {
|
||||
can_draw = (x + char_adv > page_x && x < page_x + WP_A4_PAGE_W &&
|
||||
py + wl->height > page_y && py < page_y + WP_A4_PAGE_H);
|
||||
}
|
||||
if (can_draw) {
|
||||
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;
|
||||
} else {
|
||||
x += char_adv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,8 +537,18 @@ void wp_render() {
|
||||
|
||||
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);
|
||||
int cur_x = x;
|
||||
int cur_y = py;
|
||||
if (in_page_view) {
|
||||
if (cur_x < page_x) cur_x = page_x;
|
||||
if (cur_x > page_x + WP_A4_PAGE_W) cur_x = -1;
|
||||
cur_y = gui_max(cur_y, page_y);
|
||||
int bot = gui_min(cur_y + cur_h, page_y + WP_A4_PAGE_H);
|
||||
cur_h = bot - cur_y;
|
||||
if (cur_h <= 0) cur_x = -1;
|
||||
}
|
||||
if (cur_x >= 0 && py >= edit_y && py + cur_h <= edit_y + text_area_h)
|
||||
c.fill_rect(cur_x, cur_y, 2, cur_h, colors::ACCENT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user