From c5181b9cb861f9f2e3b1afbd2782f61ca288a098 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Thu, 20 Aug 2026 07:25:36 -0400 Subject: [PATCH 1/6] Improve support for tabbed columns * Set the menu font to proportional * Do not consider the last column of a line when calculating maximum column widths * Use U+200A HAIR SPACE for finer column padding * Track errors in column padding and adjust subsequent columns so that these errors do not accumulate across a row --- win/Qt/qt_menu.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/win/Qt/qt_menu.cpp b/win/Qt/qt_menu.cpp index f469597e2..f61833dcf 100644 --- a/win/Qt/qt_menu.cpp +++ b/win/Qt/qt_menu.cpp @@ -165,7 +165,7 @@ NetHackQtMenuWindow::NetHackQtMenuWindow(QWidget *parent) : { // setFont() was in SelectMenu(), in time to be rendered but too late // when measuring the width and height that will be needed - QFont tablefont(qt_settings->normalFixedFont()); + QFont tablefont(qt_settings->normalFont()); table->setFont(tablefont); QGridLayout *grid = new QGridLayout(); @@ -400,9 +400,8 @@ void NetHackQtMenuWindow::PadMenuColumns(bool split_descr) continue; // determine column widths of sub-fields within description QStringList columns = itemlist[row].str.split("\t"); - for (int fld = 0; fld < (int) columns.size(); ++fld) { - bool lastcol = (fld == (int) columns.size() - 1); - int w = fm.QFM_WIDTH(columns[fld] + (lastcol ? "" : " ")); + for (int fld = 0; fld < (int) columns.size() - 1; ++fld) { + int w = fm.QFM_WIDTH(columns[fld] + " "); if (fld >= (int) col_widths.size()) { col_widths.push_back(w); // add another element } else if (col_widths[fld] < w) { @@ -432,12 +431,21 @@ void NetHackQtMenuWindow::PadMenuColumns(bool split_descr) continue; QString text = twi->text(); if (split_descr) { + static const QChar u_200A(0x200A); // U+200A HAIR SPACE QStringList columns = text.split("\t"); + int spc0020_width = fm.QFM_WIDTH(" "); + int spc200A_width = fm.QFM_WIDTH(u_200A); + // Track errors in column widths and adjust subsequent columns so + // that these errors do not accumulate across a row + int error = 0; for (int fld = 0; fld < (int) columns.size() - 1; ++fld) { //columns[fld] += "\t"; /* (used to pad with tabs) */ int width = col_widths[fld]; - while (fm.QFM_WIDTH(columns[fld]) < width) + while (fm.QFM_WIDTH(columns[fld]) + error + spc0020_width <= width) columns[fld] += " "; //"\t"; + while (fm.QFM_WIDTH(columns[fld]) + error + spc200A_width/2 < width) + columns[fld] += u_200A; + error += fm.QFM_WIDTH(columns[fld]) - width; } text = columns.join(""); twi->setText(text); From b52ea09e25d01c37969ac4d03e5d7a27a9b77bb4 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Thu, 20 Aug 2026 07:28:01 -0400 Subject: [PATCH 2/6] Tabify the extended command list --- src/cmd.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index cd77a6727..beeba8780 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -674,10 +674,15 @@ doextlist(void) add_menu_heading(menuwin, buf); menushown[pass] = 1; } - /* longest ef_txt at present is "wizrumorcheck" (13 chars); - 2nd field will be " " or " [A]" or " [m]" or "[mA]" */ - Sprintf(buf, " %-14s %4s %s", efp->ef_txt, - doc_extcmd_flagstr(menuwin, efp), cmd_desc); + if (iflags.menu_tab_sep) { + Sprintf(buf, " %s\t%s\t%s", efp->ef_txt, + doc_extcmd_flagstr(menuwin, efp), cmd_desc); + } else { + /* longest ef_txt at present is "wizrumorcheck" (13 chars); + 2nd field will be " " or " [A]" or " [m]" or "[mA]" */ + Sprintf(buf, " %-14s %4s %s", efp->ef_txt, + doc_extcmd_flagstr(menuwin, efp), cmd_desc); + } add_menu_str(menuwin, buf); ++n; } From b08c4f92b266e142024b92c053d297c80b160e87 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Thu, 20 Aug 2026 08:14:13 -0400 Subject: [PATCH 3/6] Tabify more menus * Status hilites * Disclose option * Symbol set selection --- src/botl.c | 8 ++++++-- src/options.c | 9 +++++++-- src/symbols.c | 13 ++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/botl.c b/src/botl.c index 4447cbb5e..301dc2b4c 100644 --- a/src/botl.c +++ b/src/botl.c @@ -4539,9 +4539,13 @@ status_hilite_menu(void) #endif any = cg.zeroany; any.a_int = fld + 1; - Sprintf(buf, "%-18s", initblstats[i].fldname); + if (iflags.menu_tab_sep) { + Sprintf(buf, "%s\t", initblstats[i].fldname); + } else { + Sprintf(buf, "%-18s ", initblstats[i].fldname); + } if (count) - Sprintf(eos(buf), " (%d defined)", count); + Sprintf(eos(buf), "(%d defined)", count); add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); } diff --git a/src/options.c b/src/options.c index 351c3903d..aa555bad1 100644 --- a/src/options.c +++ b/src/options.c @@ -5703,8 +5703,13 @@ handler_disclose(void) start_menu(tmpwin, MENU_BEHAVE_STANDARD); any = cg.zeroany; for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { - Sprintf(buf, "%-12s[%c%c]", disclosure_names[i], - flags.end_disclose[i], disclosure_options[i]); + if (iflags.menu_tab_sep) { + Sprintf(buf, "%s\t[%c%c]", disclosure_names[i], + flags.end_disclose[i], disclosure_options[i]); + } else { + Sprintf(buf, "%-12s[%c%c]", disclosure_names[i], + flags.end_disclose[i], disclosure_options[i]); + } any.a_int = i + 1; add_menu(tmpwin, &nul_glyphinfo, &any, disclosure_options[i], 0, ATR_NONE, clr, buf, MENU_ITEMFLAGS_NONE); diff --git a/src/symbols.c b/src/symbols.c index bd9596a1c..99382b721 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -979,8 +979,6 @@ symparse_find(const void *bstr_, const void *rec_) return 0; } -DISABLE_WARNING_FORMAT_NONLITERAL - /* * this is called from options.c to do the symset work. */ @@ -994,7 +992,7 @@ do_symset(boolean rogueflag) menu_item *symset_pick = (menu_item *) 0; boolean ready_to_switch = FALSE, nothing_to_do = FALSE; - char *symset_name, fmtstr[20]; + char *symset_name; struct symsetentry *sl; int res, which_set, setcount = 0, chosen = -2, defindx = 0; int clr = NO_COLOR; @@ -1041,7 +1039,6 @@ do_symset(boolean rogueflag) return TRUE; } - Sprintf(fmtstr, "%%-%ds %%s", biggest + 2); tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin, MENU_BEHAVE_STANDARD); any = cg.zeroany; @@ -1072,7 +1069,11 @@ do_symset(boolean rogueflag) any.a_int = sl->idx + 2; if (symset_name && !strcmpi(sl->name, symset_name)) defindx = any.a_int; - Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : ""); + if (iflags.menu_tab_sep) { + Sprintf(buf, "%s\t%s", sl->name, sl->desc ? sl->desc : ""); + } else { + Sprintf(buf, "%-*s %s", biggest + 2, sl->name, sl->desc ? sl->desc : ""); + } add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED @@ -1177,6 +1178,4 @@ do_symset(boolean rogueflag) return TRUE; } -RESTORE_WARNING_FORMAT_NONLITERAL - /*symbols.c*/ From 94834df5d1bc5a6d956d719dfdaf6c248f06ff7e Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Fri, 21 Aug 2026 08:19:04 -0400 Subject: [PATCH 4/6] Revise the column width calculation The last column in a line continues to the end of the line, and may overhang any subsequent columns in other lines. --- win/win32/mhmenu.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c index 7579976a6..47204025f 100644 --- a/win/win32/mhmenu.c +++ b/win/win32/mhmenu.c @@ -684,11 +684,16 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect, DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE); - data->menui.menu.tab_stop_size[column] = - max(data->menui.menu.tab_stop_size[column], - drawRect.right - drawRect.left); - - menuitemwidth += data->menui.menu.tab_stop_size[column]; + int width = drawRect.right - drawRect.left; + /* The last column overhangs any subsequent columns in other + lines */ + if (p != NULL) { + data->menui.menu.tab_stop_size[column] = + max(data->menui.menu.tab_stop_size[column], width); + menuitemwidth += data->menui.menu.tab_stop_size[column]; + } else { + menuitemwidth += width; + } if (p != NULL) *p = '\t'; @@ -1182,7 +1187,8 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) p = strchr(item->str, '\t'); column = 0; SetRect(&drawRect, x, lpdis->rcItem.top, - min(x + data->menui.menu.tab_stop_size[0], lpdis->rcItem.right), + p != NULL ? x + data->menui.menu.tab_stop_size[0] + : lpdis->rcItem.right, lpdis->rcItem.bottom); for (;;) { TCHAR wbuf2[BUFSZ]; @@ -1199,8 +1205,8 @@ onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) p = strchr(p1, '\t'); drawRect.left = drawRect.right + TAB_SEPARATION; ++column; - drawRect.right = min(drawRect.left + data->menui.menu.tab_stop_size[column], - lpdis->rcItem.right); + drawRect.right = p != NULL ? drawRect.left + data->menui.menu.tab_stop_size[column] + : lpdis->rcItem.right; } /* draw focused item */ From 7840d75365f3f0c4c1cbc998a3d86a1a3cbccb75 Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 22 Aug 2026 20:43:46 -0400 Subject: [PATCH 5/6] X11: set menu font to proportional Menus that needed to be tabified now are. --- win/X11/NetHack.ad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index f12110f6f..10f1e7a73 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -31,7 +31,7 @@ NetHack*text*borderWidth: 0 ! These correspond to the .nethackrc settings font_map, font_menu, ! font_message, font_status and font_text NetHack.font_map: mono-10 -NetHack.font_menu: mono-10 +NetHack.font_menu: sans-10 NetHack.font_message: sans-10 NetHack.font_status: sans-10 NetHack.font_text: mono-10 From b61aef834ef4fd06adeb8e6528fca4983ab9a66c Mon Sep 17 00:00:00 2001 From: Ray Chason Date: Sat, 22 Aug 2026 20:46:13 -0400 Subject: [PATCH 6/6] Set menu font to proportional --- win/X11/winX.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/X11/winX.c b/win/X11/winX.c index 0f63a5761..4b370b6c1 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1571,7 +1571,7 @@ static XtResource resources[] = { { nhStr("font_map"), nhStr("Font_map"), XtRString, sizeof(String), XtOffset(AppResources *, font_map), XtRString, nhStr("mono-10") }, { nhStr("font_menu"), nhStr("Font_menu"), XtRString, sizeof(String), - XtOffset(AppResources *, font_menu), XtRString, nhStr("mono-10") }, + XtOffset(AppResources *, font_menu), XtRString, nhStr("sans-10") }, { nhStr("font_message"), nhStr("Font_message"), XtRString, sizeof(String), XtOffset(AppResources *, font_message), XtRString, nhStr("sans-10") }, { nhStr("font_status"), nhStr("Font_status"), XtRString, sizeof(String),