Add support for tabbed menu columns

This commit is contained in:
Ray Chason
2026-08-22 10:57:24 +03:00
committed by Pasi Kallinen
parent 44b04064be
commit ae8b4bf12a
5 changed files with 177 additions and 15 deletions
+2
View File
@@ -531,8 +531,10 @@ extern void X11_update_label(Widget);
extern void X11_set_attrs(Widget, unsigned);
extern void X11_set_highlight(Widget, boolean);
extern void X11_set_percent(Widget, unsigned, Pixel);
extern void X11_set_column_widths(Widget, const int *, unsigned);
extern void X11_blink_labels(void);
extern int X11_font_height(X11_Font *);
extern int X11_column_width(Display *, X11_Font *, const char *, size_t);
/*
* These are for widgets that use the enhanced label services only for text
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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),
@@ -1646,6 +1646,9 @@ X11_init_nhwindows(int *argcp, char **argv)
Arg args[4];
uid_t savuid;
/* Request tabbed menu columns */
iflags.menu_tab_sep = TRUE;
/* Init windows to nothing. */
for (i = 0; i < MAX_WINDOWS; i++)
window_list[i].type = NHW_NONE;
+93 -12
View File
@@ -19,7 +19,8 @@
#include "winX.h"
/* Declarations depending on Xft support or none */
static int X11_text_width(Display *, X11_Font *, const char *, size_t);
static int X11_text_width(Display *, X11_Font *, const char *, size_t,
const int *, unsigned);
/* Data attached to a wrapped widget */
typedef struct WidgetData {
@@ -41,6 +42,10 @@ typedef struct WidgetData {
/* Percentage bar */
unsigned percent;
Pixel bar_color;
/* Columns */
int *columns;
unsigned num_cols;
} WidgetData;
/* Bits for WidgetData::font */
@@ -119,6 +124,7 @@ delete_callback(Widget w, XtPointer client_data, XtPointer call_data)
XFreePixmap(display, data->pixmap);
}
free_fonts(w, data);
free(data->columns);
free(data);
}
@@ -173,6 +179,25 @@ X11_set_percent(Widget w, unsigned percent, Pixel color)
}
}
void
X11_set_column_widths(Widget w, const int *col_widths, unsigned num_cols)
{
WidgetData *data = get_widget_data(w);
if (data != NULL) {
int col_spacing = X11_font_height(data->font[0]) * 2;
free(data->columns);
data->columns = (int *) alloc(sizeof(data->columns[0]) * num_cols);
if (num_cols != 0) {
data->columns[0] = 0;
for (unsigned i = 1; i < num_cols; ++i) {
data->columns[i] = data->columns[i-1] + col_spacing + col_widths[i-1];
}
}
data->num_cols = num_cols;
update_label(w, data);
}
}
/* Update the label's pixmap */
/* This is called from functions that have altered the data block, and already
have a pointer to it */
@@ -235,7 +260,8 @@ update_label(Widget w, WidgetData *data)
}
/* Get the width of the line */
int lwidth = X11_text_width(display, font, label + i, line2);
int lwidth = X11_text_width(display, font, label + i, line2,
data->columns, data->num_cols);
/* Update pixmap dimensions */
width = max(lwidth, width);
@@ -301,6 +327,7 @@ update_label(Widget w, WidgetData *data)
if ((attrs & HL_BLINK) && X11_blink) {
values.foreground = values.background;
}
#ifdef USE_XFT
Visual *visual = DefaultVisualOfScreen(screen);
Colormap cmap = DefaultColormapOfScreen(screen);
@@ -357,7 +384,8 @@ update_label(Widget w, WidgetData *data)
}
/* Get the width of the line */
int lwidth = X11_text_width(display, font, label + i, line2);
int lwidth = X11_text_width(display, font, label + i, line2,
data->columns, data->num_cols);
/* Place the line horizontally */
int x = 0;
@@ -378,13 +406,38 @@ update_label(Widget w, WidgetData *data)
/* Render the line */
#ifdef USE_XFT
XftDrawRect(draw, &bgcolor, x, y - font->ascent, lwidth, X11_font_height(font));
XftDrawString8(draw, &fgcolor, font, x, y,
(const FcChar8 *) (label + i), line2);
#else
XDrawImageString(display, new_pixmap, ggc,
x, y,
label + i, line2);
XSetForeground(display, ggc, values.background);
XFillRectangle(display, new_pixmap, ggc, x, y - font->ascent, lwidth, X11_font_height(font));
XSetForeground(display, ggc, values.foreground);
#endif
size_t j = 0;
unsigned col = 0;
while (j < line2) {
size_t line3;
int pos;
if (data->num_cols == 0) {
/* Columns not set */
line3 = line2;
pos = 0;
} else {
line3 = min(strcspn(label + i + j, "\t\n"), line2);
pos = data->columns[min(col, data->num_cols-1)];
}
#ifdef USE_XFT
XftDrawString8(draw, &fgcolor, font, x + pos, y,
(const FcChar8 *) (label + i + j), line3);
#else
XDrawString(display, new_pixmap, ggc,
x + pos, y,
label + i + j, line3);
#endif
j += line3;
if (j < line2) {
++j;
}
++col;
}
/* Draw the underline if requested */
if (attrs & HL_ULINE) {
@@ -488,13 +541,41 @@ free_fonts(Widget w, WidgetData *data)
}
}
static int
X11_text_width(Display *display, X11_Font *font, const char *text, size_t length,
const int *columns, unsigned num_cols)
{
if (num_cols == 0) {
/* Columns have not been set */
return X11_column_width(display, font, text, length);
}
/* Width is the position of the last column, plus the width of the string
in the last column */
unsigned col = 0;
size_t i = 0;
while (col < num_cols) {
size_t len = strcspn(text + i, "\t\n");
if (i+len >= length || text[i+len] != '\t') {
break;
}
++col;
i += len + 1;
}
int pos = columns[col] + X11_column_width(display, font, text + i, length - i);
return pos;
}
//////////////////////////////////////////////////////////////////////////////
// Functions that depend on the font rendering API //
//////////////////////////////////////////////////////////////////////////////
#ifdef USE_XFT
static int
X11_text_width(Display *display, X11_Font *font, const char *text, size_t length)
int
X11_column_width(Display *display, X11_Font *font, const char *text, size_t length)
{
XGlyphInfo extents;
XftTextExtents8(display, font, (const FcChar8*) text, length, &extents);
@@ -507,8 +588,8 @@ X11_font_height(X11_Font *font)
return max(font->height, font->ascent + font->descent);
}
#else /* !USE_XFT */
static int
X11_text_width(Display *display, X11_Font *font, const char *text, size_t length)
int
X11_column_width(Display *display, X11_Font *font, const char *text, size_t length)
{
nhUse(display);
return XTextWidth(font, text, length);
+77 -1
View File
@@ -60,6 +60,7 @@ static unsigned menu_scrollmask(struct xwindow *);
static void menu_unscroll(struct xwindow *);
static Widget menu_create_buttons(struct xwindow *, Widget, Widget);
static void menu_create_entries(struct xwindow *, struct menu *);
static unsigned get_col_widths(Widget, X11_Font *, const char *, int **);
static void destroy_menu_entry_widgets(struct xwindow *);
static void create_menu_translation_tables(void);
@@ -1317,6 +1318,13 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu)
Cardinal num_args;
Dimension cwidth, maxwidth = 0;
int *col_widths = NULL;
unsigned num_cols = 0;
X11_Font *font;
#ifdef USE_XFT
font = X11_new_font(wp->w, 0, NHW_MENU);
#endif
for (curr = curr_menu->base; curr; curr = curr->next) {
char tmpbuf[BUFSZ];
Widget linewidget;
@@ -1376,13 +1384,42 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu)
wp->w, args, num_args);
X11_wrap_widget(curr->w, NHW_MENU);
X11_set_attrs(curr->w, 0x1 << attr);
XtManageChild(curr->w);
if (canpick)
XtAddCallback(linewidget, XtNcallback, menu_select,
(XtPointer) curr);
prevlinewidget = linewidget;
#ifndef USE_XFT /* If Xft, the font is acquired at the start of the loop */
num_args = 0;
XtSetArg(args[num_args], XtNfont, &font); num_args++;
XtGetValues(curr->w, args, num_args);
#endif
/* Get column widths for this line */
if (strchr(str, '\t') != NULL) { /* Might be a header if no tab */
int *col_widths0;
unsigned num_cols0 = get_col_widths(curr->w, font, str, &col_widths0);
if (num_cols0 > num_cols) {
col_widths = (int *) re_alloc((long *) col_widths, num_cols0 * sizeof(col_widths[0]));
memset(col_widths + num_cols, 0, sizeof(col_widths[0]) * (num_cols0 - num_cols));
num_cols = num_cols0;
}
for (unsigned i = 0; i < num_cols0; ++i) {
col_widths[i] = max(col_widths[i], col_widths0[i]);
}
free(col_widths0);
}
}
#ifdef USE_XFT
X11_release_font(wp->w, font);
#endif
/* Set the column widths */
for (curr = curr_menu->base; curr; curr = curr->next) {
X11_set_column_widths(curr->w, col_widths, num_cols);
XtManageChild(curr->w);
boolean canpick = (how != PICK_NONE && curr->identifier.a_void);
if (canpick) {
/* get the current line width */
XtSetArg(args[0], XtNwidth, &cwidth);
@@ -1392,6 +1429,8 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu)
}
}
free(col_widths);
/* set all selectable menu entries to the maximum width */
if (how != PICK_NONE) {
XtSetArg(args[0], XtNwidth, maxwidth);
@@ -1401,6 +1440,43 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu)
}
}
/* Determine widths of columns */
static unsigned
get_col_widths(Widget w, X11_Font *font, const char *str, int **col_widths)
{
/* Determine the number of columns */
unsigned num_cols = 1;
size_t i = 0;
while (str[i] != '\0') {
size_t len = strcspn(str + i, "\t");
if (str[i+len] == '\0') {
break;
}
++num_cols;
i += len + 1;
}
/* Allocate width array */
int *cwidths = (int *) alloc(sizeof(cwidths[0]) * num_cols);
memset(cwidths, 0, sizeof(cwidths[0]) * num_cols);
/* Get the widths of the columns */
unsigned col = 0;
i = 0;
while (str[i] != '\0') {
size_t len = strcspn(str + i, "\t");
cwidths[col++] = X11_column_width(XtDisplay(w), font, str + i, len);
i += len;
if (str[i] == '\t') {
++i;
}
}
/* Return */
*col_widths = cwidths;
return num_cols;
}
static void
destroy_menu_entry_widgets(struct xwindow *wp)
{