Accept/reject focus coming by mouse click, based on coordinates
-
Just a guess, not a proven solution: Did you try to install an event filter (see QObject::eventFilter) on your widget? You could look for QEvent::FocusIn, calculate if the click was inside your visual boundary and accept or reject accordingly. But honestly, I'm not sure if this works better than ignoring the QWidget::focusInEvent...
-
@stryga42 Thanks for the suggestion.
I’ve tried that now, but the result is the same as with reimplementing
event()
orfocusInEvent()
: It doesn’t prevent that the focus is assigned. It seems the focus is assigned anyway, and the event just informs the widget about that, without a possibility to cancel it? -
Maybe you are right that the focusIn is "too late".
What about combining the eventFilter approach (the filter is notified definitely before the target object gets the event) with your current mouse click approach? Use FocusPolicy::ClickFocus and suppress the MouseButtonPress event in the event filter if the mouse position is not truly "inside". -
#include <QApplication> #include <QWidget> #include <QPainter> #include <QLayout> #include <QMouseEvent> #include <QPixmap> #include <QImage> class Widget : public QWidget { Q_OBJECT protected: void paintEvent([[maybe_unused]] QPaintEvent *event) override { QPainter painter(this); painter.drawRect(this->rect()); painter.setBrush(this->hasFocus() ? Qt::red : Qt::blue); painter.drawEllipse(this->rect()); } void mousePressEvent(QMouseEvent *event) override { QPixmap pixmap = this->grab(QRect(event->x(), event->y(), 1, 1)); auto pixel = pixmap.toImage().pixel(0, 0); if (pixel == 0xFF0000FF) this->setFocus(Qt::MouseFocusReason); QWidget::mousePressEvent(event); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget topLevel; topLevel.setGeometry(0, 0, 200, 100); QHBoxLayout layout(&topLevel); Widget w1, w2; layout.addWidget(&w1); layout.addWidget(&w2); topLevel.show(); return a.exec(); } #include "main.moc"
-
Thanks for the proposition.
However, this code would just allow to capture the mouse focus within the circle. This is what my code is doing yet. The question is how to prevent that the widget gets focus when clicked outside the circle, but inside the rectangle, when
QWidget::setFocusPolicy(Qt::FocusPolicy::ClickFocus)
had been called. -
Indeed.
Now, my problem is not how to reimplement mousePressEvent(). I've done that yet, just as you propose, and it works. The actual implementation is here: https://sommerluk.github.io/perceptualcolor/chromahuediagram_8cpp_source.html#l00137
It does quite exactly the same thing.
My problem is that this feels a little bit like a hack. This implementation will get focus by mouse click even when the widget’s focusPolicy is Qt::FocusPolicy::TabFocus. On the other hand, when focusPolicy is Qt::FocusPolicy::ClickFocus, it will also gather the focus by clicks outside the circle, but within the widget rectangle. That feels inconsistent…
-
@sommerluk said in Accept/reject focus coming by mouse click, based on coordinates:
My problem is that this feels a little bit like a hack. This implementation will get focus by mouse click even when the widget’s focusPolicy is Qt::FocusPolicy::TabFocus.
So check the widget's focus policy and call or don't call setFocus() according to your needs. It looks like adding a new focus policy reason that fits in the enum won't be rejected as of 5.15.0, although there's no explicit allowance for it in the API. Adding a separate attribute of some sort is less likely to conflict with Qt API changes.
On the other hand, when focusPolicy is Qt::FocusPolicy::ClickFocus, it will also gather the focus by clicks outside the circle, but within the widget rectangle. That feels inconsistent…
The documentation says
Qt::ClickFocus 0x2 the widget accepts focus by clicking.
QWidgets are rectangular. The unpainted or differently painted space within the rectangle still belongs to that widget as far as the framework is concerned.Have a look at the implementation. Determining if the widget gets focus due to ClickFocus happens before the click is delivered to the widget, based on its geometry, focus policy, and focus proxy.
https://code.woboq.org/qt5/qtbase/src/widgets/kernel/qapplication.cpp.html#3125For what it's worth, other people have asked for the same functionality.
https://bugreports.qt.io/browse/QTBUG-598