关于KDDockWidget源码修改和自定义

前言

前面的文章介绍过KDDockWidget的基本使用及示例,文章在这里:
KDDockWidgets源码编译及安装
qml dockwidget窗口停靠
QML + KDDockWidget 实现 tabwidget效果( 窗口可独立浮动和缩放)

今天主要记录一些在KDDockWidget源码中的修改,修改源码的目的是为了根据自己的项目来实现相关风格,KDDockWidget是第三方开源代码, 不是Qt官方的,提供的可修改的接口不一定非常完善,而且部分功能还会有bug。总之,要想实现自有风格的样式,原有接口不满足的情况下,就得去改源码了。
本文主要是记录之前在使用KDDockWidget过程中的修改记录,不一定非常完善,但是大致思路是没问题的。

首先来看看KDDockWidget提供的原有风格样式,自带的示例:

源码修改

1.编译QtQuick模式

KDDockWidget源码是支持QWidget和QtQuick两种实现方式,由于QWidget体系Qt官方提供了QDockWidget可以使用,而QtQuick却没有,所以用KDDockWidget更多是看中它能支持QML下的dock实现,那么,我们在编译KDDockWidget时需要修改配置才能编译出QtQuick使用的动态库。

如果要使用Quick ,Qt需要5.15版本以上,而且编译时默认关闭了编译QtQuick,所以如果需要编译运行Quick示例的话,需要修改编译文件,在根目录下找到CMakeLists.txt文件并打开,然后找到

option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)

将OFF改成ON即可

option(${PROJECT_NAME}_QTQUICK "Build for QtQuick instead of QtWidgets" ON)

接下来重新编译就可以生成QtQuick使用的动态库,并且bin目录下会编译生成quick的示例exe
关于KDDockWidget源码修改和自定义_第1张图片

2.自定义标题栏和窗口

在源码目录下提供的示例中,有一个叫customtitlebar,就是介绍如何实现自定义工具栏样式,
customtitlebar 中部分代码

class CustomFrameworkWidgetFactory : public KDDockWidgets::DefaultWidgetFactory
{
public:

    ~CustomFrameworkWidgetFactory() override;

    QUrl titleBarFilename() const override
    {
        return QUrl("qrc:/MyTitleBar.qml");
    }
};

通过以上方式可以设置自定义的工具栏。KDDockWidgets::DefaultWidgetFactory类中提供了四个可以自定义的样式,包括标题栏、frame窗口、dockwidget、浮动窗口
关于KDDockWidget源码修改和自定义_第2张图片
所以我们可以根据以上示例,自定义其他几个窗口样式:
关于KDDockWidget源码修改和自定义_第3张图片
然后通过设置config应用

auto flags = KDDockWidgets::Config::self().flags();
auto &config = KDDockWidgets::Config::self();
config.setFlags(flags);
config.setFrameworkWidgetFactory(new CustomFrameworkWidgetFactory());

而这四个qml自定义可以参考源码目录下原有实现:
关于KDDockWidget源码修改和自定义_第4张图片
也就是说,这四个我们可以在自己项目单独实现然后来实现自定义,这样会比较灵活一些,当然也可以直接在源码中修改这几个文件来达到想要的效果。
文件说明:

  • DockWidget.qml 是对应每一个可移动的子窗口
  • Frame.qml 是对应dock窗口的上层外窗口
  • FloatingWindow.qml 是对应浮动窗口
  • TitleBar.qml 是对应标题栏

3.修改导航图标

当拖动一个dock窗口时,会在出现一个导航图标,指示需要将窗口拖动到哪个位置去,如下图:
关于KDDockWidget源码修改和自定义_第5张图片
图标目录在src/img/classic_indicators/下
关于KDDockWidget源码修改和自定义_第6张图片
可以直接替换成新的图标,重新编译源码即可。
若要修改导航图标显示逻辑,可以在src\private\quick\qml\ClassicIndicatorsOverlay.qml中修改

4.修改dock之间的分隔线样式

修改dock之间的分隔线,文件在 src\private\multisplitter\qml\Separator.qml, 比如修改分隔线颜色:

import QtQuick 2.6

Rectangle {
    id: root
    anchors.fill: parent
    color: "#171717"

    readonly property QtObject kddwSeparator: parent

    MouseArea {
        cursorShape: kddwSeparator ? (kddwSeparator.isVertical ? Qt.SizeVerCursor : Qt.SizeHorCursor)
                                   : Qt.SizeHorCursor
        anchors.fill: parent
        onPressed: {
            kddwSeparator.onMousePressed();
        }

        onReleased: {
            kddwSeparator.onMouseReleased();
        }

        onPositionChanged: (mouse) => {
            kddwSeparator.onMouseMoved(Qt.point(mouse.x, mouse.y));
        }

        onDoubleClicked: {
            kddwSeparator.onMouseDoubleClicked();
        }
    }
}

5.修改分隔符宽度和dock最小最大尺寸

文件位置:src\private\multisplitter\Item.cpp

int Layouting::Item::separatorThickness = 5;
// There are the defaults. They can be changed by the user via Config.h API.
QSize Layouting::Item::hardcodedMinimumSize = QSize(80, 90);
QSize Layouting::Item::hardcodedMaximumSize = QSize(16777215, 16777215);

以上是默认值,可以对应修改。dock最小和最大尺寸是对应窗口能拖动最小和最大的尺寸。

6.在标题栏上添加按钮

标题栏上默认有floating和close按钮,如果想再添加一个,关键步骤是要将按钮点击事件传出来让好在外层中响应,可以按照以下流程操作。
首先,如果我们有自定义标题栏,如上面第二点提到的,重新实现了标题栏,那就在自定义的标题栏qml中添加按钮,如果没有自定义,那就在源码中进行修改,找到 src\private\quick\qml\TitleBar.qml, 可以看到这里面有floating和close按钮的定义,直接在这里添加即可:

 TitleBarButton {
     id: floatButton
     visible: root.floatButtonVisible
     imageSource: "qrc:/img/dock-float.png"
     anchors {
         verticalCenter: parent ? parent.verticalCenter : undefined
         right: closeButton.left
         topMargin: 5
         bottomMargin: 5
         rightMargin: 2
     }
     onClicked: {
         root.floatButtonClicked();
     }
 }

 TitleBarButton {
     id: closeButton
     enabled: root.closeButtonEnabled
     imageSource: "qrc:/img/close.png"
     visible:true
     anchors {
         verticalCenter: parent ? parent.verticalCenter : undefined
         right: parent ? parent.right : undefined
         topMargin: 5
         bottomMargin: 5
         leftMargin: 5
         rightMargin: 2
     }
     onClicked: {
         root.closeButtonClicked();
     }
 }

加入要添加一个菜单按钮:

TitleBarButton {
     id: menuButton
     imageSource: "qrc:/img/dock-menu.png"
     anchors {
         verticalCenter: parent ? parent.verticalCenter : undefined
         right: floatButton.left
         topMargin: 5
         bottomMargin: 5
         rightMargin: 2
     }
     onClicked: {
         root.sigMenuButtonClicked();
     }
 }

这里点击按钮发送信号sigMenuButtonClicked(),我们需要在 TitleBarBase.qml 中定义该信号
关于KDDockWidget源码修改和自定义_第7张图片

然后在本文件内添加信号连接,并调用cpp中的接口

onSigMenuButtonClicked:{
    titleBarCpp.onMenuClicked();
}

接着在TitleBar_p.h中定义onMenuClicked()接口:
关于KDDockWidget源码修改和自定义_第8张图片
TitleBar.cpp中添加接口实现

void TitleBar::onMenuClicked()
{
    if(m_frame){
        if (DockWidgetBase *dw = m_frame->currentDockWidget()) {
            qDebug() << __FUNCTION__ << "dock dw->sigMenuClicked()";
            Q_EMIT dw->sigMenuClicked();
        }
    }
    else if(m_floatingWindow){
        if (Frame *f = m_floatingWindow->singleFrame()) {
            if (DockWidgetBase *dw = f->currentDockWidget()) {
                qDebug() << __FUNCTION__ << "floating dw->sigMenuClicked()";
                Q_EMIT dw->sigMenuClicked();
            }
            else{
                qWarning() << Q_FUNC_INFO << "Frame with no dock widgets";
            }
        }
        else{
            qWarning() << Q_FUNC_INFO << "m_floatingWindow singleFrame() is null";
        }
    }
}

这里 dw->sigMenuClicked() 继续向上层传递信号,所以需要在 DockWidgetBase.h中定义这个信号:

在这里插入图片描述

最后,在DockWidgetInstantiator_p.h中也定义信号,然后在DockWidgetInstantiator.cpp中将DockWidgetBase发出的信号关联起来:

关于KDDockWidget源码修改和自定义_第9张图片
在这里插入图片描述

connect(m_dockWidget, &DockWidgetQuick::sigMenuClicked, this,
            &DockWidgetInstantiator::sigMenuClicked);

ok,这样就可以在qml中使用DockWidget的时候直接关联菜单按钮点击事件了,示例如下:

KDDW.MainWindowLayout {
	id: dockWidgetArea
    anchors.fill: parent
    uniqueName: "MyMainLayout"
    KDDW.DockWidget {
        id: dock3
        uniqueName: "dock3" // Each dock widget needs a unique id
        Rectangle{
            color: "yellow"
        }
        onSigMenuClicked:{
            console.log("---------onSigMenuClicked-------------")
        }
    }
  Component.onCompleted: {
      console.log("--------MainWindowLayout onCompleted------")
        addDockWidget(dock3, KDDW.KDDockWidgets.Location_OnTop);
  } 
}

7.修改dock被均分宽度的问题

如果当前MainWindowLayout 中有两个dock窗口,浮动一个窗口后恢复添加到MainWindowLayout 中去,会发现原本两个dock宽度不同的窗口会被均分宽度,这个在实际项目中 每个窗口都有对应的显示宽度,如果直接被layout均分显示,可能会倒是界面显示异常,所以要修改不让在恢复时均分宽度的问题。
修改也很简单,找到src\private\multisplitter\Item_p.h 找到以下两个接口,修改其默认参数:

Item类中:

virtual void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Percentage);

改为

virtual void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Side2SeparatorMove);

ItemContainer类继承于Item,所以对应的虚函数也要修改

virtual void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Percentage);

改为

virtual void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Side2SeparatorMove);

还有:

void growItem(Item *, int amount, GrowthStrategy,
                  NeighbourSqueezeStrategy neighbourSqueezeStrategy,
                  bool accountForNewSeparator = false,
                  ChildrenResizeStrategy = ChildrenResizeStrategy::Percentage);
                  
void applyGeometries(const SizingInfo::List &sizes, ChildrenResizeStrategy = ChildrenResizeStrategy::Percentage);

这两个的默认参数都要修改

void growItem(Item *, int amount, GrowthStrategy,
                  NeighbourSqueezeStrategy neighbourSqueezeStrategy,
                  bool accountForNewSeparator = false,
                  ChildrenResizeStrategy = ChildrenResizeStrategy::Side2SeparatorMove);
                  
void applyGeometries(const SizingInfo::List &sizes, ChildrenResizeStrategy = ChildrenResizeStrategy::Side2SeparatorMove);

ok,接下来重新编辑动态库即可。

当前暂时涉及到以上部分的修改,后续若有新增会及时更新。

你可能感兴趣的:(KDDockWidget,Qt,qt,KDDockWidget源码,源码修改)