Files
KDDockWidgets/src/controllers/Stack.cpp
2022-04-02 15:34:38 +01:00

273 lines
7.3 KiB
C++

/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020-2022 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 "Stack.h"
#include "Config.h"
#include "views_qtwidgets/Stack_qtwidgets.h"
#include "kddockwidgets/FrameworkWidgetFactory.h"
#include "private/Logging_p.h"
#include "private/Utils_p.h"
#include "private/WindowBeingDragged_p.h"
#include "DockWidget_p.h"
#include "controllers/TabBar.h"
#include "controllers/Frame.h"
#include "controllers/FloatingWindow.h"
#include <QDebug>
#include <QTabBar> // TODO Remove
#include <qobject.h>
using namespace KDDockWidgets;
using namespace KDDockWidgets::Controllers;
Stack::Stack(Frame *frame, TabWidgetOptions options)
: Controller(Type::Frame, Config::self().frameworkWidgetFactory()->createTabWidget(this, frame))
, Draggable(view(), Config::self().flags() & (Config::Flag_HideTitleBarWhenTabsVisible | Config::Flag_AlwaysShowTabs))
, m_tabBar(new TabBar(this))
, m_frame(frame)
, m_options(options)
{
view()->init();
}
Stack::~Stack()
{
delete m_tabBar;
}
TabWidgetOptions Stack::options() const
{
return m_options;
}
void Stack::setCurrentDockWidget(DockWidget *dw)
{
setCurrentDockWidget(indexOfDockWidget(dw));
}
bool Stack::isPositionDraggable(QPoint p) const // TODO: Move to view
{
auto view = qobject_cast<Views::Stack_qtwidgets *>(this->view()->asQWidget()); // TODO
if (view->tabPosition() != QTabWidget::North) { // TODO
qWarning() << Q_FUNC_INFO << "Not implemented yet. Only North is supported";
return false;
}
return p.y() >= 0 && p.y() <= view->tabBar()->height();
}
DockWidgetBase *Stack::currentDockWidget() const
{
return dockwidgetAt(currentIndex());
}
void Stack::addDockWidget(DockWidget *dock)
{
insertDockWidget(dock, numDockWidgets());
}
bool Stack::insertDockWidget(DockWidget *dock, int index)
{
Q_ASSERT(dock);
qCDebug(addwidget) << Q_FUNC_INFO << dock << "; count before=" << numDockWidgets();
if (index < 0)
index = 0;
if (index > numDockWidgets())
index = numDockWidgets();
if (contains(dock)) {
qWarning() << Q_FUNC_INFO << "Refusing to add already existing widget";
return false;
}
QPointer<Frame> oldFrame = dock->d->frame();
insertDockWidget(index, dock, dock->icon(DockWidget::IconPlace::TabBar), dock->title());
setCurrentDockWidget(index);
if (oldFrame && oldFrame->beingDeletedLater()) {
// give it a push and delete it immediately.
// Having too many deleteLater() puts us in an inconsistent state. For example if LayoutSaver::saveState()
// would to be called while the Frame hadn't been deleted yet it would count with that frame unless hacks.
// Also the unit-tests are full of waitForDeleted() due to deleteLater.
// Ideally we would just remove the deleteLater from frame.cpp, but QStack::insertTab()
// would crash, as it accesses the old tab-widget we're stealing from
delete oldFrame;
}
return true;
}
bool Stack::contains(DockWidget *dw) const
{
return indexOfDockWidget(dw) != -1;
}
Frame *Stack::frame() const
{
return m_frame;
}
std::unique_ptr<WindowBeingDragged> Stack::makeWindow()
{
// This is called when using Flag_HideTitleBarWhenTabsVisible
// For detaching individual tabs, TabBar::makeWindow() is called.
if (auto fw = view()->window()->asFloatingWindowController()) {
if (fw->hasSingleFrame()) {
// We're already in a floating window, and it only has 1 dock widget.
// So there's no detachment to be made, we just move the window.
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(fw, this));
}
}
QRect r = m_frame->view()->geometry();
const QPoint globalPoint = view()->mapToGlobal(QPoint(0, 0));
auto floatingWindow = new FloatingWindow(m_frame, {});
r.moveTopLeft(globalPoint);
floatingWindow->setSuggestedGeometry(r, SuggestedGeometryHint_GeometryIsFromDocked);
floatingWindow->view()->show();
return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(floatingWindow, this));
}
bool Stack::isWindow() const
{
if (auto fw = view()->window()->asFloatingWindowController()) {
// Case of dragging via the tab widget when the title bar is hidden
return fw->hasSingleFrame();
}
return false;
}
DockWidgetBase *Stack::singleDockWidget() const
{
if (m_frame->hasSingleDockWidget())
return m_frame->dockWidgets().first();
return nullptr;
}
bool Stack::isMDI() const
{
return m_frame && m_frame->isMDI();
}
void Stack::onTabInserted()
{
m_frame->onDockWidgetCountChanged();
}
void Stack::onTabRemoved()
{
m_frame->onDockWidgetCountChanged();
}
void Stack::onCurrentTabChanged(int index)
{
Q_UNUSED(index);
}
bool Stack::onMouseDoubleClick(QPoint localPos)
{
// User clicked the empty space of the tab widget and we don't have title bar
// We float the entire frame.
if (!(Config::self().flags() & Config::Flag_HideTitleBarWhenTabsVisible) || tabBar()->dockWidgetAt(localPos))
return false;
Frame *frame = this->frame();
// When using MainWindowOption_HasCentralFrame. The central frame is never detachable.
if (frame->isCentralFrame())
return false;
if (FloatingWindow *fw = frame->floatingWindow()) {
if (!fw->hasSingleFrame()) {
makeWindow();
return true;
}
} else if (frame->isInMainWindow()) {
makeWindow();
return true;
}
return false;
}
void Stack::setTabBarAutoHide(bool is)
{
// TODO
qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->setTabBarAutoHide(is);
}
void Stack::renameTab(int index, const QString &text)
{
// TODO
qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->renameTab(index, text);
}
Controllers::TabBar *Stack::tabBar() const
{
return m_tabBar;
}
int Stack::currentIndex() const
{
// TODO
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->currentIndex();
}
int Stack::numDockWidgets() const
{
// TODO
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->numDockWidgets();
}
void Stack::changeTabIcon(int index, const QIcon &icon)
{
// TODO
qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->changeTabIcon(index, icon);
}
DockWidgetBase *Stack::dockwidgetAt(int index) const
{
// TODO
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->dockwidgetAt(index);
}
int Stack::indexOfDockWidget(const DockWidget *dw) const
{
// TODO
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->indexOfDockWidget(dw);
}
void Stack::removeDockWidget(DockWidget *dw)
{
qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->removeDockWidget(dw);
}
bool Stack::insertDockWidget(int index, DockWidget *dw, const QIcon &icon, const QString &title)
{
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->insertDockWidget(index, dw, icon, title);
}
void Stack::setCurrentDockWidget(int index)
{
return qobject_cast<Views::Stack_qtwidgets *>(view()->asQWidget())->setCurrentDockWidget(index);
}