2

我想制作一个类似于OSX“Dock”功能的滑动“侧边栏”(例如鼠标通过屏幕边缘并且Dock滑出)。我一直在玩,QDockWidget但由于它嵌入在窗口布局中,当它变得可见时,它会导致一切都发生变化。

有人可以建议一种方法来实现这一点吗?

  • 不需要浮动(作为单独的窗口/工具栏)
  • 应该缩放到窗口高度(例如窗口可以是全屏或默认大小)
  • 如果这很复杂,则不需要滑动(动画)。

我是 Qt 的新手,所以不想多想。这只是自定义小部件的问题还是我应该查看无边框窗口?自定义小部件方法似乎是正确的,但我不知道如何指定它覆盖其他窗口内容并在窗口缩放时也缩放。

4

1 回答 1

2

QDockWidget与你想要的无关 - 行为上。仅仅因为它被称为 Dock 小部件并不意味着它与 OS X 中的“Dock”概念相同。它仅仅意味着它停靠在某个地方。QDockWidget的文档非常明确地解释了对接行为的含义。

下面的代码实现了您似乎想要的行为。设计好不好,值得商榷。代码“复杂”的原因似乎暗示没有人会想出这样的 UI 设计。实际单击某处的按钮以显示滑块窗口有什么问题?

该代码适用于 Qt 4.8 和 5.1。

注意:这要求在 Qt Quick 2 中实现。这就是它的设计目的:) 当然 Qt 4.6+ 改进了 QWidget 移动动画的行为,Qt 5 做了进一步的调整,但实际上这段代码闻起来很糟糕,而且还有这样做的一个很好的理由:QWidget API 虽然功能强大,但最终封装了一组 API,这些 API 可以追溯到 1984 年最初的 Macintosh 发布时。当您必须从一堆堆叠的画家那里​​合成结果时,您能做的只有这么多。在 Qt Quick 中,渲染由 GPU 完成。动画相当于将几个新的浮点数传递给 GPU 以更新单个变换矩阵。而已。

#include <QApplication>
#include <QWidget>
#include <QGridLayout>
#include <QLabel>
#include <QPainter>
#include <QGradient>
#include <QMouseEvent>
#include <QPropertyAnimation>

class Slider : public QWidget {
    void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
        QPainter p(this);
        QLinearGradient g(QPointF(0,0), QPointF(rect().bottomRight()));
        g.setColorAt(0, Qt::blue);
        g.setColorAt(1, Qt::gray);
        p.setBackground(g);
        p.eraseRect(rect());
        p.setPen(Qt::yellow);
        p.setFont(QFont("Helvetica", 48));
        p.drawText(rect(), "Click Me To Hide");
    }
    void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE {
        hide();
    }
public:
    explicit Slider(QWidget *parent = 0) : QWidget(parent) {
        setAttribute(Qt::WA_OpaquePaintEvent);
    }
};

class Window : public QWidget {
    QGridLayout m_layout;
    Slider m_slider;
    QLabel m_label;
    QPropertyAnimation m_animation;
public:
    explicit Window(QWidget *parent = 0, Qt::WindowFlags f = 0) :
        QWidget(parent, f),
        m_layout(this),
        m_slider(this),
        m_animation(&m_slider, "pos")
   {
        setMouseTracking(true);
        m_layout.addWidget(&m_label);
        m_slider.hide();
        m_slider.setMouseTracking(false);
        m_animation.setStartValue(QPoint(-width(), 0));
        m_animation.setEndValue(QPoint(0, 0));
        m_animation.setDuration(500);
        m_animation.setEasingCurve(QEasingCurve::InCubic);
    }
    void leaveEvent(QEvent *) {
        if (window() && QCursor::pos().x() <= window()->geometry().topLeft().x()) {
            showSlider();
        }
    }
    void childEvent(QChildEvent * ev) {
        if (ev->added() && ev->child()->isWidgetType()) {
            ev->child()->installEventFilter(this);
            static_cast<QWidget*>(ev->child())->setMouseTracking(true);
        }
    }
    bool event(QEvent * ev) {
        eventFilter(this, ev);
        return QWidget::event(ev);
    }
    bool eventFilter(QObject *, QEvent * ev) {
        if (ev->type() == QEvent::MouseMove) {
            auto pos = QCursor::pos();
            if (window() && window()->isFullScreen()) {
                if (pos.x() <= window()->geometry().topLeft().x()) {
                    showSlider();
                }
            }
            m_label.setText(QString("%1, %2").arg(pos.x()).arg(pos.y()));
        }
        return false;
    }
    void resizeEvent(QResizeEvent *) {
        m_slider.resize(size());
        m_animation.setStartValue(QPoint(-width(), 0));
    }
    Q_SLOT void showSlider() {
        if (m_slider.isVisible() || (window() && qApp->activeWindow() != window())) return;
        m_slider.raise();
        m_slider.show();
        m_animation.start();
    }
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Window w;
    w.show();
    return a.exec();
}
于 2013-09-02T13:41:09.053 回答