Search code examples
c++qtqgraphicsviewqt6

How to make a tab-like or ribbon-like label on a QGraphicsView?


What would be a good way to create a ribbon-like label for displaying text in a QGraphicsView which contains a QGraphicsScene such that the ribbon "floats above" the scene? i.e. is independent of the translation and other transformations of the scene and remains fixed at the bottom of the view.

I want to implement such a ribbon as a sort of status line to indicate when the application is in a modal state, but don't know if it's possible to do easily. (If it's not I'll think of something else) I don't need code just a general idea of how to approach this.

enter image description here


Solution

  • An easy way to do this is to override drawForeground in a custom scene. The trick is that drawForeground will be passed a QPainter that is set up to draw in scene coordinates but we want to draw in coordinates relative to the widget rectangle of the view, to do this you can simply set the painter's transform to the identity matrix.

    Code below. I call the content rectangle of the view minus the area taken up by any scroll bars "the client rectangle" (because this is what it would be called in Win32) beyond that this code assumes that the custom scene is in one and only one view and the scene has getters/setters for showing/hiding a ribbon called "the status line".

    // ...
    
    QRect client_rectangle(const QGraphicsView* view) {
        auto view_rect = view->contentsRect();
        if (view->verticalScrollBar()->isVisible()) {
            auto wd = view_rect.width();
            view_rect.setWidth(wd - view->verticalScrollBar()->width());
        }
        if (view->horizontalScrollBar()->isVisible()) {
            auto hgt = view_rect.height();
            view_rect.setHeight(hgt - view->horizontalScrollBar()->height());
        }
        return view_rect;
    }
    
    // ...
    
    void MyCustomGraphicsScene::drawForeground(QPainter* painter, const QRectF& rect) {
        QGraphicsScene::drawForeground(painter, rect);
    
        if (is_status_line_visible()) {
            QGraphicsView* view = views().first();
            if (!view) {
                return;
            }
    
            painter->setTransform(QTransform());
    
            auto view_rect = client_rectangle(view);
            QRect ribbon_rect = {
                view_rect.bottomLeft() - QPoint(0, 35),
                QSize(view_rect.width(), 35)
            };
            painter->fillRect(ribbon_rect, Qt::yellow);
            painter->setPen(Qt::black);
            painter->drawText(ribbon_rect, Qt::AlignRight | Qt::AlignVCenter, status_line_);
    
            painter->resetTransform();
        }
    }