Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Mouse hover on QRect not working as expected
Qt 6.11 is out! See what's new in the release blog

Mouse hover on QRect not working as expected

Scheduled Pinned Locked Moved Solved General and Desktop
20 Posts 5 Posters 5.0k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • SGaistS Offline
    SGaistS Offline
    SGaist
    Lifetime Qt Champion
    wrote on last edited by
    #10

    Hi,

    Test whether the point is in the rectangle where the trash icon is drawn.

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    ? 1 Reply Last reply
    0
    • SGaistS SGaist

      Hi,

      Test whether the point is in the rectangle where the trash icon is drawn.

      ? Offline
      ? Offline
      A Former User
      wrote on last edited by
      #11

      @SGaist said in Mouse hover on QRect not working as expected:

      Test whether the point is in the rectangle where the trash icon is drawn.

      I have colored the whole thing briefly. When I move the mouse from the red rectangle to the yellow rectangle, I want the trash can icon to change back to black. If I move from yellow to red it should become red again.

      5752699a-1138-4072-88e8-de9629978ede-image.png

      The Paint method from the delegate is not called again here, if I move from the red to the yellow rectangle. I don't know exactly how this works with the paint stuff. Do I have to overwrite the paintEvent or work with the mouseMoveEvent or something else?

      Thanks for the help!

      1 Reply Last reply
      0
      • ? Offline
        ? Offline
        A Former User
        wrote on last edited by
        #12

        @Christian-Ehrlicher @JonB @SGaist

        I have found a way how everything works now. Here is my solution.

        My paint function from custom delegate:

        void ToggleListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
        {
        
        	QString text = index.data(Qt::DisplayRole).toString();
        	QRect original = option.rect;
        	QRect button = original.adjusted(original.width() - mButtonSize.width(),0,0,0);
        	QRect label = original.adjusted(0,0, -mButtonSize.width(),0);
        	painter->drawText(label, Qt::AlignLeft, text);
        
        	QIcon icon = QIcon(":/icons/trash_black.png");
        	icon.paint(painter, button);
        
        	const auto widget = qobject_cast<ToggleListView*>(option.styleObject);
        
        	QPoint cursor = QCursor::pos();
        	QPoint position = widget->viewport()->mapFromGlobal(cursor);
        
        	const bool selected = option.state & QStyle::State_Selected;
        
        	if(selected){
        		painter->fillRect(original, option.palette.highlight());
        		painter->drawText(label, Qt::AlignLeft, text);
        		icon.paint(painter, button);
        	}
        	
        	if(button.contains(position)){
        		const bool hover = option.state & QStyle::State_MouseOver;
        		if(hover){
        			icon = QIcon(":/icons/trash_red.png");
        			icon.paint(painter, button);
        		}
        	}
        	painter->save();
        	painter->restore();
        
        }
        

        My custom lisstview:

        ToggleListView::ToggleListView(QWidget *parent): QListView(parent) {
        	this->setMouseTracking(true);
        }
        
        void ToggleListView::mouseMoveEvent(QMouseEvent *event)
        {
        	QModelIndex index = indexAt(event->pos());
        	QPoint position = event->pos();
        	QRect original = visualRect(index);
        	QRect label = original.adjusted(0,0, -mButtonSize.width(),0);
        
        	if(label.contains(position)){
        		this->viewport()->repaint();
        	} else {
        		this->viewport()->repaint();
        	}
        }
        

        Is it overkill to redraw the whole thing every time? Do you have a better idea or a suggestion for improvement?

        Thanks for your help!

        1 Reply Last reply
        0
        • jeremy_kJ Offline
          jeremy_kJ Offline
          jeremy_k
          wrote on last edited by jeremy_k
          #13

          I think the trick for getting repaints for hover events in a view is to turn on the QA_Hover attribute for the viewport.

          #include <QApplication>
          #include <QListView>
          #include <QStyledItemDelegate>
          #include <QStringListModel>
          #include <QDebug>
          
          class Delegate: public QStyledItemDelegate {
              Q_OBJECT
          public:
              void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
              {
                  qDebug() << "paint" << index;
                  QStyledItemDelegate::paint(painter, option, index);
              }
          };
          
          int main(int argc, char *argv[])
          {
              QApplication a(argc, argv);
              QListView lv;
              lv.viewport()->setAttribute(Qt::WA_Hover);
              lv.setItemDelegate(new Delegate);
              QStringListModel model({"first", "second", "third"});
              lv.setModel(&model);
              lv.show();
              return a.exec();
          }
          
          #include "main.moc"
          

          Asking a question about code? http://eel.is/iso-c++/testcase/

          jeremy_kJ 1 Reply Last reply
          0
          • jeremy_kJ jeremy_k

            I think the trick for getting repaints for hover events in a view is to turn on the QA_Hover attribute for the viewport.

            #include <QApplication>
            #include <QListView>
            #include <QStyledItemDelegate>
            #include <QStringListModel>
            #include <QDebug>
            
            class Delegate: public QStyledItemDelegate {
                Q_OBJECT
            public:
                void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
                {
                    qDebug() << "paint" << index;
                    QStyledItemDelegate::paint(painter, option, index);
                }
            };
            
            int main(int argc, char *argv[])
            {
                QApplication a(argc, argv);
                QListView lv;
                lv.viewport()->setAttribute(Qt::WA_Hover);
                lv.setItemDelegate(new Delegate);
                QStringListModel model({"first", "second", "third"});
                lv.setModel(&model);
                lv.show();
                return a.exec();
            }
            
            #include "main.moc"
            
            jeremy_kJ Offline
            jeremy_kJ Offline
            jeremy_k
            wrote on last edited by
            #14

            @jeremy_k said in Mouse hover on QRect not working as expected:

            I think the trick for getting repaints for hover events in a view is to turn on the QA_Hover attribute for the viewport.

            A more straightforward solution may be to use something like:

                view->setMouseTracking(true);
                QObject::connect(view, &QAbstractItemView::entered, [] (QModelIndex *index) {
                    // Reset the previous index if any, and then do something with this index
                    });
            

            Asking a question about code? http://eel.is/iso-c++/testcase/

            ? 1 Reply Last reply
            0
            • jeremy_kJ jeremy_k

              @jeremy_k said in Mouse hover on QRect not working as expected:

              I think the trick for getting repaints for hover events in a view is to turn on the QA_Hover attribute for the viewport.

              A more straightforward solution may be to use something like:

                  view->setMouseTracking(true);
                  QObject::connect(view, &QAbstractItemView::entered, [] (QModelIndex *index) {
                      // Reset the previous index if any, and then do something with this index
                      });
              
              ? Offline
              ? Offline
              A Former User
              wrote on last edited by
              #15

              @jeremy_k

              Thank you for your reply. I had tried the whole thing with the hover attribute before, but it didn't work. Because as described above I have to change the icon when I go from the red to the yellow area and vice versa. This works with the above solution using mouseMoveEvent.

              Thanks for your tip anyway!

              jeremy_kJ 1 Reply Last reply
              0
              • ? A Former User

                @jeremy_k

                Thank you for your reply. I had tried the whole thing with the hover attribute before, but it didn't work. Because as described above I have to change the icon when I go from the red to the yellow area and vice versa. This works with the above solution using mouseMoveEvent.

                Thanks for your tip anyway!

                jeremy_kJ Offline
                jeremy_kJ Offline
                jeremy_k
                wrote on last edited by jeremy_k
                #16

                I missed the problem revision. Thanks for pointing it out.

                The mouseMoveEvent() implementation above has a few obvious opportunities for improvement in efficiency. This is going to repaint the entire visible portion of the view on every move, even if the cursor remains in the same portion of the same delegate instance.

                	if(label.contains(position)){
                		this->viewport()->repaint();
                	} else {
                		this->viewport()->repaint();
                	}
                

                This is a tautology. If nothing else, the code can be simplified by removing the condition. The QRect::contains() is const, so the compiler may already be optimizing the test out.

                Repainting can be limited to the impacted indexes by using QAbstractItemView::update(). Cache the previous index and button highlight state to detect when a repaint isn't necessary at all.

                if (currentIndex != this->previousIndex) {
                    if (this->previousIndex.isValid())
                        this->update(this->previousIndex);
                    if (currentIndex.isValid())
                        this->update(currentIndex);
                }
                else if (currentButtonState != this->previousButtonState) {
                    this->update(currentIndex);
                }
                this->previousIndex = currentIndex;
                this->previousButtonState = currentButtonState;
                
                

                Asking a question about code? http://eel.is/iso-c++/testcase/

                ? 1 Reply Last reply
                0
                • jeremy_kJ jeremy_k

                  I missed the problem revision. Thanks for pointing it out.

                  The mouseMoveEvent() implementation above has a few obvious opportunities for improvement in efficiency. This is going to repaint the entire visible portion of the view on every move, even if the cursor remains in the same portion of the same delegate instance.

                  	if(label.contains(position)){
                  		this->viewport()->repaint();
                  	} else {
                  		this->viewport()->repaint();
                  	}
                  

                  This is a tautology. If nothing else, the code can be simplified by removing the condition. The QRect::contains() is const, so the compiler may already be optimizing the test out.

                  Repainting can be limited to the impacted indexes by using QAbstractItemView::update(). Cache the previous index and button highlight state to detect when a repaint isn't necessary at all.

                  if (currentIndex != this->previousIndex) {
                      if (this->previousIndex.isValid())
                          this->update(this->previousIndex);
                      if (currentIndex.isValid())
                          this->update(currentIndex);
                  }
                  else if (currentButtonState != this->previousButtonState) {
                      this->update(currentIndex);
                  }
                  this->previousIndex = currentIndex;
                  this->previousButtonState = currentButtonState;
                  
                  
                  ? Offline
                  ? Offline
                  A Former User
                  wrote on last edited by
                  #17

                  @jeremy_k
                  Thank you for pointing this out. Can you explain your example in more detail? For example, I don't understand how to get the "currentButtonState" or "previousButtonState". I don't have a "real" button, but only two QRects, one representing the "Button" and the other the "Label/Text".

                  How do I implement this in the mouseMoveEvent function of the custom QListView?

                  jeremy_kJ 1 Reply Last reply
                  0
                  • ? A Former User

                    @jeremy_k
                    Thank you for pointing this out. Can you explain your example in more detail? For example, I don't understand how to get the "currentButtonState" or "previousButtonState". I don't have a "real" button, but only two QRects, one representing the "Button" and the other the "Label/Text".

                    How do I implement this in the mouseMoveEvent function of the custom QListView?

                    jeremy_kJ Offline
                    jeremy_kJ Offline
                    jeremy_k
                    wrote on last edited by
                    #18

                    @Gabber said in Mouse hover on QRect not working as expected:

                    @jeremy_k
                    Thank you for pointing this out. Can you explain your example in more detail? For example, I don't understand how to get the "currentButtonState" or "previousButtonState". I don't have a "real" button, but only two QRects, one representing the "Button" and the other the "Label/Text".

                    How do I implement this in the mouseMoveEvent function of the custom QListView?

                    Use the logic already present in ToggleListDelegate::paint() and ToggleListView::mouseMoveEvent().
                    eg: bool currentButtonState = button.contains(viewport->mapFromGlobal(QCursor::pos()))

                    Asking a question about code? http://eel.is/iso-c++/testcase/

                    1 Reply Last reply
                    0
                    • ? Offline
                      ? Offline
                      A Former User
                      wrote on last edited by
                      #19

                      A very big thanks goes to @jeremy_k . Your tip and the code example was worth its weight in gold. Thank you!

                      Now my code looks like this:
                      Delegate:

                      void ToggleListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                      {
                      	QString text = index.data(Qt::DisplayRole).toString();
                      	QRect original = option.rect;
                      	QRect button = original.adjusted(original.width() - mButtonSize.width(),0,0,0);
                      	QRect label = original.adjusted(0,0, -mButtonSize.width(),0);
                      	painter->drawText(label, Qt::AlignLeft, text);
                      
                      	QIcon icon = QIcon(":/icons/trash_black.png");
                      	icon.paint(painter, button);
                      
                      	const auto widget = qobject_cast<ToggleListView*>(option.styleObject);
                      
                      	QPoint cursor = QCursor::pos();
                      	QPoint position = widget->viewport()->mapFromGlobal(cursor);
                      
                      	if(button.contains(position)){
                      		const bool hover = option.state & QStyle::State_MouseOver;
                      		if(hover){
                      			icon = QIcon(":/icons/trash_red.png");
                      			icon.paint(painter, button);
                      		}
                      	}
                      	painter->save();
                      	painter->restore();
                      }
                      

                      mouseMoveEvent:

                      void ToggleListView::mouseMoveEvent(QMouseEvent *event)
                      {
                      	QPoint position = event->pos();
                      	QModelIndex index = indexAt(position);
                      	QRect original = visualRect(index);
                      	QRect button = original.adjusted(original.width() - mButtonSize.width(),0,0,0);
                      
                      	bool currentButtonState = button.contains(position);
                      
                      	if(index != mPreviousIndex){
                      		if(mPreviousIndex.isValid())
                      			update(mPreviousIndex);
                      		if(index.isValid())
                      			update(index);
                      	} else if(currentButtonState != mPreviousButtonState){
                      		update(index);
                      	}
                      	mPreviousIndex = index;
                      	mPreviousButtonState = currentButtonState;
                      }
                      
                      jeremy_kJ 1 Reply Last reply
                      0
                      • ? A Former User

                        A very big thanks goes to @jeremy_k . Your tip and the code example was worth its weight in gold. Thank you!

                        Now my code looks like this:
                        Delegate:

                        void ToggleListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                        {
                        	QString text = index.data(Qt::DisplayRole).toString();
                        	QRect original = option.rect;
                        	QRect button = original.adjusted(original.width() - mButtonSize.width(),0,0,0);
                        	QRect label = original.adjusted(0,0, -mButtonSize.width(),0);
                        	painter->drawText(label, Qt::AlignLeft, text);
                        
                        	QIcon icon = QIcon(":/icons/trash_black.png");
                        	icon.paint(painter, button);
                        
                        	const auto widget = qobject_cast<ToggleListView*>(option.styleObject);
                        
                        	QPoint cursor = QCursor::pos();
                        	QPoint position = widget->viewport()->mapFromGlobal(cursor);
                        
                        	if(button.contains(position)){
                        		const bool hover = option.state & QStyle::State_MouseOver;
                        		if(hover){
                        			icon = QIcon(":/icons/trash_red.png");
                        			icon.paint(painter, button);
                        		}
                        	}
                        	painter->save();
                        	painter->restore();
                        }
                        

                        mouseMoveEvent:

                        void ToggleListView::mouseMoveEvent(QMouseEvent *event)
                        {
                        	QPoint position = event->pos();
                        	QModelIndex index = indexAt(position);
                        	QRect original = visualRect(index);
                        	QRect button = original.adjusted(original.width() - mButtonSize.width(),0,0,0);
                        
                        	bool currentButtonState = button.contains(position);
                        
                        	if(index != mPreviousIndex){
                        		if(mPreviousIndex.isValid())
                        			update(mPreviousIndex);
                        		if(index.isValid())
                        			update(index);
                        	} else if(currentButtonState != mPreviousButtonState){
                        		update(index);
                        	}
                        	mPreviousIndex = index;
                        	mPreviousButtonState = currentButtonState;
                        }
                        
                        jeremy_kJ Offline
                        jeremy_kJ Offline
                        jeremy_k
                        wrote on last edited by
                        #20

                        Happy to help.

                        @Gabber said in Mouse hover on QRect not working as expected:

                        Now my code looks like this:
                        Delegate:

                        void ToggleListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
                        {
                            [...]
                        	painter->save();
                        	painter->restore();
                        }
                        

                        QPainter::save() and restore are only necessary if the painter's settings are altered and need to be restored later. For example:

                        painter->save();
                        painter->setBrush(QBrush(QColorConstant::Green);
                        painter->drawRect(0, 0, 10, 10);
                        painter->restore();
                        

                        Asking a question about code? http://eel.is/iso-c++/testcase/

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved