From c5db7f57a3c0b75a1cb69fbd244beb3c9754204a Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 12 Jan 2016 02:15:35 -0800 Subject: [PATCH] X11 extended command menu Restore the ability to select extended commands by keystroke. The key translation stuff had gotten attached to the wrong widget. When using keys to highlight an entry in the extended command menu, don't reset the key-by-key handling so quickly. (On second and subsequent keystrokes, if you waited more than 0.5 second, the new key started picking an extended command from scratch rather than continuing the current one, making it pretty hard to disambiguate commands which have the same initial letter. Now the delay threshold is 2.5 seconds. In theory that should become a user- preference resource but I don't think it's worth the effort.) Display the help choice in response to '?'. Clean up the formatting of the top two buttons (dismiss + help). Start with a scroll bar if the menu is too big to fit on the screen. Prior to this, on OSX, the full menu would be constructed but any of it that was 'displayed' beneath the bottom of the screen was inaccessible because it couldn't be dragged higher and couldn't be resized to get a scroll bar (since the way to resize on OSX is by dragging the bottom right corner--which was off the screen). The problems I mentioned recently are still present: when scroll bar is present, I can click beneath it and it moves down, but neither dragging it up nor clicking above it will move it back up. Fortunately the recently implemented up and down arrows both work, even after clicking/dragging starts failing. There was a lot of trial and error involved here. Most of it eventually got phased out, but the mystery of 'defaultDistance' (which is reported to be 0 but is actually 4) remains. The screen height stuff at the end ought to be fixed up if someone can figure out how to get the width of a horizontal scroll bar or the height of a title bar. (I'm sure I used to know how to do the latter, but that was 25 or so years ago, and in Fortran with Xlib rather than Xt or Xaw....) --- win/X11/winmisc.c | 198 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 140 insertions(+), 58 deletions(-) diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 78e1b73b9..fa73f9980 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -1,11 +1,11 @@ -/* NetHack 3.6 winmisc.c $NHDT-Date: 1432512807 2015/05/25 00:13:27 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ */ -/* Copyright (c) Dean Luick, 1992 */ +/* NetHack 3.6 winmisc.c $NHDT-Date: 1452593730 2016/01/12 10:15:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.15 $ */ +/* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ /* * Misc. popup windows: player selection and extended commands. * - * + Global functions: player_selection() and get_ext_cmd(). + * + Global functions: player_selection() and get_ext_cmd(). */ #ifndef SYSV @@ -727,6 +727,9 @@ Cardinal *num_params; if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; + } else if (ch == '?') { + extend_help(); + return; } else if (index("\033\n\r", ch)) { if (ch == '\033') { /* unselect while still visible */ @@ -745,8 +748,14 @@ Cardinal *num_params; return; } - /* too much time has elapsed */ - if ((xkey->time - ec_time) > 500) + /* + * If too much time has elapsed, treat current key as starting a new + * choice, otherwise it is a continuation of the choice in progress. + * Extra letters might be needed to disambiguate between choices + * ("ride" vs "rub", for instance), or player may just be typing in + * the whole word. + */ + if ((xkey->time - ec_time) > 2500) /* 2.5 seconds */ ec_active = FALSE; if (!ec_active) { @@ -816,21 +825,21 @@ init_extended_commands_popup() /* * Create a popup widget of the following form: * - * popup_label - * ----------- ------------ - * |left_name| |right_name| - * ----------- ------------ - * ------------------------ - * | name1 | - * ------------------------ - * ------------------------ - * | name2 | - * ------------------------ - * . - * . - * ------------------------ - * | nameN | - * ------------------------ + * popup_label + * ----------- ------------ + * |left_name| |right_name| + * ----------- ------------ + * ------------------------ + * | name1 | + * ------------------------ + * ------------------------ + * | name2 | + * ------------------------ + * . + * . + * ------------------------ + * | nameN | + * ------------------------ */ static Widget make_menu(popup_name, popup_label, popup_translations, left_name, @@ -854,15 +863,14 @@ Widget *formp; /* return */ int i; Arg args[8]; Cardinal num_args; - Dimension width, max_width; + Dimension width, other_width, max_width, border_width, + height, cumulative_height, screen_height; int distance, skip; - commands = (Widget *) alloc((unsigned) num_names * sizeof(Widget)); + commands = (Widget *) alloc((unsigned) num_names * sizeof (Widget)); num_args = 0; - XtSetArg(args[num_args], XtNallowShellResize, True); - num_args++; - + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; popup = XtCreatePopupShell(popup_name, transientShellWidgetClass, toplevel, args, num_args); XtOverrideTranslations( @@ -871,60 +879,71 @@ Widget *formp; /* return */ num_args = 0; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(popup_translations)); num_args++; view = XtCreateManagedWidget("menuformview", viewportWidgetClass, popup, args, num_args); num_args = 0; - XtSetArg(args[num_args], XtNtranslations, - XtParseTranslationTable(popup_translations)); - num_args++; *formp = form = XtCreateManagedWidget("menuform", formWidgetClass, view, args, num_args); - /* Get the default distance between objects in the form widget. */ + /* + * Get the default distance between objects in the viewport widget. + * (Something is fishy here: 'distance' ends up being 0 but there + * is a non-zero gap between the borders of the internal widgets. + * It matches exactly the default value of 4 for defaultDistance.) + */ num_args = 0; - XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); - num_args++; + XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); num_args++; + XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width); num_args++; XtGetValues(view, args, num_args); + if (!distance) + distance = 4; /* * Create the label. */ num_args = 0; - XtSetArg(args[num_args], XtNborderWidth, 0); - num_args++; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; label = XtCreateManagedWidget(popup_label, labelWidgetClass, form, args, num_args); + cumulative_height = 0; + XtSetArg(args[0], XtNheight, &height); + XtGetValues(label, args, ONE); + cumulative_height += distance + height; /* no border for label */ + /* * Create the left button. */ num_args = 0; - XtSetArg(args[num_args], nhStr(XtNfromVert), label); - num_args++; - /* - XtSetArg(args[num_args], nhStr(XtNshapeStyle), - XmuShapeRoundedRectangle); num_args++; - */ + XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++; +#if 0 + XtSetArg(args[num_args], nhStr(XtNshapeStyle), + XmuShapeRoundedRectangle); num_args++; +#endif left = XtCreateManagedWidget(left_name, commandWidgetClass, form, args, num_args); XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0); - skip = 3 * distance; /* triple the spacing */ - if (!skip) - skip = 3; + skip = (distance < 4) ? 8 : 2 * distance; + + num_args = 0; + XtSetArg(args[0], XtNheight, &height); + XtGetValues(left, args, ONE); + cumulative_height += distance + height + 2 * border_width; /* * Create right button. */ num_args = 0; - XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); - num_args++; - XtSetArg(args[num_args], nhStr(XtNfromVert), label); - num_args++; - /* - XtSetArg(args[num_args], nhStr(XtNshapeStyle), - XmuShapeRoundedRectangle); num_args++; - */ + XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++; + XtSetArg(args[num_args], nhStr(XtNhorizDistance), skip); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++; +#if 0 + XtSetArg(args[num_args], nhStr(XtNshapeStyle), + XmuShapeRoundedRectangle); num_args++; +#endif right = XtCreateManagedWidget(right_name, commandWidgetClass, form, args, num_args); XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0); @@ -939,29 +958,33 @@ Widget *formp; /* return */ if (!widget_names[i]) continue; num_args = 0; - XtSetArg(args[num_args], nhStr(XtNfromVert), above); - num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++; if (above == left) { /* if first, we are farther apart */ - XtSetArg(args[num_args], nhStr(XtNvertDistance), skip); - num_args++; - } + XtSetArg(args[num_args], nhStr(XtNvertDistance), skip); num_args++; + cumulative_height += skip; + } else + cumulative_height += distance; + cumulative_height += height + 2 * border_width; *curr = XtCreateManagedWidget(widget_names[i], commandWidgetClass, form, args, num_args); XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) i); above = *curr++; } + cumulative_height += distance; /* space at bottom of form */ /* - * Now find the largest width. Start with the width dismiss + help - * buttons, since they are adjacent. + * Now find the largest width. Start with width of left + right buttons + * ('dismiss' + 'help' or 'quit' + 'random'), since they are adjacent. */ XtSetArg(args[0], XtNwidth, &max_width); XtGetValues(left, args, ONE); XtSetArg(args[0], XtNwidth, &width); XtGetValues(right, args, ONE); - max_width = max_width + width + distance; + /* doesn't count leftmost 'distance + border_width' and + rightmost 'border_width + distance' since all entries have those */ + max_width = max_width + border_width + skip + border_width + width; /* Next, the title. */ XtSetArg(args[0], XtNwidth, &width); @@ -980,6 +1003,40 @@ Widget *formp; /* return */ curr++; } + /* + * Re-do the two side-by-side widgets to take up half the width each. + * + * With max_width and skip both having even values, we never have to + * tweak left or right to maybe be one pixel wider than the other. + */ + if (max_width % 2) + ++max_width; + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(left, args, ONE); + XtSetArg(args[0], XtNwidth, &other_width); + XtGetValues(right, args, ONE); + if (width + border_width + skip / 2 < max_width / 2 + && other_width + border_width + skip / 2 < max_width / 2) { + /* both are narrower than half */ + width = other_width = max_width / 2 - border_width - skip / 2; + XtSetArg(args[0], XtNwidth, width); + XtSetValues(left, args, ONE); + XtSetArg(args[0], XtNwidth, other_width); + XtSetValues(right, args, ONE); + } else if (width + border_width + skip / 2 < max_width / 2) { + /* 'other_width' (right) is half or more */ + width = max_width - other_width - 2 * border_width - skip; + XtSetArg(args[0], XtNwidth, width); + XtSetValues(left, args, ONE); + } else if (other_width + border_width + skip / 2 < max_width / 2) { + /* 'width' (left) is half or more */ + other_width = max_width - width - 2 * border_width - skip; + XtSetArg(args[0], XtNwidth, other_width); + XtSetValues(right, args, ONE); + } else { + ; /* both are exactly half... */ + } + /* * Finally, set all of the single line widgets to the largest width. */ @@ -999,6 +1056,31 @@ Widget *formp; /* return */ else free((char *) commands); + /* + * We actually want height of topmost background window, which + * may or may not be the root window. + * + * On OSX, screen height includes the space taken up by the + * desktop title bar, which isn't accessible to applications + * unless the preference settings for X11 are changed to force + * full-screen mode (so by default, this 'screen_height' value + * ends up being bigger than the available size...). + */ + screen_height = XHeightOfScreen(XtScreen(popup)); + + /* + * If the menu's complete height is too big for the display, + * forcing the height to be smaller will cause the vertical + * scroll bar (enabled but not forced above) to be included. + */ + if (cumulative_height >= screen_height) { + /* trial and error: 25 is a guesstimate for scrollbar width on + width adjustment and for title bar height on height adjustment */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, max_width + 25); num_args++; + XtSetArg(args[num_args], XtNheight, screen_height - 25); num_args++; + XtSetValues(popup, args, num_args); + } XtRealizeWidget(popup); XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1); -- 2.40.0