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 slotLineEdit::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 fromValidator::fixup
if the user pressed Enter/Return:
Howerver, it does not work when clicking provokes a focus lost:
Losing focus callsValidator::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 theQToolTip::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 -
Hi and welcome to devnet,
Did you check whether the position is valid and within your widget ?
-
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 implementLineEdit::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 callQValidator::fixup
fromQLineEdit::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 inLineEdit::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 :
- It prevent the user to click on the window frame buttons since mouse events are intercepted.
- 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 overrideLineEdit::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 inLineEdit::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
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 changedmapToGlobal(pos());
tomapToGlobal(s_zero);
withstatic const QPoint s_zero = QPoint(0, 0)