自定义标题栏

换Titlebar

class CT_CORE_EXPORT TitleBar : public QFrame
{
    Q_OBJECT

public:
    explicit TitleBar(QString titleName, QWidget *parent = nullptr);
    ~TitleBar();

    static void swapTitleBar(QString titleName, QWidget* wid, bool translucent = true);

protected:
    void mousePressEvent(QMouseEvent *event) override;

    void mouseMoveEvent(QMouseEvent *event) override;

private:
    Ui::TitleBar *ui;

    QPoint dragPosition;
};
TitleBar::TitleBar(QString titleName, QWidget *parent) :
    QFrame(parent),
    ui(new Ui::TitleBar)
{
    ui->setupUi(this);

    setFixedHeight(30);
    ui->label_title->setText(titleName);
    connect(ui->pushButton, &QPushButton::clicked, parent, &QWidget::close);
}

TitleBar::~TitleBar()
{
    delete ui;
}

void TitleBar::swapTitleBar(QString titleName, QWidget *wid, bool translucent) {
    // 去除系统标题栏
    wid->setWindowFlags(Qt::FramelessWindowHint);
    wid->setAttribute(Qt::WA_TranslucentBackground, translucent);

    // 获取 Designer 的主布局(假设是 verticalLayout)
    QBoxLayout *originalLayout = qobject_cast<QBoxLayout *>(wid->layout());
    if (!originalLayout) {
        originalLayout = new QVBoxLayout(wid);
        wid->setLayout(originalLayout);
    }

    // 插入自定义标题栏到顶部
    TitleBar *titleBar = new TitleBar(titleName, wid);
    originalLayout->setSpacing(0);
    originalLayout->insertWidget(0, titleBar);
}

void TitleBar::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        dragPosition = event->globalPos() - parentWidget()->pos();
        event->accept();
    }
}

void TitleBar::mouseMoveEvent(QMouseEvent *event) {
    if (event->buttons() & Qt::LeftButton) {
        parentWidget()->move(event->globalPos() - dragPosition);
        event->accept();
    }
}

实现拖拽功能

去掉标题栏后,mousemoveevent不会自动触发,即使设置了mousetracking。需要点击左键已经才能触发,所以设置hover触发。mousemove函数只响应拖动

class GrayAnalysisDialog : public QDialog
{
    Q_OBJECT

public:
    explicit GrayAnalysisDialog(vtkSmartPointer<vtkImageData> data, QWidget *parent = nullptr);
    ~GrayAnalysisDialog();

    enum ResizeRegion {
        None,
        Left, Right, Top, Bottom,
        TopLeft, TopRight, BottomLeft, BottomRight
    };

    ResizeRegion m_Region = None;
    bool m_resizing = false;
    QPoint m_dragStartPos;
    QRect m_startGeometry;
    int m_border = 6;

protected:
    void mouseMoveEvent(QMouseEvent *event);

    void mousePressEvent(QMouseEvent *event);

    void mouseReleaseEvent(QMouseEvent *);

    bool event(QEvent* event);

    ResizeRegion detectRegion(const QPoint& pos);

    void updateCursorShape(const QPoint& pos);

    void resizeWindow(const QPoint& globalPos);

private:
    Ui::GrayAnalysisDialog *ui;
};
GrayAnalysisDialog::GrayAnalysisDialog(vtkSmartPointer<vtkImageData> data, QWidget *parent) :
    QDialog(parent),
    ui(new Ui::GrayAnalysisDialog)
{
    setAttribute(Qt::WA_DeleteOnClose);
    this->resize(800, 600);
    ui->setupUi(this);
    TitleBar::swapTitleBar("灰度分析", this, false);
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground, false);
    setAttribute(Qt::WA_Hover, true);
    this->setMouseTracking(true);
}

void GrayAnalysisDialog::mouseMoveEvent(QMouseEvent *event)
{
    if (m_resizing) {
        resizeWindow(event->globalPos());
    }
    QDialog::mouseMoveEvent(event);
}

void GrayAnalysisDialog::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_Region = detectRegion(event->pos());
        if (m_Region != None) {
            m_resizing = true;
            m_dragStartPos = event->globalPos();
            m_startGeometry = geometry();
        }
    }
    QDialog::mousePressEvent(event);
}

void GrayAnalysisDialog::mouseReleaseEvent(QMouseEvent *event)
{
    m_resizing = false;
    m_Region = None;
    QDialog::mouseReleaseEvent(event);
}

bool GrayAnalysisDialog::event(QEvent *event)
{
    if (event->type() == QEvent::HoverMove) {
        QHoverEvent* hover = static_cast<QHoverEvent*>(event);
        updateCursorShape(hover->pos());
    }
    return QDialog::event(event);
}

GrayAnalysisDialog::ResizeRegion GrayAnalysisDialog::detectRegion(const QPoint &pos)
{
    const int x = pos.x();
    const int y = pos.y();
    const int w = width();
    const int h = height();

    bool onLeft = x <= m_border;
    bool onRight = x >= w - m_border;
    bool onTop = y <= m_border;
    bool onBottom = y >= h - m_border;

    if (onTop && onLeft) return TopLeft;
    if (onTop && onRight) return TopRight;
    if (onBottom && onLeft) return BottomLeft;
    if (onBottom && onRight) return BottomRight;
    if (onTop) return Top;
    if (onBottom) return Bottom;
    if (onLeft) return Left;
    if (onRight) return Right;

    return None;
}

void GrayAnalysisDialog::updateCursorShape(const QPoint &pos)
{
    switch (detectRegion(pos)) {
    case Top:
    case Bottom:
        setCursor(Qt::SizeVerCursor); break;
    case Left:
    case Right:
        setCursor(Qt::SizeHorCursor); break;
    case TopLeft:
    case BottomRight:
        setCursor(Qt::SizeFDiagCursor); break;
    case TopRight:
    case BottomLeft:
        setCursor(Qt::SizeBDiagCursor); break;
    default:
        setCursor(Qt::ArrowCursor); break;
    }
}

void GrayAnalysisDialog::resizeWindow(const QPoint &globalPos)
{
    QRect g = m_startGeometry;
    QPoint delta = globalPos - m_dragStartPos;

    switch (m_Region) {
    case Left:
        g.setLeft(g.left() + delta.x()); break;
    case Right:
        g.setRight(g.right() + delta.x()); break;
    case Top:
        g.setTop(g.top() + delta.y()); break;
    case Bottom:
        g.setBottom(g.bottom() + delta.y()); break;
    case TopLeft:
        g.setTopLeft(g.topLeft() + delta); break;
    case TopRight:
        g.setTopRight(g.topRight() + delta); break;
    case BottomLeft:
        g.setBottomLeft(g.bottomLeft() + delta); break;
    case BottomRight:
        g.setBottomRight(g.bottomRight() + delta); break;
    default:
        return;
    }

    // 可选:限制最小尺寸
    if (g.width() < minimumWidth()) g.setWidth(minimumWidth());
    if (g.height() < minimumHeight()) g.setHeight(minimumHeight());

    setGeometry(g);
}

在windows系统下,使用NativeEvent()直接实现

    win32{
        msvc:LIBS += User32.lib
        msvc:LIBS += gdi32.lib
    }
#include "Windows.h"
#include <windowsx.h>
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    const int borderWidth = 5;
    if (eventType == "windows_generic_MSG") {
        MSG* msg = static_cast<MSG*>(message);
        if (msg->message == WM_NCHITTEST) {
            RECT winRect;
            HWND hwnd = (HWND)winId();
            GetWindowRect(hwnd, &winRect);

            long x = GET_X_LPARAM(msg->lParam);
            long y = GET_Y_LPARAM(msg->lParam);
            int b = borderWidth;

            // 判断四个角
            if (x >= winRect.left && x < winRect.left+b &&
                y >= winRect.top && y < winRect.top+b) {
                *result = HTTOPLEFT; return true;
            }
            if (x < winRect.right && x >= winRect.right-b &&
                y >= winRect.top && y < winRect.top+b) {
                *result = HTTOPRIGHT; return true;
            }
            if (x >= winRect.left && x < winRect.left+b &&
                y < winRect.bottom && y >= winRect.bottom-b) {
                *result = HTBOTTOMLEFT; return true;
            }
            if (x < winRect.right && x >= winRect.right-b &&
                y < winRect.bottom && y >= winRect.bottom-b) {
                *result = HTBOTTOMRIGHT; return true;
            }
            // 判断四边
            if (x >= winRect.left && x < winRect.left+b) {
                *result = HTLEFT; return true;
            }
            if (x < winRect.right && x >= winRect.right-b) {
                *result = HTRIGHT; return true;
            }
            if (y >= winRect.top && y < winRect.top+b) {
                *result = HTTOP; return true;
            }
            if (y < winRect.bottom && y >= winRect.bottom-b) {
                *result = HTBOTTOM; return true;
            }
        }
    }
    return QMainWindow::nativeEvent(eventType, message, result);
}
Table of Contents