First Commit

This commit is contained in:
2025-11-18 14:18:26 -07:00
parent 33eb6e3707
commit 27277ec342
6106 changed files with 3571167 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Sergio Martins <sergio.martins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
cmake_minimum_required(VERSION 3.15)
project(qtwidgets_dockwidgets)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT TARGET kddockwidgets)
# For the purpose of our example, we're looking for Qt5 or Qt6 KDDW.
# For your own purposes, just chose the one you need.
find_package(KDDockWidgets QUIET)
if(NOT KDDockWidgets_FOUND)
find_package(KDDockWidgets-qt6 REQUIRED)
endif()
endif()
set(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
add_executable(qtwidgets_dockwidgets main.cpp MyViewFactory.cpp MyMainWindow.cpp MyWidget.cpp ${RESOURCES_EXAMPLE_SRC})
target_link_libraries(qtwidgets_dockwidgets PRIVATE KDAB::kddockwidgets)

View File

@@ -0,0 +1,41 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#pragma once
#include <kddockwidgets/Config.h>
#include <QObject>
#include <QKeyEvent>
// This event filter is just for examplify how to use the KDDW API to show/hide
// the drag indicators when ctrl is pressed
class CtrlKeyEventFilter : public QObject
{
public:
using QObject::QObject;
~CtrlKeyEventFilter() override;
bool eventFilter(QObject *, QEvent *ev) override
{
if (ev->type() != QEvent::KeyPress && ev->type() != QEvent::KeyRelease)
return false;
auto me = static_cast<QKeyEvent *>(ev);
if (me->key() != Qt::Key_Control)
return false;
KDDockWidgets::Config::self().setDropIndicatorsInhibited(ev->type() == QEvent::KeyRelease);
return false;
}
};

View File

@@ -0,0 +1,265 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyMainWindow.h"
#include "MyWidget.h"
#include "CtrlKeyEventFilter.h"
#include <kddockwidgets/Config.h>
#include <kddockwidgets/LayoutSaver.h>
#include <kddockwidgets/core/DockWidget.h>
#include <kddockwidgets/core/MainWindow.h>
#include <kddockwidgets/core/Layout.h>
#include <QMenu>
#include <QMenuBar>
#include <QEvent>
#include <QDebug>
#include <QString>
#include <QTextEdit>
#include <QWindow>
#include <QPushButton>
#include <QVBoxLayout>
#include <QRandomGenerator>
#include <QApplication>
#include <utility>
// clazy:excludeall=qstring-allocations,ctor-missing-parent-argument,detaching-member
static MyWidget *newMyWidget()
{
const int randomNumber = QRandomGenerator::global()->bounded(0, 100) + 1;
if (randomNumber < 50) {
if (randomNumber < 33) {
return new MyWidget1();
} else {
return new MyWidget3();
}
} else {
return new MyWidget2();
}
}
MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
ExampleOptions exampleOptions,
const QString &affinityName, QWidget *parent)
: KDDockWidgets::QtWidgets::MainWindow(uniqueName, options, parent)
, m_exampleOptions(exampleOptions)
{
auto menubar = menuBar();
auto fileMenu = new QMenu(QStringLiteral("File"), this);
m_toggleMenu = new QMenu(QStringLiteral("Toggle"), this);
auto miscMenu = new QMenu(QStringLiteral("Misc"), this);
menubar->addMenu(fileMenu);
menubar->addMenu(m_toggleMenu);
menubar->addMenu(miscMenu);
QAction *newAction = fileMenu->addAction(QStringLiteral("New DockWidget"));
connect(newAction, &QAction::triggered, this, [] {
static int count = 0;
count++;
auto w = newMyWidget();
w->setGeometry(100, 100, 400, 400);
auto dock = new KDDockWidgets::QtWidgets::DockWidget(
QStringLiteral("new dock %1").arg(count));
dock->setWidget(w);
dock->resize(QSize(600, 600));
dock->open();
});
auto saveLayoutAction = fileMenu->addAction(QStringLiteral("Save Layout"));
connect(saveLayoutAction, &QAction::triggered, this, [] {
KDDockWidgets::LayoutSaver saver;
const bool result = saver.saveToFile(QStringLiteral("mylayout.json"));
qDebug() << "Saving layout to disk. Result=" << result;
});
auto restoreLayoutAction = fileMenu->addAction(QStringLiteral("Restore Layout"));
connect(restoreLayoutAction, &QAction::triggered, this, [this] {
KDDockWidgets::RestoreOptions options = KDDockWidgets::RestoreOption_None;
if (m_exampleOptions & ExampleOption::RestoreIsRelative)
options |= KDDockWidgets::RestoreOption_RelativeToMainWindow;
KDDockWidgets::LayoutSaver saver(options);
saver.restoreFromFile(QStringLiteral("mylayout.json"));
});
auto closeAllAction = fileMenu->addAction(QStringLiteral("Close All"));
connect(closeAllAction, &QAction::triggered, this, [this] {
for (auto dw : std::as_const(m_dockwidgets))
dw->close();
});
auto layoutEqually = fileMenu->addAction(QStringLiteral("Layout Equally"));
connect(layoutEqually, &QAction::triggered, this, [this] { this->layoutEqually(); });
auto quitAction = fileMenu->addAction(QStringLiteral("Quit"));
connect(quitAction, &QAction::triggered, qApp, &QApplication::quit);
QAction *toggleDropIndicatorSupport =
miscMenu->addAction(QStringLiteral("Toggle Drop Indicator Support"));
toggleDropIndicatorSupport->setCheckable(true);
toggleDropIndicatorSupport->setChecked(true);
connect(toggleDropIndicatorSupport, &QAction::toggled, this, [](bool checked) {
KDDockWidgets::Config::self().setDropIndicatorsInhibited(!checked);
});
// for debug purposes only:
QAction *dumpLayout =
miscMenu->addAction(QStringLiteral("Dump layout to terminal"));
connect(dumpLayout, &QAction::triggered, this, [this] {
mainWindow()->layout()->dumpLayout();
});
setAffinities({ affinityName });
createDockWidgets();
if (options & KDDockWidgets::MainWindowOption_HasCentralWidget) {
setPersistentCentralWidget(new MyWidget1());
}
if (m_exampleOptions & ExampleOption::CtrlKeyFiltersDropIndicators) {
/// Drop indicators will only be visible when ctrl is pressed
KDDockWidgets::Config::self().setDropIndicatorsInhibited(true);
qGuiApp->installEventFilter(new CtrlKeyEventFilter(this));
}
}
MyMainWindow::~MyMainWindow()
{
qDeleteAll(m_dockwidgets);
}
void MyMainWindow::createDockWidgets()
{
Q_ASSERT(m_dockwidgets.isEmpty());
const int numDockWidgets = (m_exampleOptions & ExampleOption::NonDockableDockWidget9) ? 10 : 9;
// Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget
// instances)
for (int i = 0; i < numDockWidgets; i++)
m_dockwidgets << newDockWidget();
// MainWindow::addDockWidget() attaches a dock widget to the main window:
addDockWidget(m_dockwidgets.at(0), KDDockWidgets::Location_OnTop);
// Here, for finer granularity we specify right of dockwidgets[0]:
addDockWidget(m_dockwidgets.at(1), KDDockWidgets::Location_OnRight, m_dockwidgets.at(0));
addDockWidget(m_dockwidgets.at(2), KDDockWidgets::Location_OnLeft);
addDockWidget(m_dockwidgets.at(3), KDDockWidgets::Location_OnBottom);
addDockWidget(m_dockwidgets.at(4), KDDockWidgets::Location_OnBottom);
// Tab two dock widgets together
m_dockwidgets[3]->addDockWidgetAsTab(m_dockwidgets.at(5));
// 6 is floating, as it wasn't added to the main window via MainWindow::addDockWidget().
// and we tab 7 with it.
m_dockwidgets[6]->addDockWidgetAsTab(m_dockwidgets.at(7));
// Floating windows also support nesting, here we add 8 to the bottom of the group
m_dockwidgets[6]->addDockWidgetToContainingWindow(m_dockwidgets.at(8),
KDDockWidgets::Location_OnBottom);
auto floatingWindow = m_dockwidgets.at(6)->rootView();
floatingWindow->move(100, 100);
}
KDDockWidgets::QtWidgets::DockWidget *MyMainWindow::newDockWidget()
{
static int count = 0;
// Passing options is optional, we just want to illustrate Option_NotClosable here
KDDockWidgets::DockWidgetOptions options = KDDockWidgets::DockWidgetOption_None;
KDDockWidgets::LayoutSaverOptions layoutSaverOptions = KDDockWidgets::LayoutSaverOption::None;
if (count == 0 && (m_exampleOptions & ExampleOption::DockWidget0IsNonClosable))
options |= KDDockWidgets::DockWidgetOption_NotClosable;
if (count == 9 && (m_exampleOptions & ExampleOption::NonDockableDockWidget9))
options |= KDDockWidgets::DockWidgetOption_NotDockable;
if ((count == 6 || count == 7 || count == 8) && (m_exampleOptions & ExampleOption::DockWidgets678DontCloseBeforeRestore))
layoutSaverOptions |= KDDockWidgets::LayoutSaverOption::Skip;
auto dock = new KDDockWidgets::QtWidgets::DockWidget(
QStringLiteral("DockWidget #%1").arg(count), options, layoutSaverOptions);
dock->setAffinities(affinities()); // optional, just to show the feature. Pass -mi to the
// example to see incompatible dock widgets
if (count == 1)
dock->setIcon(QIcon::fromTheme(QStringLiteral("mail-message")));
auto myWidget = newMyWidget();
if (count == 8) {
if (m_exampleOptions & ExampleOption::MaxSizeForDockWidget8) {
// Set a maximum size on dock #8
myWidget->setMaximumSize(200, 200);
auto button = new QPushButton("dump debug info", myWidget);
connect(button, &QPushButton::clicked, this, [myWidget] {
KDDockWidgets::Config::self().printDebug();
qDebug() << "Widget: " << myWidget->geometry() << myWidget->minimumSize() << myWidget->minimumSizeHint() << myWidget->maximumSize() << myWidget->sizeHint() << myWidget->window();
auto tlw = myWidget->window();
qDebug() << "TLW : " << tlw << tlw->geometry() << tlw->minimumSize() << tlw->minimumSizeHint() << tlw->maximumSize() << tlw->sizeHint();
auto window = tlw->windowHandle();
qDebug() << "Window : " << window << window->frameGeometry() << window->geometry() << window->minimumSize() << window->maximumSize() << window->frameGeometry() << window->flags();
});
}
}
if (count == 5 && (m_exampleOptions & ExampleOption::ProgrammaticDragEvent)) {
qDeleteAll(myWidget->children());
auto button = new QPushButton("Start Drag (by tab)", myWidget);
connect(button, &QPushButton::pressed, this, [dock] {
dock->asDockWidgetController()->startDragging(true);
});
auto button2 = new QPushButton("Start Drag (by titlebar)", myWidget);
connect(button2, &QPushButton::pressed, this, [dock] {
dock->asDockWidgetController()->startDragging(false);
});
auto vbox = new QVBoxLayout(myWidget);
vbox->addWidget(button);
vbox->addWidget(button2);
vbox->addStretch();
}
if (count == 0 && (m_exampleOptions & ExampleOption::Dock0BlocksCloseEvent))
myWidget->blockCloseEvent();
dock->setWidget(myWidget);
if (dock->options() & KDDockWidgets::DockWidgetOption_NotDockable) {
dock->setTitle(QStringLiteral("DockWidget #%1 (%2)").arg(count).arg("non dockable"));
} else {
dock->setTitle(QStringLiteral("DockWidget #%1").arg(count));
}
dock->resize(QSize(600, 600));
m_toggleMenu->addAction(dock->toggleAction());
dock->toggleAction()->setShortcut(QStringLiteral("ctrl+%1").arg(count));
count++;
return dock;
}
CtrlKeyEventFilter::~CtrlKeyEventFilter() = default;

View File

@@ -0,0 +1,47 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#pragma once
#include <kddockwidgets/DockWidget.h>
#include <kddockwidgets/MainWindow.h>
class MyMainWindow : public KDDockWidgets::QtWidgets::MainWindow
{
Q_OBJECT
public:
enum class ExampleOption {
DockWidget0IsNonClosable = 1,
NonDockableDockWidget9 = 2,
RestoreIsRelative = 4,
MaxSizeForDockWidget8 = 8,
DockWidgets678DontCloseBeforeRestore = 16,
Dock0BlocksCloseEvent = 32,
ProgrammaticDragEvent = 64,
CtrlKeyFiltersDropIndicators = 128,
};
Q_DECLARE_FLAGS(ExampleOptions, ExampleOption)
explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options,
ExampleOptions exampleOptions,
const QString &affinityName = {}, // Usually not needed. Just here to show
// the feature.
QWidget *parent = nullptr);
~MyMainWindow() override;
private:
void createDockWidgets();
KDDockWidgets::QtWidgets::DockWidget *newDockWidget();
QMenu *m_toggleMenu = nullptr;
const ExampleOptions m_exampleOptions;
QVector<KDDockWidgets::QtWidgets::DockWidget *> m_dockwidgets;
};

View File

@@ -0,0 +1,78 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef EXAMPLETITLEBAR_CSS_H
#define EXAMPLETITLEBAR_CSS_H
#pragma once
#include <kddockwidgets/qtwidgets/views/TitleBar.h>
#include <kddockwidgets/core/TitleBar.h>
/**
* @brief Shows how to implement a custom titlebar which uses "Qt StyleSheets".
*
* Derive from KDDockWidgets::QtWidgets::ViewFactory and override the createTitleBar() method.
*
* To try it out, modify examples/dockwidgets/MyViewFactory.cpp to return a MyTitleBar_CSS instance.
* Run the example with: ./bin/qtwidgets_dockwidgets -p
*
* WARNINGS:
* - Qt StyleSheets are not recommended for new applications. Often you are able to style 90% of
* the application but then hit a road block. QStyle is much more powerful and flexible.
* - The Qt maintainers have manifested intention to deprecated stylesheets.
* - Stylesheets are supported for built-in QWidgets (QPushButton, QComboBox, etc.), any widget
* that's not in Qt needs to be crafted by the user, that includes, for example, having to paint
* your background manually. KDDockWidget::QtWidgets::TitleBar does this for your
* convenience though.
* - Qt stylesheets don't react to property changes (known old bug in Qt), for example:
* QLineEdit[readOnly="true"] { color: gray }
* this won't trigger when readOnly changes to false, you need to set/unset. This is QTBUG-51236
* - KDDockWidget::QtWidgets::TitleBar::isFocused is a property, there for needs to
* workaround the above bug by unsetting the sheet and setting it again.
*/
class MyTitleBar_CSS : public KDDockWidgets::QtWidgets::TitleBar
{
public:
explicit MyTitleBar_CSS(KDDockWidgets::Core::TitleBar *controller,
KDDockWidgets::Core::View *parent = nullptr)
: KDDockWidgets::QtWidgets::TitleBar(controller, parent)
{
initStyleSheet();
connect(this, &KDDockWidgets::QtWidgets::TitleBar::isFocusedChanged, this, [this] {
// Workaround QTBUG-51236, this makes the [isFocused=true] syntax useful
setStyleSheet(QString());
initStyleSheet();
});
}
~MyTitleBar_CSS() override;
void initStyleSheet()
{
// Or use qApp->setStyleSheet(), as you prefer
setStyleSheet(QStringLiteral("KDDockWidgets--TitleBarWidget {"
"background: blue"
"}"
"KDDockWidgets--TitleBarWidget:hover {"
"background: red"
"}"
"KDDockWidgets--TitleBarWidget[isFocused=true] {"
"background: green"
"}"));
}
};
MyTitleBar_CSS::~MyTitleBar_CSS()
{
}
#endif

View File

@@ -0,0 +1,113 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyViewFactory.h"
#include "MyTitleBar_CSS.h"
#include <kddockwidgets/qtwidgets/views/TitleBar.h>
#include <kddockwidgets/qtwidgets/views/Separator.h>
#include <kddockwidgets/qtwidgets/views/Group.h>
#include <kddockwidgets/qtcommon/View.h>
#include <QApplication>
#include <QPainter>
// clazy:excludeall=missing-qobject-macro,ctor-missing-parent-argument
class MyTitleBar : public KDDockWidgets::QtWidgets::TitleBar
{
public:
explicit MyTitleBar(KDDockWidgets::Core::TitleBar *controller, KDDockWidgets::Core::View *parent = nullptr)
: KDDockWidgets::QtWidgets::TitleBar(controller, parent)
, m_controller(controller)
{
setFixedHeight(60);
}
~MyTitleBar() override;
void paintEvent(QPaintEvent *) override
{
QPainter p(this);
QPen pen(Qt::black);
const QColor focusedBackgroundColor = Qt::yellow;
const QColor backgroundColor = focusedBackgroundColor.darker(115);
QBrush brush(m_controller->isFocused() ? focusedBackgroundColor : backgroundColor);
pen.setWidth(4);
p.setPen(pen);
p.setBrush(brush);
p.drawRect(rect().adjusted(4, 4, -4, -4));
QFont f = qGuiApp->font();
f.setPixelSize(30);
f.setBold(true);
p.setFont(f);
p.drawText(QPoint(10, 40), m_controller->title());
}
// Not needed to override. Just here to illustrate setHideDisabledButtons()
void init() override
{
// For demo purposes, we're hiding the close button if it's disabled (non-closable dock widget)
// Affects dock #0 when running: ./bin/qtwidgets_dockwidgets -n -p
m_controller->setHideDisabledButtons(KDDockWidgets::TitleBarButtonType::Close);
// But if you do override init(), never forget to call the base method:
KDDockWidgets::QtWidgets::TitleBar::init();
}
private:
KDDockWidgets::Core::TitleBar *const m_controller;
};
MyTitleBar::~MyTitleBar() = default;
// Inheriting from SeparatorWidget instead of Separator as it handles moving and mouse cursor
// changing
class MySeparator : public KDDockWidgets::QtWidgets::Separator
{
public:
explicit MySeparator(KDDockWidgets::Core::Separator *controller, KDDockWidgets::Core::View *parent)
: KDDockWidgets::QtWidgets::Separator(controller, parent)
{
}
~MySeparator() override;
void paintEvent(QPaintEvent *) override
{
QPainter p(this);
p.fillRect(QWidget::rect(), Qt::cyan);
}
};
MySeparator::~MySeparator() = default;
KDDockWidgets::Core::View *
CustomWidgetFactory::createTitleBar(KDDockWidgets::Core::TitleBar *controller,
KDDockWidgets::Core::View *parent) const
{
// Feel free to return MyTitleBar_CSS here instead, but just for education purposes!
return new MyTitleBar(controller, parent);
}
KDDockWidgets::Core::View *
CustomWidgetFactory::createSeparator(KDDockWidgets::Core::Separator *controller,
KDDockWidgets::Core::View *parent) const
{
return new MySeparator(controller, parent);
}
KDDockWidgets::Core::View *CustomWidgetFactory::createGroup(KDDockWidgets::Core::Group *controller, KDDockWidgets::Core::View *parent) const
{
// If you want a different styling, just inherit from QtWidgets::Group() and return it here
return new KDDockWidgets::QtWidgets::Group(controller, KDDockWidgets::QtCommon::View_qt::asQWidget(parent));
}

View File

@@ -0,0 +1,27 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#pragma once
#include <kddockwidgets/qtwidgets/ViewFactory.h>
// clazy:excludeall=ctor-missing-parent-argument
class CustomWidgetFactory : public KDDockWidgets::QtWidgets::ViewFactory
{
Q_OBJECT
public:
KDDockWidgets::Core::View *createTitleBar(KDDockWidgets::Core::TitleBar *,
KDDockWidgets::Core::View *parent) const override;
KDDockWidgets::Core::View *createSeparator(KDDockWidgets::Core::Separator *,
KDDockWidgets::Core::View *parent = nullptr) const override;
KDDockWidgets::Core::View *createGroup(KDDockWidgets::Core::Group *, KDDockWidgets::Core::View *parent) const override;
};

View File

@@ -0,0 +1,133 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyWidget.h"
#include <QPainter>
#include <QDebug>
#include <QFile>
#include <QLineEdit>
#include <QCloseEvent>
static QHash<QString, QImage> s_images; /// clazy:exclude=non-pod-global-static
MyWidget::MyWidget(const QString &backgroundFile, const QString &logoFile, QWidget *parent)
: QWidget(parent)
{
if (!backgroundFile.isEmpty()) {
auto it = s_images.find(backgroundFile);
if (it == s_images.end())
it = s_images.insert(backgroundFile, QImage(backgroundFile));
m_background = it.value();
}
if (!logoFile.isEmpty()) {
auto it = s_images.find(logoFile);
if (it == s_images.end())
it = s_images.insert(logoFile, QImage(logoFile));
m_logo = it.value();
}
setFocusPolicy(Qt::StrongFocus);
if (qEnvironmentVariableIsSet("KDDW_DEBUG_FOCUS")) {
// Uncomment to show focus propagation working
new QLineEdit(this);
auto l2 = new QLineEdit(this);
l2->move(0, 100);
setFocusProxy(l2);
}
}
MyWidget::~MyWidget()
{
}
void MyWidget::drawLogo(QPainter &p)
{
if (m_logo.isNull())
return;
const qreal ratio = m_logo.height() / (m_logo.width() * 1.0);
const int maxWidth = int(0.80 * size().width());
const int maxHeight = int(0.80 * size().height());
const int proposedHeight = int(maxWidth * ratio);
const int width = proposedHeight <= maxHeight ? maxWidth : int(maxHeight / ratio);
const int height = int(width * ratio);
QRect targetLogoRect(0, 0, width, height);
targetLogoRect.moveCenter(rect().center() + QPoint(0, -int(size().height() * 0.00)));
p.drawImage(targetLogoRect, m_logo, m_logo.rect());
}
void MyWidget::blockCloseEvent()
{
m_blocksCloseEvent = true;
}
void MyWidget::closeEvent(QCloseEvent *ev)
{
if (m_blocksCloseEvent) {
ev->ignore();
} else {
QWidget::closeEvent(ev);
}
}
MyWidget1::MyWidget1(MyWidget::QWidget *parent)
: MyWidget(QStringLiteral(":/assets/triangles.png"),
QStringLiteral(":/assets/KDAB_bubble_white.png"), parent)
{
}
void MyWidget1::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), QColor(0xCC, 0xCC, 0xCC));
p.drawImage(m_background.rect(), m_background, m_background.rect());
drawLogo(p);
}
MyWidget2::MyWidget2(MyWidget::QWidget *parent)
: MyWidget(QString(), QStringLiteral(":/assets/KDAB_bubble_blue.png"), parent)
{
}
void MyWidget2::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), Qt::white);
drawLogo(p);
}
MyWidget3::MyWidget3(MyWidget::QWidget *parent)
: MyWidget(QStringLiteral(":/assets/base.png"),
QStringLiteral(":/assets/KDAB_bubble_fulcolor.png"), parent)
, m_triangle(QImage(QStringLiteral(":/assets/tri.png")))
{
}
void MyWidget3::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.fillRect(rect(), QColor(0xD5, 0xD5, 0xD5));
p.drawImage(m_background.rect(), m_background, m_background.rect());
const QRect targetRect =
QRect({ width() - m_triangle.width(), height() - m_triangle.height() }, m_triangle.size());
p.drawImage(targetRect, m_triangle, m_triangle.rect());
drawLogo(p);
}

View File

@@ -0,0 +1,74 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef EXAMPLEDOCKABLEWIDGET_H
#define EXAMPLEDOCKABLEWIDGET_H
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
class QPainter;
QT_END_NAMESPACE
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(const QString &backgroundFile, const QString &logoFile,
QWidget *parent = nullptr);
~MyWidget();
// These two are just for demonstrating how to block the close event, if desired
void blockCloseEvent();
void closeEvent(QCloseEvent *) override;
protected:
void drawLogo(QPainter &);
QImage m_background;
QImage m_logo;
bool m_blocksCloseEvent = false;
};
class MyWidget1 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget1(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *) override;
};
class MyWidget2 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget2(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *) override;
};
class MyWidget3 : public MyWidget
{
Q_OBJECT
public:
explicit MyWidget3(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *) override;
QImage m_triangle;
};
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@@ -0,0 +1,510 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "MyWidget.h"
#include "MyMainWindow.h"
#include "MyViewFactory.h"
#include "CtrlKeyEventFilter.h"
#include <kddockwidgets/Config.h>
#include <kddockwidgets/qtwidgets/ViewFactory.h>
#include <kddockwidgets/core/DockWidget.h>
#include <kddockwidgets/core/Draggable_p.h>
#include <QStyleFactory>
#include <QApplication>
#include <QDebug>
#include <QCommandLineParser>
#include <algorithm>
// clazy:excludeall=qstring-allocations
using namespace KDDockWidgets;
int main(int argc, char **argv)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
QApplication app(argc, argv);
app.setOrganizationName(QStringLiteral("KDAB"));
app.setApplicationName(QStringLiteral("Test app"));
KDDockWidgets::initFrontend(KDDockWidgets::FrontendType::QtWidgets);
QCommandLineParser parser;
parser.setApplicationDescription("KDDockWidgets example application");
parser.addHelpOption();
// Fusion looks better in general, but feel free to change
qApp->setStyle(QStyleFactory::create(QStringLiteral("Fusion")));
QCommandLineOption customStyle(
"p",
QCoreApplication::translate("main",
"Shows how to style framework internals via ViewFactory"));
parser.addOption(customStyle);
QCommandLineOption reorderTabsOption(
"r", QCoreApplication::translate("main", "Support re-ordering tabs with mouse"));
parser.addOption(reorderTabsOption);
QCommandLineOption noTitleBars(
"t", QCoreApplication::translate("main", "Hide titlebars when tabs are visible"));
parser.addOption(noTitleBars);
QCommandLineOption alwaysTitleBarWhenFloating(
"q",
QCoreApplication::translate("main",
"Don't hide title bars if floating, even if "
"Flag_HideTitleBarWhenTabsVisible is specified."));
parser.addOption(alwaysTitleBarWhenFloating);
QCommandLineOption alwaysTabs(
"z", QCoreApplication::translate("main", "Show tabs even if there's only one"));
parser.addOption(alwaysTabs);
QCommandLineOption lazyResizeOption("l",
QCoreApplication::translate("main", "Use lazy resize"));
parser.addOption(lazyResizeOption);
QCommandLineOption multipleMainWindows(
"m", QCoreApplication::translate("main", "Shows two multiple main windows"));
parser.addOption(multipleMainWindows);
QCommandLineOption incompatibleMainWindows(
"i",
QCoreApplication::translate(
"main",
"Only usable with -m. Make the two main windows incompatible with each other. "
"(Illustrates (MainWindowBase::setAffinityName))"));
parser.addOption(incompatibleMainWindows);
QCommandLineOption tabsHaveCloseButton(
"c", QCoreApplication::translate("main", "Tabs have a close button"));
parser.addOption(tabsHaveCloseButton);
QCommandLineOption nonClosableDockWidget(
"n", QCoreApplication::translate("main", "DockWidget #0 will be non-closable"));
parser.addOption(nonClosableDockWidget);
QCommandLineOption relativeRestore(
"s",
QCoreApplication::translate(
"main", "Don't restore main window geometry, restore dock widgets in relative sizes"));
parser.addOption(relativeRestore);
QCommandLineOption doubleClickMaximize(
"x",
QCoreApplication::translate("main",
"Double clicking a title bar will maximize a floating window"));
parser.addOption(doubleClickMaximize);
QCommandLineOption nonDockable(
"d", QCoreApplication::translate("main", "DockWidget #9 will be non-dockable"));
parser.addOption(nonDockable);
QCommandLineOption maximizeButtonOption(
"b",
QCoreApplication::translate(
"main",
"Floating dockWidgets have maximize/restore buttons instead of float/dock button"));
parser.addOption(maximizeButtonOption);
QCommandLineOption minimizeButtonOption(
"k",
QCoreApplication::translate("main",
"Floating dockWidgets have a minimize button. Implies not "
"being an utility window (~Qt::Tool)"));
parser.addOption(minimizeButtonOption);
QCommandLineOption segmentedIndicators(
"y", QCoreApplication::translate("main", "Use segmented indicators instead of classical"));
parser.addOption(segmentedIndicators);
QCommandLineOption noUtilityWindows(
"u",
QCoreApplication::translate(
"main", "FloatingWindows will be normal windows instead of utility windows"));
parser.addOption(noUtilityWindows);
QCommandLineOption keepAbove(
"o",
QCoreApplication::translate("main",
"FloatingWindows will have Qt::WindowStaysOnTopHint. Implies "
"not being an utility window (try it with -u too)"));
parser.addOption(keepAbove);
parser.addPositionalArgument(
"savedlayout",
QCoreApplication::translate("main", "loads the specified json file at startup"));
QCommandLineOption dockableMainWindows(
"j",
QCoreApplication::translate("main",
"Allow main windows to be docked inside other main windows"));
parser.addOption(dockableMainWindows);
QCommandLineOption maxSizeOption(
"g", QCoreApplication::translate("main", "Make dock #8 have a max-size of 200x200."));
parser.addOption(maxSizeOption);
QCommandLineOption centralFrame(
"f", QCoreApplication::translate("main", "Persistent central group"));
QCommandLineOption autoHideSupport(
"w",
QCoreApplication::translate("main", "Enables auto-hide/minimization to side-bar support"));
parser.addOption(autoHideSupport);
QCommandLineOption closeOnlyCurrentTab(
"close-only-current-tab",
QCoreApplication::translate(
"main",
"The title bar's close button will only close the current tab instead of all. "
"Illustrates using Config::Flag_CloseOnlyCurrentTab"));
parser.addOption(closeOnlyCurrentTab);
QCommandLineOption dontCloseBeforeRestore(
"dont-close-widget-before-restore", // krazy:exclude=spelling
QCoreApplication::translate("main",
"DockWidgets 6, 7 and 8 won't be closed before a restore. Illustrates "
"LayoutSaverOption::Skip"));
parser.addOption(dontCloseBeforeRestore);
QCommandLineOption blockCloseEvent(
"block-close-event",
QCoreApplication::translate("main", "DockWidget #0 will block close events"));
parser.addOption(blockCloseEvent);
QCommandLineOption programmaticDragEvent(
"programmatic-drag",
QCoreApplication::translate("main", "Shows how to start a drag programmatically (advanced usage)"));
parser.addOption(programmaticDragEvent);
QCommandLineOption showButtonsInTabBarIfTitleBarHidden(
"show-buttons-in-tabbar-if-titlebar-hidden",
QCoreApplication::translate("main",
"If we're not using title bars we'll still show the close and "
"float button in the tab bar"));
parser.addOption(showButtonsInTabBarIfTitleBarHidden);
QCommandLineOption centralWidget(
"central-widget",
QCoreApplication::translate("main",
"The main window will have a non-detachable central widget"));
parser.addOption(centralWidget);
QCommandLineOption tabsAtBottom("tabs-at-bottom", QCoreApplication::translate("main", "Shows tabs at bottom"));
parser.addOption(tabsAtBottom);
QCommandLineOption ctxtMenuOnTabs(
"allow-switch-tabs-via-menu",
QCoreApplication::translate("main", "Allow switching tabs via context menu in tabs area"));
parser.addOption(ctxtMenuOnTabs);
QCommandLineOption hideCertainDockingIndicators(
"hide-certain-docking-indicators",
QCoreApplication::translate("main",
"Illustrates usage of Config::setDropIndicatorAllowedFunc()"));
parser.addOption(hideCertainDockingIndicators);
QCommandLineOption ctrlTogglesDropIndicators(
"ctrl-toggles-drop-indicators",
QCoreApplication::translate("main", "Ctrl key toggles drop indicators"));
parser.addOption(ctrlTogglesDropIndicators);
#if defined(DOCKS_DEVELOPER_MODE)
parser.addOption(centralFrame);
QCommandLineOption noQtTool(
"no-qttool", QCoreApplication::translate("main", "(internal) Don't use Qt::Tool"));
QCommandLineOption noParentForFloating(
"no-parent-for-floating",
QCoreApplication::translate("main", "(internal) FloatingWindows won't have a parent"));
QCommandLineOption nativeTitleBar(
"native-title-bar",
QCoreApplication::translate("main", "(internal) FloatingWindows a native title bar"));
QCommandLineOption noDropIndicators(
"no-drop-indicators",
QCoreApplication::translate("main", "(internal) Don't use any drop indicators"));
parser.addOption(noQtTool);
parser.addOption(noParentForFloating);
parser.addOption(nativeTitleBar);
parser.addOption(noDropIndicators);
#if defined(Q_OS_WIN)
QCommandLineOption noAeroSnap(
"no-aero-snap", QCoreApplication::translate("main", "(internal) Disable AeroSnap"));
parser.addOption(noAeroSnap);
#endif
#else
KDDW_UNUSED(centralFrame)
#endif
parser.process(app);
if (parser.isSet(customStyle)) {
Config::self().setViewFactory(new CustomWidgetFactory()); // Sets our custom factory
// Increase the separator size, just for demo
Config::self().setSeparatorThickness(10);
}
if (parser.isSet(segmentedIndicators))
KDDockWidgets::Core::ViewFactory::s_dropIndicatorType =
KDDockWidgets::DropIndicatorType::Segmented;
MainWindowOptions options = MainWindowOption_None;
auto flags = KDDockWidgets::Config::self().flags();
#if defined(DOCKS_DEVELOPER_MODE)
auto internalFlags = KDDockWidgets::Config::self().internalFlags();
options = parser.isSet(centralFrame) ? MainWindowOption_HasCentralGroup : MainWindowOption_None;
if (parser.isSet(centralWidget))
options |= MainWindowOption_HasCentralWidget;
if (parser.isSet(noQtTool))
internalFlags |= KDDockWidgets::Config::InternalFlag_DontUseQtToolWindowsForFloatingWindows;
if (parser.isSet(keepAbove))
flags |= KDDockWidgets::Config::Flag_KeepAboveIfNotUtilityWindow;
if (parser.isSet(noParentForFloating))
internalFlags |= KDDockWidgets::Config::InternalFlag_DontUseParentForFloatingWindows;
if (parser.isSet(nativeTitleBar))
flags |= KDDockWidgets::Config::Flag_NativeTitleBar;
if (parser.isSet(noDropIndicators))
KDDockWidgets::Core::ViewFactory::s_dropIndicatorType = KDDockWidgets::DropIndicatorType::None;
#if defined(Q_OS_WIN)
if (parser.isSet(noAeroSnap))
internalFlags |= KDDockWidgets::Config::InternalFlag_NoAeroSnap;
#endif
Config::self().setInternalFlags(internalFlags);
#endif
if (parser.isSet(autoHideSupport))
flags |= Config::Flag_AutoHideSupport;
if (parser.isSet(closeOnlyCurrentTab))
flags |= Config::Flag_CloseOnlyCurrentTab;
if (parser.isSet(showButtonsInTabBarIfTitleBarHidden))
flags |= Config::Flag_ShowButtonsOnTabBarIfTitleBarHidden;
if (parser.isSet(noTitleBars))
flags |= KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible;
if (parser.isSet(noUtilityWindows))
flags |= KDDockWidgets::Config::Flag_DontUseUtilityFloatingWindows;
if (parser.isSet(alwaysTabs))
flags |= KDDockWidgets::Config::Flag_AlwaysShowTabs;
if (parser.isSet(alwaysTitleBarWhenFloating)) {
flags |= KDDockWidgets::Config::Flag_AlwaysTitleBarWhenFloating;
if (!(flags & KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible)) {
qWarning() << "Flag_AlwaysTitleBarWhenFloating is unneeded if "
"Flag_HideTitleBarWhenTabsVisible isn't used."
<< "As floating windows already have title bars by default.";
}
}
if (parser.isSet(customStyle) || qEnvironmentVariableIsSet("KDDW_DEBUG_FOCUS"))
flags |= KDDockWidgets::Config::Flag_TitleBarIsFocusable; // also showing title bar focus
// with -p, just to not introduce
// another switch
if (parser.isSet(reorderTabsOption))
flags |= KDDockWidgets::Config::Flag_AllowReorderTabs;
if (parser.isSet(maximizeButtonOption))
flags |= KDDockWidgets::Config::Flag_TitleBarHasMaximizeButton;
if (parser.isSet(minimizeButtonOption))
flags |= KDDockWidgets::Config::Flag_TitleBarHasMinimizeButton;
if (parser.isSet(lazyResizeOption))
flags |= KDDockWidgets::Config::Flag_LazyResize;
if (parser.isSet(tabsHaveCloseButton))
flags |= KDDockWidgets::Config::Flag_TabsHaveCloseButton;
if (parser.isSet(ctxtMenuOnTabs))
flags |= KDDockWidgets::Config::Flag_AllowSwitchingTabsViaMenu;
if (parser.isSet(doubleClickMaximize))
flags |= KDDockWidgets::Config::Flag_DoubleClickMaximizes;
if (parser.isSet(incompatibleMainWindows) && !parser.isSet(multipleMainWindows)) {
qWarning() << "Error: Argument -i requires -m";
return 1;
}
if (parser.isSet(programmaticDragEvent) && parser.isSet(ctrlTogglesDropIndicators)) {
qWarning() << "Error: Arguments --programmatic-drag and --ctrl-toggles-drop-indicators are "
"mutually exclusive. The latter is already included in the former.";
return 1;
}
if (parser.isSet(hideCertainDockingIndicators)) {
// Here we exemplify adding a restriction to "Dock Widget 8"
// Dock widget 8 will only be allowed to dock to the outer areas
auto func = [](KDDockWidgets::DropLocation location,
const KDDockWidgets::Core::DockWidget::List &source,
const KDDockWidgets::Core::DockWidget::List &target,
Core::DropArea *) {
KDDW_UNUSED(target); // When dragging into a tab, 'target' would have the list of already
// tabbed dock widgets
const bool isDraggingDW8 =
std::find_if(source.cbegin(), source.cend(),
[](KDDockWidgets::Core::DockWidget *dw) {
return dw->uniqueName() == QLatin1String("DockWidget #8");
})
!= source.cend();
return (location & KDDockWidgets::DropLocation_Outter) || !isDraggingDW8;
};
KDDockWidgets::Config::self().setDropIndicatorAllowedFunc(func);
}
KDDockWidgets::Config::self().setTabsAtBottom(parser.isSet(tabsAtBottom));
KDDockWidgets::Config::self().setFlags(flags);
MyMainWindow::ExampleOptions exampleOptions = {};
if (parser.isSet(ctrlTogglesDropIndicators))
exampleOptions |= MyMainWindow::ExampleOption::CtrlKeyFiltersDropIndicators;
if (parser.isSet(nonClosableDockWidget))
exampleOptions |= MyMainWindow::ExampleOption::DockWidget0IsNonClosable;
if (parser.isSet(relativeRestore))
exampleOptions |= MyMainWindow::ExampleOption::RestoreIsRelative;
if (parser.isSet(nonDockable))
exampleOptions |= MyMainWindow::ExampleOption::NonDockableDockWidget9;
if (parser.isSet(maxSizeOption))
exampleOptions |= MyMainWindow::ExampleOption::MaxSizeForDockWidget8;
if (parser.isSet(dontCloseBeforeRestore))
exampleOptions |= MyMainWindow::ExampleOption::DockWidgets678DontCloseBeforeRestore;
if (parser.isSet(blockCloseEvent))
exampleOptions |= MyMainWindow::ExampleOption::Dock0BlocksCloseEvent;
if (parser.isSet(programmaticDragEvent)) {
exampleOptions |= MyMainWindow::ExampleOption::ProgrammaticDragEvent;
static auto s_ctrlKeyEventFilter = new CtrlKeyEventFilter(qApp);
/// Here we demonstrate how to use setDragAboutToStartFunc()/setDragEndedFunc() to prevent a drag from happening
/// In this made up example:
/// 1. If ctrl is pressed, we always allow the drag
/// 2. If the drag is programmatic (DockWidget::startDrag()), we always allow the drag as well
/// 3. If the dragged widget is already a window, we allow a drag to move it, but not dock it
/// 3.1. We also install an event filter to show the drop indicators when ctrl is pressed
KDDockWidgets::Config::self().setDragAboutToStartFunc([](Core::Draggable *draggable) -> bool {
const bool ctrlIsPressed = qGuiApp->keyboardModifiers() & Qt::ControlModifier;
if (ctrlIsPressed || draggable->isInProgrammaticDrag()) {
KDDockWidgets::Config::self().setDropIndicatorsInhibited(false);
return true;
}
if (draggable->isWindow()) {
qGuiApp->installEventFilter(s_ctrlKeyEventFilter);
// Ctrl might already be pressed before the DnD even starts, so honour that as well
KDDockWidgets::Config::self().setDropIndicatorsInhibited(!ctrlIsPressed);
return true;
}
return false;
});
KDDockWidgets::Config::self().setDragEndedFunc([]() {
// cleanup
qGuiApp->removeEventFilter(s_ctrlKeyEventFilter);
});
}
const bool usesMainWindowsWithAffinity = parser.isSet(multipleMainWindows);
const bool usesDockableMainWindows = parser.isSet(dockableMainWindows);
MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, exampleOptions);
mainWindow.setWindowTitle("Main Window 1");
mainWindow.resize(1200, 1200);
mainWindow.show();
if (usesMainWindowsWithAffinity) {
if (usesDockableMainWindows) {
qWarning() << "MainWindows with affinity option is incompatible with Dockable Main "
"Windows option";
return 1;
}
// By default a dock widget can dock into any main window.
// By setting an affinity name we can prevent that. Dock widgets of different affinities are
// incompatible.
const QString affinity =
parser.isSet(incompatibleMainWindows) ? QStringLiteral("affinity1") : QString();
auto mainWindow2 =
new MyMainWindow(QStringLiteral("MyMainWindow-2"), options, exampleOptions, affinity);
if (affinity.isEmpty())
mainWindow2->setWindowTitle("Main Window 2");
else
mainWindow2->setWindowTitle("Main Window 2 (different affinity)");
mainWindow2->resize(1200, 1200);
mainWindow2->show();
} else if (usesDockableMainWindows) {
auto mainWindowDockWidget =
new KDDockWidgets::QtWidgets::DockWidget(QStringLiteral("MyMainWindow-2-DW"));
const QString affinity = QStringLiteral("Inner-DockWidgets-2");
MyMainWindow::ExampleOptions exampleOptions2 = {};
if (parser.isSet(relativeRestore))
exampleOptions2 |= MyMainWindow::ExampleOption::RestoreIsRelative;
auto dockableMainWindow =
new MyMainWindow(QStringLiteral("MyMainWindow-2"), options, exampleOptions2, affinity);
dockableMainWindow->setAffinities({ affinity });
dockableMainWindow->setStyleSheet(QStringLiteral("background: yellow"));
dockableMainWindow->setWindowTitle("Dockable Main Window");
dockableMainWindow->show();
mainWindowDockWidget->setWidget(dockableMainWindow);
mainWindowDockWidget->open();
mainWindowDockWidget->resize(QSize(800, 800));
}
const QStringList args = parser.positionalArguments();
if (!args.isEmpty()) {
const QString sourceJsonFileName = args[0];
KDDockWidgets::LayoutSaver loader;
if (!loader.restoreFromFile(sourceJsonFileName)) {
qWarning() << Q_FUNC_INFO << "Failed to restore from" << sourceJsonFileName;
return 1;
}
}
return app.exec();
}

View File

@@ -0,0 +1,10 @@
<RCC>
<qresource prefix="/">
<file>assets/base.png</file>
<file>assets/KDAB_bubble_blue.png</file>
<file>assets/KDAB_bubble_fulcolor.png</file>
<file>assets/KDAB_bubble_white.png</file>
<file>assets/triangles.png</file>
<file>assets/tri.png</file>
</qresource>
</RCC>