Qt無邊框窗體-最大化時支持拖拽還原

原文連接:Qt無邊框窗體-最大化時支持拖拽還原less

1、概述

用Qt進行開發界面時,既想要實現友好的用戶交互又想界面漂亮,那麼自定義界面就必不可少。其中有一個操做就是是咱們每個Qter開發者都要會的,並且是常常進行的。函數

Qt::FramelessWindowHint這個屬性想必你們都使用過,有些同窗可能對這個屬性很瞭解,也用的是爐火純青,今天咱們也來講說這個屬性。優化

關於這個無邊框屬性網上也有一些文章,有些談論的是bug,固然了這是針對不一樣os而言,也有些是跟其餘第三方庫混合使用時的問題。但是問題歸問題,想要實現自定義的優秀界面這個屬性也是必不可少的。ui

今天咱們就來實現一個無邊框窗體最大化時,支持拖拽標題欄進行還原的功能。this

無邊框窗體支持縮放、移動這些不屬於本篇文章的內容,本篇文章主要講解怎麼實現最大化時拖拽標題欄進行還原窗體,本篇文章的代碼依賴於博主以前封裝的一個拖拽代理類。spa

2、效果展現

如效果圖所示,作了一個簡單的事例,雙擊標題欄窗體最大化,這個時候若是進行標題欄拖拽,當鼠標按下並移動一段距離時窗體恢復normal狀態。.net

恢復normal狀態下的窗體仍然支持放大和縮小,有接口能夠設置。設計

3、demo製做

demo的製做過程仍是比較簡單的,分爲以下幾步代理

一、設計窗體

經過desinger設計器咱們拖拽了一個大體窗體內容,爲了更好的展現效果,標題欄加上了icon和背景色

二、雙擊放大

鼠標雙擊標題欄放大這個功能實現起來方法也比較多,這裏博主選擇了代碼量最少而且實現起來最簡單的方式,直接把標題欄的事件循環安裝到了主窗體上。

ui.widget->installEventFilter(this);

接下來咱們就須要重寫主窗口的eventFilter函數便可

bool DragWidget::eventFilter(QObject * watched, QEvent * event)
{
    if (watched == ui.widget)
    {
        if (event->type() == QEvent::MouseButtonDblClick)
        {
            if (isMaximized())
            {
                showNormal();
                m_handler.setWidgetResizable(true);
                m_handler.setWidgetMovable(true);
            }
            else
            {
                showMaximized();
                m_handler.setWidgetResizable(false);
                m_handler.setWidgetMovable(false);
            }
        }
    }

    return QWidget::eventFilter(watched, event);
}

細心的同窗就會發現代碼裏有一個m_handler變量,這個類就是博主以前本身封裝的一個拖拽代理,經過接口能夠設置被代理的窗體,並設置須要代理哪些行爲。

本篇文章中所演示的事例代碼,咱們代理了主窗口上標題欄部分的移動事件和整個窗體的縮放事件,設置代碼以下所示

m_handler.activateOn(this);
m_handler.useLocalMoveabled(true);
m_handler.addLocalWidget(ui.widget);
m_handler.setMaximumMove(true, true);

拖拽代理類內容比較多,本篇文章暫不講解。

4、拖拽

爲了更好的理解本篇文章,這裏須要把拖拽代理類的頭文件放出來,這樣更有利於你們理解。

接口都比較簡單,代碼中也有註釋,你們自行閱讀。

class WidgetResizeHandler : public QObject
{

public:
    explicit WidgetResizeHandler(QObject* parent = 0);
    ~WidgetResizeHandler();

public:
    void activateOn(QWidget * topLevelWidget);//添加topLevelWidget事件代理
    void removeFrom(QWidget * topLevelWidget);//移除topLevelWidget事件代理

    Qt::CursorShape CursorShape(QWidget * widget);

    //窗口移動 default:true
    void setWidgetMovable(bool movable);
    bool isWidgetMovable();

    //大小可變 default:true
    void setWidgetResizable(bool resizable);
    bool isWidgetResizable();

    // 橡膠式窗口移動 default:false
    void useRubberBandOnMove(bool use);
    bool isUsingRubberBandOnMove();

    //橡膠式修改大小 default:false
    void useRubberBandOnResize(bool use);
    bool isUsingRubberBandOnResisze();

    void setBorderWidth(int newBorderWidth);
    int borderWidth();

    //局部可移動
    void useLocalMoveabled(bool use);
    void addLocalWidget(QWidget *);

    //最大化時支持拖拽 參數2表示是否可放大縮小
    void setMaximumMove(bool move, bool resize = false);

protected:
    virtual bool eventFilter(QObject * obj, QEvent * event) Q_DECL_OVERRIDE;

private:
    WidgetResizeHandlerImpl * d_ptr;
};

值得注意的是最後一個setMaximumMove接口,他就是咱們今天的豬腳-是否支持最大化時拖拽。當咱們設置了這個接口後,窗體最大化時也就能進行拖拽,並還原到以前的normal狀態。

文章第三小節講解demo時,說過主窗體已經被代理拖拽類進行了事件代理,那麼主窗體的全部事件首先都會傳遞給這個代理類,這裏咱們須要重點關注下鼠標按下時移動事件。

void WidgetData::handleMouseMoveEvent(QMouseEvent* event)
{
    if (mLeftButtonPressed)
    {
        if (d_ptr->mWidgetResizable && mPressedMousePos.onEdges)
        {
            resizeWidget(event->globalPos());
        }
        else if (d_ptr->mWidgetMovable)
        {
            moveWidget(event->globalPos());
        }
        else if (d_ptr->mMaxMovable)
        {
            if (mWidget->isMaximized() && TryMoveWidget(event))
            {
                d_ptr->mWidgetMovable = true;
                //d_ptr->mWidgetResizable = true;
            }
        }
    }
    else if (d_ptr->mWidgetResizable)
    {
        updateCursorShape(event->globalPos());
    }
}

這段代碼包含有其餘縮放窗體和正常移動的邏輯,最大化時支持移動的邏輯應該不難找木九十TryMoveWidget這個函數,該函數中咱們進行了充分的邏輯判斷,一旦觸發了窗體移動,那麼咱們把mWidgetMovable變量置爲true,下一次鼠標按下移動事件就會觸發正常的拖拽邏輯。

仔細思考上邊一段話,其中有2個關鍵信息

  1. 觸發窗體移動,並還原到以前的normal狀態
  2. 進行了第一步後,須要把mWidgetMovable變量置爲true,以後走正常的窗體移動流程

窗體移動

嘗試移動窗體,當鼠標當前位置距離鼠標按下時的距離大於20px時,進行窗體還原操做,並返回true,表明窗體已經被重置到normal態。

bool WidgetData::TryMoveWidget(QMouseEvent* event)
{
    QPoint distance = event->globalPos() - mDragPos;
    int length = distance.manhattanLength();
    if (length > 20)
    {
        QRect rect = mWidget->normalGeometry();
        int desX = mDragPos.x() * rect.width() / mWidget->geometry().width();
        int desY = mDragPos.y();
        rect.moveTopLeft(event->globalPos() - QPoint(desX, desY));

        mWidget->showNormal();
        mWidget->setGeometry(rect);

        mDragPos = QPoint(desX, desY);
        mIsMaxMove = true;

        return true;
    }

    return false;
}

上述代碼中的mIsMaxMove標識是爲了在一次窗體還原操做後,釋放鼠標時能夠正常的設置縮放標識而設。

有了上述代碼以後,窗體就能還原到最大化以前的大小,而且爲之也移動到了鼠標相應的位置,關於這個新位置的計算這裏須要說明下。

x座標

x軸座標使用了比例計算方式。窗體全屏時鼠標按下的位置在窗體上的位置在窗體還原後依然保持不變,這樣計算比較簡單並且不會出錯,保證窗體還原後,鼠標會一直在標題欄內。

若是須要優化x軸座標的計算方法,只須要從新計算上述代碼中的desX值便可。

y座標

y軸座標這裏沒有作特殊處理。由於窗體還原時,標題欄的高度是沒有發生變化的,所以這裏不須要作特殊處理。

講到這裏,本篇文章的主要內容基本完成,關於代理拖拽類,不屬於本篇文章內容,所以就不作過多解釋。

5、相關文章

值得一看的優秀文章:

  1. 財聯社-產品展現
  2. 廣聯達-產品展現
  3. Qt定製控件列表
  4. 牛逼哄哄的Qt庫





若是您以爲文章不錯,不妨給個 打賞,寫做不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!














很重要--轉載聲明

  1. 本站文章無特別說明,皆爲原創,版權全部,轉載時請用連接的方式,給出原文出處。同時寫上原做者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。

相關文章
相關標籤/搜索