Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions prose_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ typedef struct {
#define CLR_SEARCH_HL (g_theme.search_hl)

/* ── Layout constants ── */
#define TITLEBAR_H 40
#define TABBAR_H 38
#define TITLEBAR_H 36
#define TABBAR_H 0
#define MENUBAR_H 0
#define STATUSBAR_H 30
#define GUTTER_PAD 16
Expand Down Expand Up @@ -333,11 +333,11 @@ typedef struct {
wchar_t autosave_dir[MAX_PATH];
unsigned int next_autosave_id;

int menu_open;
int menu_hover_item;
int menu_bar_widths[MENU_COUNT];
int menu_open; /* -1 = closed, 0 = combined dropdown open */
int menu_hover_item; /* flat index across all menus */

int fab_hover;
int dropdown_hover; /* 1 if hovering the dropdown trigger button */
int dropdown_btn_x; /* computed x position of dropdown button */

int spellcheck_enabled;

Expand Down
211 changes: 111 additions & 100 deletions render.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,52 +25,69 @@ void render_titlebar(HDC hdc) {
int th = DPI(TITLEBAR_H);
fill_rect(hdc, 0, 0, g_editor.client_w, th, CLR_BG_DARK);

SelectObject(hdc, g_editor.font_title);
SetBkMode(hdc, TRANSPARENT);

static const wchar_t *fab_icons[MENU_COUNT] = {
L"\x2630", L"\x270E", L"\x2315", L"\x2699",
};
/* Tabs (starting from left edge) */
int tx = DPI(6);
int right_limit = g_editor.client_w - DPI(46) * 3 - DPI(8);
SelectObject(hdc, g_editor.font_ui_small);

int fab_size = DPI(28);
int fab_pad = DPI(6);
int fab_start_x = DPI(8);
int fab_y = (th - fab_size) / 2;
for (int i = 0; i < g_editor.tab_count; i++) {
Document *doc = g_editor.tabs[i];
wchar_t label[128];
swprintf(label, 128, L"%ls%ls", doc->title, doc->modified ? L" \x2022" : L"");
int tw = (int)wcslen(label) * DPI(8) + DPI(TAB_PAD) * 2;
if (tw < DPI(TAB_MIN_W)) tw = DPI(TAB_MIN_W);
if (tw > DPI(TAB_MAX_W)) tw = DPI(TAB_MAX_W);

SelectObject(hdc, g_editor.font_ui_small);
for (int i = 0; i < MENU_COUNT; i++) {
int fx = fab_start_x + i * (fab_size + fab_pad);

COLORREF fab_bg;
if (g_editor.menu_open == i) {
fab_bg = CLR_ACCENT;
} else if (g_editor.fab_hover == i) {
fab_bg = g_theme.is_dark ? RGB(50, 50, 62) : RGB(220, 220, 225);
if (tx + tw > right_limit) break;

if (i == g_editor.active_tab) {
fill_rounded_rect(hdc, tx, DPI(5), tw, th - DPI(5), DPI(10), CLR_TAB_ACTIVE);
fill_rounded_rect(hdc, tx + DPI(12), th - DPI(3), tw - DPI(24), DPI(2), DPI(1), CLR_ACCENT);
draw_text(hdc, tx + DPI(TAB_PAD), (th - DPI(12)) / 2, label, (int)wcslen(label), CLR_TEXT);
} else {
fab_bg = g_theme.is_dark ? RGB(38, 38, 46) : RGB(235, 235, 238);
draw_text(hdc, tx + DPI(TAB_PAD), (th - DPI(12)) / 2, label, (int)wcslen(label), CLR_OVERLAY0);
}
fill_rounded_rect(hdc, fx, fab_y, fab_size, fab_size, fab_size / 2, fab_bg);

COLORREF icon_clr = (g_editor.menu_open == i) ?
(g_theme.is_dark ? RGB(255, 255, 255) : RGB(255, 255, 255)) : CLR_SUBTEXT;
SetTextColor(hdc, icon_clr);
RECT fr = { fx, fab_y, fx + fab_size, fab_y + fab_size };
DrawTextW(hdc, fab_icons[i], 1, &fr, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
/* Tab close (×) button — use main font for larger size */
SelectObject(hdc, g_editor.font_ui);
draw_text(hdc, tx + tw - DPI(22), (th - DPI(16)) / 2, L"\x00D7", 1, CLR_OVERLAY0);
SelectObject(hdc, g_editor.font_ui_small);

g_editor.menu_bar_widths[i] = fab_size + fab_pad;
tx += tw + DPI(4);
}

int fab_total_w = MENU_COUNT * (fab_size + fab_pad);
int title_x = fab_start_x + fab_total_w + DPI(12);
/* New tab (+) button — use main font for larger size */
if (tx + DPI(30) <= right_limit) {
SelectObject(hdc, g_editor.font_ui);
draw_text(hdc, tx + DPI(6), (th - DPI(16)) / 2, L"+", 1, CLR_OVERLAY0);
SelectObject(hdc, g_editor.font_ui_small);
}

SelectObject(hdc, g_editor.font_title);
Document *doc = current_doc();
if (doc) {
wchar_t title[256];
swprintf(title, 256, L"%ls%ls", doc->title, doc->modified ? L" \x2022" : L"");
draw_text(hdc, title_x, (th - DPI(16)) / 2, title, (int)wcslen(title), CLR_SUBTEXT);
/* Dropdown trigger button (▾) — after (+) button */
int btn_size = DPI(22);
int btn_x = tx + DPI(32);
int btn_y = (th - btn_size) / 2;
g_editor.dropdown_btn_x = btn_x;

COLORREF btn_bg;
if (g_editor.menu_open >= 0) {
btn_bg = CLR_ACCENT;
} else if (g_editor.dropdown_hover) {
btn_bg = g_theme.is_dark ? RGB(50, 50, 62) : RGB(220, 220, 225);
} else {
btn_bg = g_theme.is_dark ? RGB(38, 38, 46) : RGB(235, 235, 238);
}
fill_rounded_rect(hdc, btn_x, btn_y, btn_size, btn_size, DPI(4), btn_bg);

SelectObject(hdc, g_editor.font_ui_small);
COLORREF icon_clr = (g_editor.menu_open >= 0) ? RGB(255, 255, 255) : CLR_SUBTEXT;
RECT br = { btn_x, btn_y, btn_x + btn_size, btn_y + btn_size };
SetTextColor(hdc, icon_clr);
DrawTextW(hdc, L"\x25BE", 1, &br, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

/* Window control buttons */
int bw = DPI(46), bh = th;
int x = g_editor.client_w - bw * 3;

Expand Down Expand Up @@ -98,38 +115,8 @@ void render_titlebar(HDC hdc) {
}

void render_tabbar(HDC hdc) {
int y = DPI(TITLEBAR_H + MENUBAR_H);
int tbh = DPI(TABBAR_H);
fill_rect(hdc, 0, y, g_editor.client_w, tbh, CLR_BG_DARK);

SelectObject(hdc, g_editor.font_ui_small);
SetBkMode(hdc, TRANSPARENT);

int x = DPI(8);
for (int i = 0; i < g_editor.tab_count; i++) {
Document *doc = g_editor.tabs[i];
wchar_t label[128];
swprintf(label, 128, L"%ls%ls", doc->title, doc->modified ? L" \x2022" : L"");
int tw = (int)wcslen(label) * DPI(8) + DPI(TAB_PAD) * 2;
if (tw < DPI(TAB_MIN_W)) tw = DPI(TAB_MIN_W);
if (tw > DPI(TAB_MAX_W)) tw = DPI(TAB_MAX_W);

if (i == g_editor.active_tab) {
fill_rounded_rect(hdc, x, y + DPI(5), tw, tbh - DPI(5), DPI(10), CLR_TAB_ACTIVE);
fill_rounded_rect(hdc, x + DPI(12), y + tbh - DPI(3), tw - DPI(24), DPI(2), DPI(1), CLR_ACCENT);
draw_text(hdc, x + DPI(TAB_PAD), y + (tbh - DPI(12)) / 2, label, (int)wcslen(label), CLR_TEXT);
} else {
draw_text(hdc, x + DPI(TAB_PAD), y + (tbh - DPI(12)) / 2, label, (int)wcslen(label), CLR_OVERLAY0);
}

draw_text(hdc, x + tw - DPI(20), y + (tbh - DPI(12)) / 2, L"\x00D7", 1, CLR_OVERLAY0);

x += tw + DPI(4);
}

draw_text(hdc, x + DPI(8), y + (tbh - DPI(12)) / 2, L"+", 1, CLR_OVERLAY0);

fill_rect(hdc, 0, y + tbh - 1, g_editor.client_w, 1, CLR_SURFACE0);
/* Tabs are now rendered inline in the titlebar */
(void)hdc;
}

void render_statusbar(HDC hdc) {
Expand Down Expand Up @@ -952,31 +939,35 @@ void render_menubar(HDC hdc) {
}

void render_menu_dropdown(HDC hdc) {
if (g_editor.menu_open < 0 || g_editor.menu_open >= MENU_COUNT) return;
if (g_editor.menu_open < 0) return;

const MenuDef *menu = &g_menus[g_editor.menu_open];
int item_h = DPI(26);
int sep_h = DPI(9);
int header_h = DPI(24);
int dropdown_w = DPI(260);
int pad_x = DPI(12);

int fab_size = DPI(28);
int fab_pad = DPI(6);
int fab_start_x = DPI(8);

int dropdown_x = fab_start_x + g_editor.menu_open * (fab_size + fab_pad);
int dropdown_x = g_editor.dropdown_btn_x;
int dropdown_y = DPI(TITLEBAR_H);

/* Calculate total height across all menus */
int total_h = DPI(4);
for (int i = 0; i < menu->item_count; i++) {
total_h += (menu->items[i].id == MENU_ID_SEP) ? sep_h : item_h;
for (int m = 0; m < MENU_COUNT; m++) {
if (m > 0) total_h += sep_h; /* separator between categories */
total_h += header_h; /* category header */
for (int i = 0; i < g_menus[m].item_count; i++) {
total_h += (g_menus[m].items[i].id == MENU_ID_SEP) ? sep_h : item_h;
}
}
total_h += DPI(4);

/* Shadow */
fill_rounded_rect(hdc, dropdown_x + DPI(2), dropdown_y + DPI(2),
dropdown_w, total_h, DPI(8), RGB(0, 0, 0));
/* Background */
fill_rounded_rect(hdc, dropdown_x, dropdown_y, dropdown_w, total_h, DPI(8),
g_theme.is_dark ? RGB(34, 34, 42) : RGB(248, 248, 248));
/* Border */
HPEN pen = CreatePen(PS_SOLID, 1, CLR_SURFACE1);
HBRUSH hollow = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
HPEN old_pen = (HPEN)SelectObject(hdc, pen);
Expand All @@ -991,40 +982,60 @@ void render_menu_dropdown(HDC hdc) {
SetBkMode(hdc, TRANSPARENT);

int cy = dropdown_y + DPI(4);
for (int i = 0; i < menu->item_count; i++) {
const MenuItem *item = &menu->items[i];
int flat_idx = 0;

if (item->id == MENU_ID_SEP) {
for (int m = 0; m < MENU_COUNT; m++) {
const MenuDef *menu = &g_menus[m];

/* Category separator (between groups, not before the first) */
if (m > 0) {
int line_y = cy + sep_h / 2;
fill_rect(hdc, dropdown_x + pad_x, line_y, dropdown_w - pad_x * 2, 1, CLR_SURFACE0);
cy += sep_h;
continue;
}

if (g_editor.menu_hover_item == i) {
fill_rounded_rect(hdc, dropdown_x + DPI(4), cy, dropdown_w - DPI(8), item_h,
DPI(4), CLR_ACCENT);
SetTextColor(hdc, g_theme.is_dark ? RGB(255, 255, 255) : RGB(255, 255, 255));
}
/* Category header */
SelectObject(hdc, g_editor.font_title);
draw_text(hdc, dropdown_x + pad_x, cy + (header_h - DPI(12)) / 2,
menu->label, (int)wcslen(menu->label), CLR_OVERLAY0);
SelectObject(hdc, g_editor.font_ui_small);
cy += header_h;

/* Menu items */
for (int i = 0; i < menu->item_count; i++) {
const MenuItem *item = &menu->items[i];

if (item->id == MENU_ID_SEP) {
int line_y = cy + sep_h / 2;
fill_rect(hdc, dropdown_x + pad_x, line_y, dropdown_w - pad_x * 2, 1, CLR_SURFACE0);
cy += sep_h;
continue;
}

COLORREF label_clr = (g_editor.menu_hover_item == i)
? (g_theme.is_dark ? RGB(255, 255, 255) : RGB(255, 255, 255))
: CLR_TEXT;
draw_text(hdc, dropdown_x + pad_x, cy + (item_h - DPI(12)) / 2,
item->label, (int)wcslen(item->label), label_clr);

if (item->shortcut) {
SIZE ssz;
GetTextExtentPoint32W(hdc, item->shortcut, (int)wcslen(item->shortcut), &ssz);
COLORREF sc_clr = (g_editor.menu_hover_item == i)
? (g_theme.is_dark ? RGB(205, 210, 230) : RGB(220, 230, 255))
: CLR_OVERLAY0;
draw_text(hdc, dropdown_x + dropdown_w - pad_x - ssz.cx,
cy + (item_h - DPI(12)) / 2,
item->shortcut, (int)wcslen(item->shortcut), sc_clr);
}
if (g_editor.menu_hover_item == flat_idx) {
fill_rounded_rect(hdc, dropdown_x + DPI(4), cy, dropdown_w - DPI(8), item_h,
DPI(4), CLR_ACCENT);
}

cy += item_h;
COLORREF label_clr = (g_editor.menu_hover_item == flat_idx)
? RGB(255, 255, 255) : CLR_TEXT;
draw_text(hdc, dropdown_x + pad_x, cy + (item_h - DPI(12)) / 2,
item->label, (int)wcslen(item->label), label_clr);

if (item->shortcut) {
SIZE ssz;
GetTextExtentPoint32W(hdc, item->shortcut, (int)wcslen(item->shortcut), &ssz);
COLORREF sc_clr = (g_editor.menu_hover_item == flat_idx)
? (g_theme.is_dark ? RGB(205, 210, 230) : RGB(220, 230, 255))
: CLR_OVERLAY0;
draw_text(hdc, dropdown_x + dropdown_w - pad_x - ssz.cx,
cy + (item_h - DPI(12)) / 2,
item->shortcut, (int)wcslen(item->shortcut), sc_clr);
}

flat_idx++;
cy += item_h;
}
}
}

Expand Down
Loading
Loading