]> granicus.if.org Git - nethack/commitdiff
Qt text windows
authorPatR <rankin@nethack.org>
Thu, 5 Nov 2020 23:35:30 +0000 (15:35 -0800)
committerPatR <rankin@nethack.org>
Thu, 5 Nov 2020 23:35:30 +0000 (15:35 -0800)
Text window search behaved very strangely:  at some point after
selecting [Search], entering a search string, having the string
entry popup go away, and having the search performed, but before
the result could be shown, the text window got pushed behind the
main window (map+messages+paperdoll+status).  Clicking on the
main window's minimize button hid the main window and gave access
to the text window behind it.  That was still functional even
after having been inaccessible; another search could be performed
and/or it could be dismissed.  I still don't know what causes
that or how to properly fix it, but using raise() is a workaround
to bring it to the front where it belongs.  Unfortunately you can
see it go away and come back so searching for text is distracting.

Allow <return> (when not searching) to dismiss all text windows
including RIP.  Accept ctrl+[ as ESC.

Make text window searching be case-insensitive.

Searching wouldn't find a match on the first line of text.  Now
it will.

This also includes an attempt to fix github issue #400 (typing a
pickup command while "things that are here" popup text window is
displayed seems to hang the program), but since I can't reproduce
that, I can't tell whether the fix works.  The issue description
says that pickup started executing and "things here" couldn't be
dismissed which is different from "things here" being behind the
map waiting for it to be dismissed.  The attempted fix is for text
window handling to tell Qt that it wants control of the keyboard,
so nethack shouldn't see any attempted pickup command.

doc/fixes37.0
win/Qt/Qt-issues.txt
win/Qt/qt_menu.cpp
win/Qt/qt_menu.h

index b92e0791518e84e3e15bc3354dff0151726a0c50..4eb6b906249d9b1878eb026f3335d92cbdf9ccef 100644 (file)
@@ -1,4 +1,4 @@
-NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.344 $ $NHDT-Date: 1603836614 2020/10/27 22:10:14 $
+NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.347 $ $NHDT-Date: 1604619327 2020/11/05 23:35:27 $
 
 General Fixes and Modified Features
 -----------------------------------
@@ -467,6 +467,10 @@ Qt: don't clobber an existing save file after choosing "new game" in the
        saved game selection widget
 Qt: don't get stuck in a loop after choosing "play" while the character name
        field is empty in the character selection widget
+Qt: {maybe just Qt+OSX:} when viewing a text window ('V' to look at 'history'
+       for instance), clicking on [Search], entering a search target in the
+       resulting popup and clicking on [Okay] or typing <return>, the text
+       window got pushed underneath the main window so seemed to go away
 Qt+OSX: fix control key
 Qt+OSX: rename menu entry "nethack->Preferences..." for invoking nethack's
        'O' command to "Game->Run-time options" and entry "Game->Qt settings"
index 4ac6fc2e7e6f85062f18125b6d7bec3ec6393d58..b25adff97c5b87293d54bbb3876ff705f60a3603 100644 (file)
@@ -52,4 +52,11 @@ intended to be displayed, is displayed (as blank terrain).
 3.6 status conditions (Stone, Slime, Strngl, Deaf, Lev, Fly, Ride)
 have been implemented but need icon artwork (one 40x40 tile for each).
 
+In a menu window, typing ':' (via shift+something) works to initiate
+a search without the need to click on the [Search] button.  In a text
+window, it does not, despite moving the menu key press interpretation
+code into its own routine and calling that for both types of windows.
+From the text window code, it sees the initial shift but not the ':'
+that should follow.
+
 -----
index 3a6062ee1b4f45d10cb75e59541df094d8d29a01..4a59f43562c782aafc4702965efee37f70e18d8d 100644 (file)
@@ -55,6 +55,24 @@ namespace nethack_qt_ {
 void centerOnMain( QWidget* w );
 // end temporary
 
+static uchar keyValue(QKeyEvent *key_event)
+{
+    // key_event manipulation derived from NetHackQtBind::notify()
+    const int k = key_event->key();
+    Qt::KeyboardModifiers mod = key_event->modifiers();
+    QChar ch = !key_event->text().isEmpty() ? key_event->text().at(0) : 0;
+    if (ch >= 128)
+        ch = 0;
+    // on OSX, ascii control codes are not sent, force them
+    if (ch == 0 && (mod & Qt::ControlModifier) != 0) {
+        if (k >= Qt::Key_A && k <= Qt::Key_Underscore)
+            ch = QChar((k - (Qt::Key_A - 1)));
+    }
+    uchar result = (uchar) ch.cell();
+    //raw_printf("kV: k=%d, ch=%d", k, result);
+    return result;
+}
+
 QSize NetHackQtTextListBox::sizeHint() const
 {
     QScrollBar *hscroll = horizontalScrollBar();
@@ -697,18 +715,9 @@ void NetHackQtMenuWindow::ClearCount(void)
 
 void NetHackQtMenuWindow::keyPressEvent(QKeyEvent *key_event)
 {
-    // key_event manipulation derived from NetHackQtBind::notify()
-    const int k = key_event->key();
-    Qt::KeyboardModifiers mod = key_event->modifiers();
-    QChar ch = !key_event->text().isEmpty() ? key_event->text().at(0) : 0;
-    if (ch > 128)
-        ch = 0;
-    // on OSX, ascii control codes are not sent, force them
-    if (ch == 0 && (mod & Qt::ControlModifier) != 0) {
-        if (k >= Qt::Key_A && k <= Qt::Key_Underscore)
-            ch = (QChar) (k - (Qt::Key_A - 1));
-    }
-    uchar key = (char) ch.cell();
+    uchar key = keyValue(key_event);
+    if (!key)
+        return;
 
     // only one possible match for key==ch, and if one occurs it takes
     // precedence over any other match (for instance, some menus might
@@ -837,7 +846,7 @@ void NetHackQtMenuWindow::Search()
     line[0] = '\0'; /* for EDIT_GETLIN */
     if (requestor.Get(line)) {
        for (int i=0; i<itemcount; i++) {
-           if (itemlist[i].str.contains(line))
+           if (itemlist[i].str.contains(line, Qt::CaseInsensitive))
                ToggleSelect(i, false);
        }
     }
@@ -953,19 +962,31 @@ long NetHackQtMenuWindow::count(int row)
     return cstr.isEmpty() ? -1L : cstr.toLong();
 }
 
+// initialize a text window
 NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) :
     QDialog(parent),
     use_rip(false),
     str_fixed(false),
-    ok("Dismiss",this),
-    search("Search",this),
+    textsearching(false),
+    ok("&Dismiss", this),
+    search("&Search", this),
     lines(new NetHackQtTextListBox(this)),
+    target(""),
     rip(this)
 {
+    //
+    // TODO?
+    //  Searching would be far more convenient if the window contained
+    //  the search string requestor widget instead of just a [Search]
+    //  button to request a popup for that.
+    //  Also, searching should probably be disabled if the entire text
+    //  fits within the window so there's nothing to scroll through.
+    //
+
     ok.setDefault(true);
-    connect(&ok,SIGNAL(clicked()),this,SLOT(accept()));
-    connect(&search,SIGNAL(clicked()),this,SLOT(Search()));
-    connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate()));
+    connect(&ok,SIGNAL(clicked()), this, SLOT(doDismiss()));
+    connect(&search, SIGNAL(clicked()), this, SLOT(Search()));
+    connect(qt_settings, SIGNAL(fontChanged()), this, SLOT(doUpdate()));
 
     QVBoxLayout* vb = new QVBoxLayout(this);
     vb->addWidget(&rip);
@@ -974,6 +995,10 @@ NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) :
     hb->addWidget(&ok);
     hb->addWidget(&search);
     vb->addWidget(lines);
+
+    // we don't want keystrokes being sent to the main window for use as
+    // commands while this text window is popped up
+    setFocusPolicy(Qt::StrongFocus);
 }
 
 void NetHackQtTextWindow::doUpdate()
@@ -984,7 +1009,6 @@ void NetHackQtTextWindow::doUpdate()
 
 NetHackQtTextWindow::~NetHackQtTextWindow()
 {
-
 }
 
 QWidget* NetHackQtTextWindow::Widget()
@@ -994,7 +1018,16 @@ QWidget* NetHackQtTextWindow::Widget()
 
 bool NetHackQtTextWindow::Destroy()
 {
-    return !isVisible();
+    return true; /*!isVisible();*/
+}
+
+void NetHackQtTextWindow::doDismiss()
+{
+    // [Clear() was needed when the search target string was kept in
+    //  a static buffer but is superfluous now that that's part of
+    //  the TextWindow class and initialized in the constructor.]
+    Clear();
+    accept();
 }
 
 void NetHackQtTextWindow::UseRIP(int how, time_t when)
@@ -1083,12 +1116,18 @@ void NetHackQtTextWindow::UseRIP(int how, time_t when)
 void NetHackQtTextWindow::Clear()
 {
     lines->clear();
-    use_rip=false;
-    str_fixed=false;
+    target[0] = '\0'; // discard search target string
+    use_rip = false;
+    str_fixed = false;
+    textsearching = false;
 }
 
 void NetHackQtTextWindow::Display(bool block UNUSED)
 {
+    // make sure window isn't completely empty
+    if (!lines->count())
+        PutStr(ATR_NONE, "");
+
     if (str_fixed) {
        lines->setFont(qt_settings->normalFixedFont());
     } else {
@@ -1117,7 +1156,11 @@ void NetHackQtTextWindow::Display(bool block UNUSED)
        centerOnMain(this);
        show();
     }
+
+    lines->clearSelection(); // affects [Search]
+
     exec();
+    textsearching = false;
 }
 
 void NetHackQtTextWindow::PutStr(int attr UNUSED, const QString& text)
@@ -1126,22 +1169,77 @@ void NetHackQtTextWindow::PutStr(int attr UNUSED, const QString& text)
     lines->addItem(text);
 }
 
+// prompt for a target string and search current text window for it;
+// if found, highlight the next line target occurs on;
+// multiple searches with same or different search string are supported
 void NetHackQtTextWindow::Search()
 {
-    NetHackQtStringRequestor requestor(this, "Search for:");
-    static char line[256]="";
-    requestor.SetDefault(line);
-    if (requestor.Get(line)) {
-       int current=lines->currentRow();
-       for (int i=1; i<lines->count(); i++) {
-           int lnum=(i+current)%lines->count();
-           QString str=lines->item(lnum)->text();
-           if (str.contains(line)) {
-               lines->setCurrentRow(lnum);
-               return;
-           }
-       }
-       lines->setCurrentItem(NULL);
+    textsearching = true;
+    NetHackQtStringRequestor requestor(this, "Search for:", "Done", "Find");
+    requestor.SetDefault(target);
+    boolean get_a_line = requestor.Get(target, (int) sizeof target);
+
+    // FIXME:
+    //  Force text window to be on top.  Without this, it moves behind
+    //  the map after the string requestor completes.  Then it can't
+    //  be seen or accessed (unless the game window is minimized or
+    //  possibly dragged out of the way).  Unfortunately the window
+    //  noticeably vanishes and then immediately gets redrawn.
+    if (!this->isActiveWindow())
+        this->activateWindow();
+    this->raise();
+
+    if (get_a_line) {
+        int linecount = lines->count();
+        int current = lines->currentRow();
+        // when no row is highlighted (selected), start the search
+        // on the current row, otherwise start on the row after it
+        // [normally means that the very first row is a candidate
+        // for containgin the target during the very first search]
+        int startln = lines->selectedItems().count();
+        for (int i = startln; i < linecount; ++i) {
+            int lnum = (i + current) % linecount;
+            const QString &str = lines->item(lnum)->text();
+            // Check whether target occurs on this line.  If it does,
+            // the line is highlighted and this search finishes.
+            // When not currently within view, highlighting also
+            // scrolls the view to make it become the bottom line.
+            // A subsequent search will remember the target string
+            // and start searching on the line past the highlighted
+            // one (even if a new target is specified).
+            if (str.contains(target, Qt::CaseInsensitive)) {
+                lines->setCurrentRow(lnum);
+                return;
+            }
+        }
+        lines->setCurrentItem(NULL);
+    } else {
+        target[0] = '\0';
+    }
+    textsearching = false;
+    return;
+}
+
+void NetHackQtTextWindow::keyPressEvent(QKeyEvent *key_event)
+{
+    uchar key = keyValue(key_event);
+
+    //
+    // FIXME:
+    //  Typing ':' doesn't produce ':' so key won't match MENU_SEARCH,
+    //  despite the fact that it does produce ':' for menu input and
+    //  we're calling the exact same code for both types of window...?
+    //
+    if (key == MENU_SEARCH) {
+        if (!use_rip)
+            Search();
+    } else if (key == '\n' || key == '\r') {
+        if (!textsearching)
+            accept();
+    } else if (key == '\033') {
+        reject();
+    } else {
+        QDialog::keyPressEvent(key_event);
     }
 }
 
index b61a9305b3c7fbfa3f93b70fdcd793fb42f87ddd..e69265a4f42bfe4c32e93343cd260be0b16d4946 100644 (file)
@@ -155,15 +155,21 @@ public slots:
        void Search();
 
 private slots:
+        void doDismiss();
        void doUpdate();
 
+protected:
+       virtual void keyPressEvent(QKeyEvent *);
+
 private:
        bool use_rip;
        bool str_fixed;
+        bool textsearching;
 
        QPushButton ok;
        QPushButton search;
        NetHackQtTextListBox* lines;
+        char target[BUFSZ];
 
        NetHackQtRIP rip;
 };