Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Drag and Drop With Overlaying QWidget



  • I have a QTableView with a transparent widget overlayed on top of it. It's basically a watermark-label that tells the user what he should do to fill the table.

    The overlay widget is uses this
    @setAttribute(Qt::WA_TransparentForMouseEvents);@

    I then control its visibility based on whether the table has rows.

    Now, when the table is empty and the overlay label is displayed, I have a problem with dropping new items into the table. Even though the widget is "transparent for mouse events", when its visible I can no longer drop items.

    I think the solution lies in implementing the dragEnterEvent / dropEvent on the widget - but with my experimenting, I didn't manage to find the right one.

    Ideally, I would like to hide the overlay widget when I enter drag, show it if I exit drag, but if I drop something inside I would like the parent widget to process the whole thing, as if I dragged and dropped inside it. Preferably without subclassing QTableView.

    Some code:
    @
    //OverlayLabel.h
    class OverlayLabel : public QWidget
    {
    Q_OBJECT

    public:
    OverlayLabel(QWidget *parent = 0);
    ~OverlayLabel();

    int labelFontSizePoints();
    void setLabelFontSizePoints(int newSize);
    QString labelText();
    void setLabelText(const QString &newLabelText);
    void setDashBorderVisible(bool isVisible);

    void resizeByParent();

    private:
    static const QString LABEL_HTML_TEMPLATE;
    static const QString DASH_BORDER_CSS;

    void refreshLabel();

    int mFontSizePoints;
    QString mLabelPlainText;
    Ui::OverlayLabel ui; // the ui is the label surrounded by four spacers making sure it's pushed to the center
    };
    @

    @
    //OverlayLabel.cpp

    #include "overlaylabel.h"

    const QString OverlayLabel::LABEL_HTML_TEMPLATE = "<html><head/><body><p><span style=" font-size:%0pt;">%1</span></p></body></html>";
    const QString OverlayLabel::DASH_BORDER_CSS = "padding: 10px; border: 2px dashed grey";

    OverlayLabel::OverlayLabel(QWidget *parent)
    : QWidget(parent)
    {
    ui.setupUi(this);
    setPalette(Qt::transparent);

    setAttribute(Qt::WA_TransparentForMouseEvents);

    setAcceptDrops(true);
    mFontSizePoints = 16;
    }

    OverlayLabel::~OverlayLabel()
    {
    }

    int OverlayLabel::labelFontSizePoints()
    {
    return mFontSizePoints;
    }

    void OverlayLabel::setLabelFontSizePoints(int newSize)
    {
    mFontSizePoints = newSize;
    refreshLabel();
    }

    QString OverlayLabel::labelText()
    {
    return mLabelPlainText;
    }

    void OverlayLabel::setLabelText(const QString &newLabelText)
    {
    mLabelPlainText = newLabelText;
    refreshLabel();
    }

    void OverlayLabel::setDashBorderVisible(bool isVisible)
    {
    ui.label->setStyleSheet(isVisible ? DASH_BORDER_CSS : QString());
    }

    void OverlayLabel::resizeByParent()
    {
    resize(parentWidget()->size());
    }

    void OverlayLabel::refreshLabel()
    {
    ui.label->setText(LABEL_HTML_TEMPLATE.arg(mFontSizePoints).arg(mLabelPlainText));
    }

    void OverlayLabel::dragEnterEvent(QDragEnterEvent *event)
    {
    //ui.label->setVisible(false);
    //event->acceptProposedAction();
    }

    void OverlayLabel::dragLeaveEvent(QDragLeaveEvent *event)
    {
    //ui.label->setVisible(true);
    //event->accept();
    }

    void OverlayLabel::dropEvent(QDropEvent *event)
    {
    //ui.label->setVisible(true);
    //setVisible(false);
    //event->accept();
    }
    @

    @
    //usage:
    editorOverlay = new OverlayLabel(/* my tableview */);
    editorOverlay->setLabelText(tr("Drop Items Here"));
    editorOverlay->setDashBorderVisible(true);
    @


  • Lifetime Qt Champion

    Hi,

    Did you tried with an event filter ?



  • Hi SGalst, and thank you for your reply!

    No, I didn't. What would you suggest? Seeing if ignoring the drag related events would give me the wanted result?


  • Lifetime Qt Champion

    I was rather thinking about intercepting the events and sending them to the right widget (using postEvent)



  • Ah postEvent, thanks that will be useful.



  • I may require some more guidance here - still stuck.

    Here's what I did: I allowed the overlaying label to accept the drag events, but from the main application window, I used eventFilter to catch the drop event. I then tried to generate the QDragEnterEvent / QDragMoveEvent / QDropEvent objects myself in the eventFilter function and to use sendEvent().

    Though the sendEvent() call returns true, my check for isAccepted() tells me the event was not accepted.

    Any more help please?


  • Lifetime Qt Champion

    Why not use the events you are receiving ?



  • What do you mean, use them? I wanted to pass them on to the QTableView so that it handles them (eventually that would be activating the mime data drop functionality). But I couldn't get the sendEvent() to work, or so it seems.

    What I eventually did as a workaround: I filtered just the drop event and then used its mimedata to manually drop mime data onto the table view.


  • Lifetime Qt Champion

    I understood that you where creating new events based on those you received rather than sending them to the table view.

    Your workaround doesn't seem bad



  • I tried sending the original events and I would get crashes. I then tried creating new events and that just wouldn't work. Then workaround. :)


  • Lifetime Qt Champion

    Sounds reasonable for me :)

    You could to try asking on the mailing if there is another possibility for this (you might cross the path of a maintainer/developer with some other ideas on the matter)


Log in to reply