Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QToolTip::showText not showing - Mouse event ?



  • Hello,

    I'm trying to do a Validator that can notify when input is Invalid or Intermediate. Therefor I emit a custom signal that contain the notification message:

    void Validator::fixup(QString& input) const
    {
        Fixup(input);
        if (options.testFlag(Option::SendAtFixup) == true)
        {
            qDebug() << "FixUp";
            emit Error(ErrorMessage());
        }
    }
    
    Validator::State Validator::validate(QString& input, int& pos) const
    {
        State state = Validate(input, pos);
    
        if (options.testFlag(Option::SendDurringValidation) == true)
        {
            switch (state)
            {
            case State::Invalid :
                qDebug() << "Validate";
                emit Error(ErrorMessage());
                break;
            case State::Intermediate :
                break;
            case State::Acceptable :
                break;
            }
        }
        return state;
    }
    
    QString Validator::ErrorMessage() const
    {
        return ErrorStart + QString("Error") + ErrorEnd;
    }
    

    Then on my LineEdit using Validator I connect the signal Validator::Error to a custom slot LineEdit::ShowMessage to display the message as a ToolTip:

    void LineEdit::ShowMessage(const QString& message)
    {
        int halfWidth = rect().width() * 0.5f;
        int halfHeight = rect().height() * 0.5f;
    
        QPoint tooltipPos = mapToGlobal(pos());
    
        tooltipPos.rx() += halfWidth;
        tooltipPos.ry() -= halfHeight;
    
        QToolTip::showText(tooltipPos, message, this);
    }
    

    It works well when used from Validator::validate or when called from Validator::fixup if the user pressed Enter/Return:
    Error.png

    Howerver, it does not work when clicking provokes a focus lost:
    Losing focus calls Validator::fixup which emit the signal that triggersLineEdit::ShowMessage but the ToolTip is not shown.
    I first thought that is was because LineEdit did not have focus when the QToolTip::showText was called. Therefor, I overrode the focusOut method:

    void LineEdit::focusOutEvent(QFocusEvent* focusEvent)
    {
        (void)focusEvent;
        if (hasAcceptableInput() == false)
        {
            setFocus();
            QString invalidInupt = text();
            validator()->fixup(invalidInupt);
        }
    }
    

    Here LineEdit has focus but the ToolTip is still not shown. My last guess is that a mouse event, probably the clicking, prevents the ToolTip.
    Any ideas on whether or not I'm on the right track and if so, how to do it properly? Thank you


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Did you check whether the position is valid and within your widget ?



  • Hi thank you,

    Well it is the same slot as the other working cases that are correctly showing the tooltip (on the picture) so I assume yes ?



  • I've made it work somewhat, but there are 2 remaining problems (actually one was not yet identified in the original post):

    First, I decided to use QWidget::mouseGrab when LineEdit gain focus:

    void LineEdit::focusInEvent(QFocusEvent* focusEvent)
    {
        grabMouse();
        QLineEdit::focusInEvent(focusEvent);
    }
    

    That leads to being unable to lose focus by cliking (obviously).
    So, I needed to implement LineEdit::mousePressEvent to know what to do:

    • When cliking inside the LineEdit: behave as normal

    • When cliking on other widget: use QLineEdit::hasAcceptableInput to verify input.
      If input is valid -> release the mouse and set focus on other widget;
      Else -> manualy call QValidator::fixup from QLineEdit::validator (that will trigger the message to be shown).

    • When clicking anywhere else is the same as clicking on other widget except that the focus is cleared instead.

    Actualy, this was kind of working but the tooltip was only shown when the mouse button was being pressed: upon release, the tooltip would hide itself.
    (I belive any mouse event prevent the tooltip to be shown in this case as the cursor is outside the widget).
    So I ended up moving this in LineEdit::mouseReleaseEvent. Here is a simplified version:

    void LineEdit::mouseReleaseEvent(QMouseEvent* event)
    {
        if (UnderMouse(event->globalPos()) == true) // Check if LineEdit is under cursor
        {
            QLineEdit::mouseReleaseEvent(event);
            return;
        }
    
        if (hasAcceptableInput() == false)
        {
            FixInput(); // Helper method to call validator()->fixup(copiedText) (trigger will show the tooltip)
            return;
        }
        else
        {
            releaseMouse();
            clearFocus();
            return;
        }    
    }
    

    (There is still a similar first 'if-block' in LineEdit::mousePressEvent to prevent mousePressEvent to do something if not 'mouse-pressing' inside the LineEdit)

    This work as I want to, except the 2 following problems :

    1. It prevent the user to click on the window frame buttons since mouse events are intercepted.
    2. Using Tab key still allows to give focus to next widget, again the QValidator::fixup is indeed called, but the tooltip is not showing.

    First Problem (Just my insight on the problem but I think I can mange, you can skip to next pb if you want to help ☺):
    Idealy, I would have to override LineEdit::mouseMoveEvent and do something (involving releasing the mouse) when hovering the main frame so the user can interact with the buttons. But it would still needs to re-grab the mouse if the cursor goes back inside the window. That part should be doable with a bit of work, I'm not to concerned right now.

    Second Problem ('Real' problem):
    Sadly using focus policy Qt::ClickFocus does not prevent Leaving with keyboard, it just prevent Entering with keyboard.
    So, as with the mouse, I tried to grab the keyboard LineEdit gain focus and just debug the key pressed in LineEdit::keyPressEvent but this method was not called. So, I guess this is not a Keyboard Event? maybe a Shortcut Event ? If not I have no more idea. I will try Shortcut Event soon.



  • Tab problem is solved !

    I don't think it is a Shortcut Event as StandardKey of QKeySequence does not map Tab key. I don't realy know how it is done inside Qt, I might search later.

    I actualy just overrode QLineEdit::focusNextPreviousChild to do a validation check:

    bool LineEdit::focusNextPrevChild(bool next)
    {
        if (hasAcceptableInput() == false)
        {
            FixInput();
            return true; // To prevent searching for other widgets
        }
    
        return QLineEdit::focusNextPrevChild(next);
    }
    

    As a result, if input is invalid when the user press Tab the tooltip is shown but if the input is valid the next widget gain focus
    Error2.png

    Apparently I still need to have better accuracy for the position of the tooltip though
    EDIT: QWidget::mapToGlobal already include the position of the widget (should have been obvious) So I just changed mapToGlobal(pos()); to mapToGlobal(s_zero); with static const QPoint s_zero = QPoint(0, 0)