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. Fully Functional PushButtons drawn inside a QStyleDelegate

Fully Functional PushButtons drawn inside a QStyleDelegate

Scheduled Pinned Locked Moved Solved General and Desktop
7 Posts 1 Posters 277 Views
  • 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.
  • A Offline
    A Offline
    Andrea_Venturelli
    wrote on 22 Feb 2025, 18:57 last edited by
    #1

    hi everyone, this is not a support request for some bugged code. Instead a want to share with the community a working example of one of my latest project in the hope to help someone else developing is own version and doesn't know were to start.

    The Reason of this post

    for the sake of curiosity I was always wondering I to integrate button-like functionality inside a QStyledItemDelegate capable of emitting signals and react to clickedState or change if hovered or pressed and I never found a topic related to it, so I decided to do it my self, sharing my personal implementation of that. It's far from being bulletproof or the most optimezed way but I think will be a great starting point to share and learn.

    Feel free to ask questions or share your opition to make improvements to the code

    What you'll learn from this post
    1. Basic understanding on how a QStyledItemDelegate work
    2. Draw dynamic buttons that react to mouse events and emit signal
    3. A shallow notion of custom Models that pass data to the view.
    The final result looks like the following:

    a5f09d50-08a5-48da-9d13-23881b208251-image.png

    the above image retrives the "GroupName" (QString) and "Elements count" (QString) from the model.
    "GroupName" is simply the data retrived from QAbstractListModel::data( index, displayRole) whereas
    "Elements count" is build from QList<Obj>::count() and returned as QUserRole+1 from within the model::data() method

    step 1: the model

    In my case the model is quite simple, I store a QMap< QString, QList<PathInfo>> where the QString (key) is the groupName that you see in the screenshot and where the PathInfo is a User defined class that for semplicity we can drain to this implementation and the model definition is as follows

    struct PathInfo
    {
        QString path{ "C:\\symple\\path" };
        bool isOpenable{ true };
    
        PathInfo( QString _path, bool openable= true ) : path{_path}, isOpenable{openable} {}
    };
    
    
    class GroupsListModel : public QAbstractListModel
    {
        Q_OBJECT
    public:
        enum Roles {
            ItemCount = Qt::UserRole + 1
        };
    
        enum RowsOperation {
            Add    = 0,
            Remove = 1
        };
    
        explicit GroupsListModel(QMap< QString, QList<PathInfo>>& map_groups ,QWidget *parent = nullptr);
    
        /*! overridden virtual methods */
        int rowCount( const QModelIndex& parent= QModelIndex() ) const override;
        QVariant data( const QModelIndex& index, int role= Qt::DisplayRole ) const override;
        //----------------------------------------------------------------------------------------
        void addGroup( const QString& groupName );
        void addGroup( const QString& groupName, QList<PathInfo> pInfoList );
        QList<PathInfo> removeGroup( const QString& groupName );
    
        inline QString groupName(int row) const { return m_groupsName[row]; }
        inline     int itemCount(int row) const { return m_groups[m_groupsName[row]].count(); }
    
        inline int indexOfGroup( const QString& groupName ) const { return m_groupsName.indexOf( groupName ); }
    
    private:
        QMap<QString, QList<PathInfo>>& m_groups;
        QStringList m_groupsName;
    };
    

    because QMap is "unordered" (meaning that doesn't keep the order of the key in the same order which them is been added) and can't be retrived by index like the QList, I had to use "beginResetModel" inside an "addGroup()" method and also for deleting records with the method "removeGroup()" instead of relaying on "insertRows()" and "removeRows()" declared virtual on the parent

    // model's logic

    GroupsListModel::GroupsListModel(QMap<QString, QList<PathInfo>>& map_groups, QWidget *parent)
        : QAbstractListModel{parent}, m_groups{ map_groups }, m_groupsName{ map_groups.keys() }
    {}
    
    /*!
     *  overridden methods
    */
    int GroupsListModel::rowCount( const QModelIndex& parent ) const
    {
        Q_UNUSED( parent )
        return m_groups.count();
    }
    
    QVariant GroupsListModel::data( const QModelIndex& index, int role ) const
    {
        if ( !index.isValid() )
        {
            qDebug() << "invalid Index: model.data()";
            return QVariant();
        }
        switch ( role )
        {
            case Qt::DisplayRole:
            {
                // return the groupName at index= row
                return QVariant( m_groupsName[ index.row() ] );
            }
            case GroupsListModel::Roles::ItemCount:
            {
                QString elem_count("Elements count: ");
                elem_count += QString::number( m_groups[ m_groupsName[index.row()] ].count() );
                return QVariant( elem_count );
            }
            default:
                return QVariant();
        }
    }
    
    void GroupsListModel::addGroup( const QString& groupName )
    {
        if ( m_groups.contains( groupName ) )
        {
            QMessageBox::warning(
                nullptr, "Duplicate Group Name",
                "Sorry, this group name is already in use.\n Try another one."
            );
            return;
        }
        beginResetModel();
    
        // add the new key to QMap
        m_groups[ groupName ] = QList<PathInfo>();
    
        // update the QStringList for Indexing accessing
        m_groupsName = m_groups.keys();
    
        endResetModel();
        return;
    }
    
    void GroupsListModel::addGroup( const QString& groupName,  QList<PathInfo> pInfoList )
    {
        if ( m_groups.contains( groupName ) )
        {
            QMessageBox::warning(
                nullptr, "Duplicate Group Name",
                "Sorry, this group name is already in use.\n Try another one."
                );
            return;
        }
        beginResetModel();
    
        // add the new key to QMap
        m_groups[ groupName ] = pInfoList;
        // update the QStringList for Indexing accessing
        m_groupsName = m_groups.keys();
    
        endResetModel();
        return;
    }
    
    QList<PathInfo> GroupsListModel::removeGroup( const QString& groupName )
    {
        if ( !m_groups.contains( groupName ) )
        {
            QMessageBox::warning(
                nullptr, "Unexisting Group",
                "Sorry, a not existing group could not be removed.\n Try to select a group first."
                );
            return QList<PathInfo>();
        }
        beginResetModel();
    
        // remove the key from the map
        QList<PathInfo> temp = m_groups.take( groupName );
        // update the QStringList for Indexing accessing
        m_groupsName = m_groups.keys();
    
        endResetModel();
    
        return temp;
    }
    
    1 Reply Last reply
    0
    • A Offline
      A Offline
      Andrea_Venturelli
      wrote on 25 Mar 2025, 14:35 last edited by
      #7

      I ended up using a slightly different approach that stores each button style inside a QHash inside the custom TableModel.
      the code is available on github at:
      https://github.com/aVenturelli-qt/ArchWay-Project-Manager/tree/main/models

      1 Reply Last reply
      0
      • A Offline
        A Offline
        Andrea_Venturelli
        wrote on 22 Feb 2025, 19:18 last edited by
        #2

        Step 2: Subclassing QStyledItemDelegate

        After the model is ready, and all the data can be retrived by the correct Qt::Role we can procede to subclassing the styledDelegate implementing at least this virtual methods:

        void paint(
                QPainter* p, const QStyleOptionViewItem& option,
                const QModelIndex& index
            ) const override;
        
        QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
        
        /*
         *  When editing of an item starts, this function is called with the event that triggered the editing,
         *  the model, the index of the item, and the option used for rendering the item.
         *  Mouse events are sent to editorEvent() even if they don't start editing of the item.
         *
         *  This can, for instance, be useful if you wish to open a context menu when the right mouse button is pressed on an item.
         *  The base implementation returns false (indicating that it has not handled the event).
        */
        bool editorEvent(
                QEvent* event, QAbstractItemModel* model,
                const QStyleOptionViewItem& option, const QModelIndex& index
            ) override;
        

        Paint() is responsable of drawing each item in the list at a time (so don't call painter.end() at the end or you will see only the first one ;) )

        sizeHint is fondamental for suggest to the view, how much space allocate for each element based on your needs.

        editorEvent is the pivot method that allows us to make our drawn buttons feel alive and emit signals when pressed

        here the Delegate Definition (part 1):

        class GroupsListDelegate : public QStyledItemDelegate
        {
            Q_OBJECT
        public:
            enum BtnState
            {
                Default  = 0,
                Hover    = 2,
                Pressed  = 4,
                Disabled = 14
            };
        
            explicit GroupsListDelegate(QWidget *parent = nullptr);
        
            // setter getter - to access ButtonStyle
            inline void setTopButtonsStyle   ( ButtonStyle btnStyle ) { m_top_btn_style = btnStyle; }
            inline void setBottomButtonsStyle( ButtonStyle btnStyle ) { m_bottom_btn_style = btnStyle; }
            inline void setButtonsStyle( ButtonStyle top_btn, ButtonStyle bottom_btn ) { m_top_btn_style = top_btn;  m_bottom_btn_style = bottom_btn;}
            inline std::pair<ButtonStyle, ButtonStyle> buttonsStyle() { return std::make_pair( m_top_btn_style, m_bottom_btn_style); }
        
            void paint(
                QPainter* p, const QStyleOptionViewItem& option,
                const QModelIndex& index
            ) const override;
        
            QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
        
        signals:
            void editButtonClickedGroup( const QString group );
            void editButtonClicked( const QModelIndex& modelIndex );
            void openAllButtonClickedGroup( const QString group );
            void openAllButtonClicked( const QModelIndex& modelIndex );
        
        protected:
            bool editorEvent(
                QEvent* event, QAbstractItemModel* model,
                const QStyleOptionViewItem& option, const QModelIndex& index
            ) override;
        
        private:
            QListView* m_view{};
            ButtonStyle m_top_btn_style{};
            ButtonStyle m_bottom_btn_style{};
            GroupsListDelegate::BtnState top_btn_state{ BtnState::Default };
            GroupsListDelegate::BtnState bottom_btn_state{ BtnState::Default };
        
            QPoint m_lastMousePressedPos{};
        
            /**
             * @param font: the font you want to get the right size from
             * @param text: the text to use for calculating Height and Width
             * @param painter: given a QPainter this will automatically call painter->setFont( font )
            */
            QRect _rectFromFontMetrics(
                const QFont& font, const QString& text,
                QPainter* painter= nullptr
            ) const;
        
        
            void _drawButton(
                QRect button_rect, QString text,
                QPainter* p, GroupsListDelegate::BtnState state,
                bool isTopBtn= true
            ) const;
        
            bool _updateStateButton(
                QPoint mousePos, QRect btnRect,
                BtnState& currState, BtnState newState
            );
        
            bool _isMouseEvent( QEvent* event ) const;
            QRect& _offsetXYRect(int x_offset, int y_offset, QRect& originalRect ) const;
        
            // help variable to draw inside the paint method
            static constexpr int PADDING{ 16 };
            static constexpr int TEXT_SPACING{ 12 };
            //----------------------------------------
            static constexpr int BUTTONS_SPACING{ 8 };
            //----------------------------------------
            static constexpr int BUTTON1_HEIGHT{ 32 };
            static constexpr int BUTTON2_HEIGHT{ 40 };
            //----------------------------------------
            static constexpr int BUTTONS_WIDTH{ 100 };
            static constexpr int BG_BORDER_RAD{ 8 };
        
            static QFont  GROUP_TEXT_FONT;
            static QFont  SUB_TEXT_FONT;
            static QFont  BTN_FONT;
            //---------------------------------------
            static QColor BG_COLOR;
            static QColor BG_COLOR_SELECTED;
            //---------------------------------------
        
           // this will create a cool border around the selected item in the list
            static QConicalGradient _createConicalGradient( QPoint center )
            {
                QConicalGradient con_grad( center, 0 );
                con_grad.setColorAt(  0.1, QColor(250, 102, 192) );
                con_grad.setColorAt(  0.3, QColor(251, 173,  56) );
                con_grad.setColorAt( 0.55, QColor(218, 152, 251) );
                con_grad.setColorAt( 0.88, QColor( 91, 183, 244) );
                con_grad.setColorAt( 0.98, QColor(250, 102, 192) );
        
                return con_grad;
            }
        };
        
        1 Reply Last reply
        0
        • A Offline
          A Offline
          Andrea_Venturelli
          wrote on 22 Feb 2025, 19:22 last edited by
          #3

          here the implementation of Paint and editorEvent and cunstroctor where is crucial to set "setMouseTracking(true)" to be able to track the mouse location

          GroupsListDelegate::GroupsListDelegate(QWidget *parent)
              : QStyledItemDelegate{parent}
          {
              m_view = qobject_cast<QListView*>( parent );
              // needed to check the hover state
              // even when LeftMouseButton isn't pressed
              m_view->setMouseTracking( true );
          }
          
          bool GroupsListDelegate::editorEvent(
                  QEvent* event, QAbstractItemModel* model,
                  const QStyleOptionViewItem& option, const QModelIndex& index
              )
          {
              Q_UNUSED(option)
          
              auto* _model = qobject_cast<GroupsListModel*>( model );
              const QString str(_model->data( index, Qt::DisplayRole ).toString() );
          
              if ( !this->_isMouseEvent( event ))
                  return false;
          
              // from now on, the event could only be one of mouseEvent
              auto* mouseEvent = static_cast<QMouseEvent*>( event );
              QPoint cursorPosition{ mouseEvent->pos() };
              bool event_handled{ false };
              int pen_offset{ 0 };                    // accounts for various border-width;
          
              QRect editBtn{
                  option.rect.topRight().x() - PADDING - BUTTONS_WIDTH,
                  option.rect.center().y() - BUTTONS_SPACING - BUTTON1_HEIGHT,
                  BUTTONS_WIDTH,
                  BUTTON1_HEIGHT
              };
          
              QRect OpenAllBtn{
                  option.rect.topRight().x() - PADDING - BUTTONS_WIDTH,
                  option.rect.center().y(),
                  BUTTONS_WIDTH,
                  BUTTON2_HEIGHT
              };
          
              switch ( event->type() )
              {
                  // - - - - - - - - - - - - - - - - - BTN HOVER - - - - - - - - - - - - - - - - - - - - - - - -
                  case QEvent::MouseButtonRelease:
                  case QEvent::MouseMove:
                  {
                      // ------ TOP BUTTON HOVER ---------
                      pen_offset = m_top_btn_style.pen_hover.width();
                      editBtn = this->_offsetXYRect( pen_offset, pen_offset, editBtn );
          
                      event_handled = this->_updateStateButton(
                          cursorPosition, editBtn,
                          top_btn_state, BtnState::Hover
                          );
          
                      if ( event_handled )
                      {
                          if ( event->type() == QEvent::MouseButtonRelease )
                          {
                              emit editButtonClicked( index );
                              emit editButtonClickedGroup( str );
                          }
                          return true;
                      }
          
                      // ------ BOTTOM BUTTON HOVER ---------
                      pen_offset = m_bottom_btn_style.pen_hover.width();
                      OpenAllBtn = this->_offsetXYRect( pen_offset, pen_offset, OpenAllBtn );
          
                      event_handled = this->_updateStateButton(
                          cursorPosition, OpenAllBtn,
                          bottom_btn_state, BtnState::Hover
                          );
          
                      if ( event_handled )
                      {
                          if ( event->type() == QEvent::MouseButtonRelease )
                          {
                              emit openAllButtonClicked( index );
                              emit openAllButtonClickedGroup( str );
                          }
                          return true;
                      }
          
                      return false;
                  }
                  // - - - - - - - - - - - - - - - - - - BTN PRESSED - - - - - - - - - - - - - - - - - - - - - - - -
                  case QEvent::MouseButtonPress:
                  {
                      if ( mouseEvent->buttons() == Qt::LeftButton )
                      {
                          // ------ TOP BUTTON PRESSED ---------
                          pen_offset = m_top_btn_style.pen_pressed.width();
                          editBtn = this->_offsetXYRect( pen_offset, pen_offset, editBtn );
          
                          event_handled = this->_updateStateButton(
                              cursorPosition, editBtn,
                              top_btn_state, BtnState::Pressed
                              );
                          if ( event_handled ) return true;
          
                          // ------ BOTTOM BUTTON PRESSED ---------
                          pen_offset = m_bottom_btn_style.pen_pressed.width();
                          OpenAllBtn = this->_offsetXYRect( pen_offset, pen_offset, OpenAllBtn );
          
                          event_handled = this->_updateStateButton(
                              cursorPosition, OpenAllBtn,
                              bottom_btn_state, BtnState::Pressed
                              );
                          if ( event_handled ) return true;
          
                          return false;
                      }
          
                      return false;
                  }
                  // - - - - - - - - - - - - - - - - DEFAULT STATE - - - - - - - - - - - - - - - - - - - -
                  default:
                      return false;
                  }
          }
          
          
          void GroupsListDelegate::paint(
                  QPainter* p, const QStyleOptionViewItem& option,
                  const QModelIndex& index
              ) const
          {
              p->save();
              p->setRenderHint( QPainter::Antialiasing, true );
              p->setRenderHint( QPainter::TextAntialiasing, true );
          
              const bool isCardSelected = option.state & QStyle::State_Selected ;
          
              // draw the bg
              QString groupName  = index.data( Qt::DisplayRole ).toString();
              QString elem_count = index.data( Qt::UserRole + 1 ).toString();
          
              // qDebug() << "\nGroupName: " << groupName << ", element counts: " << elem_count;
          
              QPainterPath clipPath;
              clipPath.addRoundedRect( option.rect, BG_BORDER_RAD, BG_BORDER_RAD );
              p->setClipPath( clipPath );
          
              p->setPen( Qt::NoPen );
              p->fillRect( option.rect, BG_COLOR );
          
              // change border color of an item when selected
              if ( isCardSelected )
              {
                  auto border_brush = QBrush(GroupsListDelegate::_createConicalGradient( option.rect.center()));
                  // bg selected state
                  p->setPen( QPen( border_brush, 6.0 ));
                  p->drawRoundedRect( option.rect, BG_BORDER_RAD, BG_BORDER_RAD );
              } else {
                  // normal bg color
                  p->setPen( Qt::NoPen );
                  p->drawRoundedRect( option.rect, BG_BORDER_RAD, BG_BORDER_RAD );
              }
          
              //              DRAWING THE TWO TEXT
              //---------------------------------------------------
              auto txt_color = (isCardSelected)? QColor(231, 143,  26) : Qt::black;
              p->setPen( txt_color );
          
              auto upper_txt_rect = this->_rectFromFontMetrics( GROUP_TEXT_FONT, groupName, p );
          
              upper_txt_rect.moveBottomLeft(
                  QPoint(
                      option.rect.x() + PADDING,
                      option.rect.center().y()
                      )
                  );
          
              // draw the groupText offsetted by 16px on the x
              p->drawText( upper_txt_rect, groupName );
          
          
              // BOTTOM TEXT ------------------
              QRect lower_txt_rect = this->_rectFromFontMetrics( SUB_TEXT_FONT, elem_count, p );
          
              lower_txt_rect.moveTopLeft(
                  QPoint(
                      upper_txt_rect.bottomLeft() + QPoint( 0, TEXT_SPACING)    // padding 12px on the y
                      )
                  );
          
              p->setPen( QPen( Qt::black ) );
              p->drawText( lower_txt_rect, elem_count );
          
              //              DRAWING TWO PUSHBUTTON
              //---------------------------------------------------
          
              // ------- BUTTON1 (AKA TOP BUTTON) -------
              // the top button's bottomRight corner is placed 12px above the option.rect center
              // and 16px (PADDING) from the right of the option.rect
              QRect edit_btn{
                  option.rect.topRight().x() - PADDING - BUTTONS_WIDTH,
                  option.rect.center().y() - BUTTONS_SPACING - BUTTON1_HEIGHT,
                  BUTTONS_WIDTH,
                  BUTTON1_HEIGHT
              };
          
              // see next comment for explanation
              if ( edit_btn.contains( m_lastMousePressedPos ) && ( top_btn_state == BtnState::Pressed ) )
              {
                  this->_drawButton( edit_btn, "Edit Group", p, top_btn_state );
              } else {
                  this->_drawButton( edit_btn, "Edit Group", p, BtnState::Default );
              }
          
              QRect open_btn{
                  option.rect.topRight().x() - PADDING - BUTTONS_WIDTH,
                  option.rect.center().y(),
                  BUTTONS_WIDTH,
                  BUTTON2_HEIGHT
              };
          
              // As before, this line fixes a but that occure when,
              // after selecting and deleting the last element
              // and press one of the buttons, will trigger an update
              // in the style for all the others
              if ( open_btn.contains( m_lastMousePressedPos ) && ( bottom_btn_state == BtnState::Pressed ) )
              {
                  this->_drawButton( open_btn, "Open All", p, bottom_btn_state, false );
              } else {
                  this->_drawButton( open_btn, "Open All", p, BtnState::Default, false );
              }
              // reset for next item
              p->restore();
          }
          
          
          1 Reply Last reply
          0
          • A Offline
            A Offline
            Andrea_Venturelli
            wrote on 22 Feb 2025, 19:42 last edited by
            #4

            here the remaing methods that doesn't fit in the previous post for some forum's limitation on the message lenght:

            
            QSize GroupsListDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
                Q_UNUSED(option)
                Q_UNUSED(index)
                return QSize(  280, 2*PADDING + BUTTON1_HEIGHT + BUTTONS_SPACING + BUTTON2_HEIGHT + 12 );
            }
            
            /*-------------------------------------------------------------
             *                       HELPER FUNCTIONS
             *-------------------------------------------------------------*/
            QRect GroupsListDelegate::_rectFromFontMetrics( const QFont& font, const QString& text, QPainter* painter) const {
                if ( painter )
                    painter->setFont( font );
            
                QFontMetrics fnt_m( font );
                return fnt_m.boundingRect( text );
            }
            
            void GroupsListDelegate::_drawButton(
                    QRect button_rect, QString text,
                    QPainter* p, GroupsListDelegate::BtnState state,
                    bool isTopBtn
                ) const
            {
                auto previous_font  = p->font();
                auto previous_brush = p->brush();
                auto previous_pen   = p->pen();
                p->setFont( BTN_FONT );
            
                std::pair<QColor, QPen> fill_pen_pair;
            
                switch ( state ) {
                    case BtnState::Default:
                    {
                        fill_pen_pair = ( isTopBtn ) ? m_top_btn_style.fillAndPenStroke() : m_bottom_btn_style.fillAndPenStroke();
                        break;
                    }
                    case BtnState::Hover:
                    {
                        fill_pen_pair =
                            ( isTopBtn ) ?
                            m_top_btn_style.fillAndPenStroke( ButtonStyle::Hover ) :
                            m_bottom_btn_style.fillAndPenStroke( ButtonStyle::Hover );
                        break;
                    }
                    case BtnState::Pressed:
                    {
                        fill_pen_pair =
                            ( isTopBtn ) ?
                            m_top_btn_style.fillAndPenStroke( ButtonStyle::Pressed ) :
                            m_bottom_btn_style.fillAndPenStroke( ButtonStyle::Pressed );
                        break;
                    }
                    case BtnState::Disabled:
                    {
                        fill_pen_pair =
                            ( isTopBtn ) ?
                            m_top_btn_style.fillAndPenStroke( ButtonStyle::Disabled ) :
                            m_bottom_btn_style.fillAndPenStroke( ButtonStyle::Disabled );
                        break;
                    }
                    default:
                        qDebug() << "\nInvalid state..." ;
                }
                auto [ fill, pen ] = fill_pen_pair;
                p->setBrush( fill );
                p->setPen( pen );
            
                // frame btn
                p->drawRoundedRect( button_rect, 4, 4 );
            
                // text
                p->setPen( Qt::black );
                p->drawText( button_rect, Qt::AlignCenter, text );
            
                // restore original font, brush and pen
                p->setFont( previous_font );
                p->setBrush( previous_brush );
                p->setPen( previous_pen );
                return;
            }
            
            QRect& GroupsListDelegate::_offsetXYRect(int x_offset, int y_offset, QRect& originalRect ) const{
                originalRect.translate( x_offset, y_offset );
                return originalRect;
            }
            
            bool GroupsListDelegate::_isMouseEvent( QEvent* event ) const{
                switch ( event->type() ) {
                case QEvent::MouseMove:
                case QEvent::MouseButtonPress:
                case QEvent::MouseButtonRelease:
                case QEvent::MouseButtonDblClick:
                case QEvent::Wheel:
                    return true;
                default:
                    return false;
                }
            }
            
            /** If the click is inside the button
             *  but the button is Disabled, simply return true
             *  to consume the mouseEvent
             * Otherwise checks the button state*/
            bool GroupsListDelegate::_updateStateButton( QPoint mousePos, QRect btnRect, BtnState& currState, BtnState newState  ) {
                m_lastMousePressedPos = mousePos;
            
                // ignore event if button is set to disabled
                if (( currState == BtnState::Disabled && btnRect.contains( mousePos )) )  {
                    qDebug() << "\nNothing changed.. the button is disabled or is already in the newState";
                    return true;
                }
            
                if ( btnRect.contains( mousePos ) ) {
                    currState = newState;
                    // repaint the button to reflect the new state
                    m_view->update( btnRect );
                    return true;
                } else {
                    currState = BtnState::Default;
                    btnRect.adjust( -10, -10, +10, +10 );
            
                    // repaint the button to reflect the new state
                    m_view->update( btnRect );
                    return false;
                }
                return false;
            }
            
            1 Reply Last reply
            0
            • A Offline
              A Offline
              Andrea_Venturelli
              wrote on 22 Feb 2025, 19:49 last edited by
              #5
              Which is the purpose of the ButtonStyle class:

              In order to rappresent the various status of the buttons ( hover, pressed, disabled and default ) I defined this simple class that is responsable only for that and contains simply pairs of QColor that rappresent Fill ( background-color in stylesheets and Strokes ( equivalent to border-color ). This is a personal choise and someone will surely came up with a better and more elegant solution maybe utilazing QStyledOption.drawControls() for a more native look for the buttons.

              In my version of the StyledDelegate the state Disabled is not fully handled. To do so, you need to implement two extra methods called "setDisabledButton( bool disabled, bool setTopButton = true ) and hand set the ButtonState for the right button to BtnState::Disabled and do the same for the setEnabledButton() and test the cases if all works as expected

              1 Reply Last reply
              0
              • A Offline
                A Offline
                Andrea_Venturelli
                wrote on 22 Feb 2025, 19:53 last edited by
                #6

                this is an header only, so no .cpp file exists. this isn't the full implementation beacuse the server continue to mark it as spam

                struct ButtonStyle{
                    using FillStrokePair = std::pair<QColor, QColor>;
                
                    enum ButtonStates
                    {
                        Disabled = 0,
                        Default  = 1,
                        Hover    = 2,
                        Pressed  = 3
                    };
                
                    FillStrokePair FS_disabled{ std::make_pair( Qt::gray, Qt::darkGray) };
                    FillStrokePair FS_default { std::make_pair( Qt::white, Qt::black  ) };
                    FillStrokePair FS_hover   { std::make_pair( Qt::white, QColor(251, 173, 56) ) };
                    FillStrokePair FS_pressed { std::make_pair( QColor(251, 237, 214), QColor(251, 173, 56) ) };
                
                    QPen pen_disabled{ QPen( FS_disabled.second, 2.0, Qt::SolidLine ) };
                    QPen pen_default { QPen( FS_default.second , 2.0, Qt::SolidLine ) };
                    QPen pen_hover   { QPen( FS_hover.second   , 2.0, Qt::SolidLine ) };
                    QPen pen_pressed { QPen( FS_pressed.second , 2.0, Qt::SolidLine ) };
                
                
                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Andrea_Venturelli
                  wrote on 25 Mar 2025, 14:35 last edited by
                  #7

                  I ended up using a slightly different approach that stores each button style inside a QHash inside the custom TableModel.
                  the code is available on github at:
                  https://github.com/aVenturelli-qt/ArchWay-Project-Manager/tree/main/models

                  1 Reply Last reply
                  0
                  • A Andrea_Venturelli has marked this topic as solved on 25 Mar 2025, 14:36

                  • Login

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