510 lines
10 KiB
C++
510 lines
10 KiB
C++
/*
|
|
This file is part of KDDockWidgets.
|
|
|
|
SPDX-FileCopyrightText: 2020 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 "core/View.h"
|
|
#include "core/View_p.h"
|
|
#include "core/Utils_p.h"
|
|
#include "core/layouting/Item_p.h"
|
|
#include "core/EventFilterInterface.h"
|
|
#include "core/FloatingWindow.h"
|
|
#include "core/Group.h"
|
|
#include "core/Stack.h"
|
|
#include "core/TitleBar.h"
|
|
#include "core/TabBar.h"
|
|
#include "core/MainWindow.h"
|
|
#include "core/DropArea.h"
|
|
#include "core/MDILayout.h"
|
|
#include "core/Logging_p.h"
|
|
#include "core/Platform.h"
|
|
#include "core/Window_p.h"
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <utility>
|
|
|
|
using namespace KDDockWidgets;
|
|
using namespace KDDockWidgets::Core;
|
|
|
|
namespace KDDockWidgets {
|
|
static qint64 s_nextId = 1;
|
|
|
|
static Controller *maybeCreateController(Controller *controller, ViewType type, View *view)
|
|
{
|
|
if (controller)
|
|
return controller;
|
|
|
|
if (type == ViewType::ViewWrapper)
|
|
return new Controller(ViewType::ViewWrapper, view);
|
|
|
|
return new Controller(ViewType::None, view);
|
|
}
|
|
|
|
}
|
|
|
|
View::View(Controller *controller, ViewType type)
|
|
: d(new Private(this, QString::number(KDDockWidgets::s_nextId++), type))
|
|
, m_controller(maybeCreateController(controller, type, this))
|
|
{
|
|
}
|
|
|
|
View::~View()
|
|
{
|
|
m_inDtor = true;
|
|
safeEmitSignal(d->beingDestroyed);
|
|
|
|
if (!d->freed() && !View::is(ViewType::ViewWrapper) && !View::is(ViewType::DropAreaIndicatorOverlay)) {
|
|
// Views should be deleted via View::free()!
|
|
// However some of our deletes are coming from widgets parent destroying their children
|
|
// But we want the controllers to drive things instead. For now detect the view destruction
|
|
// and destroy its controller, which was the old behaviour.
|
|
delete m_controller;
|
|
}
|
|
|
|
delete d;
|
|
}
|
|
|
|
QString View::Private::id() const
|
|
{
|
|
return m_id;
|
|
}
|
|
|
|
ViewType View::Private::type() const
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
void View::Private::free()
|
|
{
|
|
if (m_freed) {
|
|
KDDW_ERROR("Free already called");
|
|
return;
|
|
}
|
|
|
|
m_freed = true;
|
|
delete q;
|
|
}
|
|
|
|
void View::init()
|
|
{
|
|
}
|
|
|
|
bool View::Private::freed() const
|
|
{
|
|
return m_freed;
|
|
}
|
|
|
|
bool View::inDtor() const
|
|
{
|
|
return m_inDtor;
|
|
}
|
|
|
|
void View::setZOrder(int)
|
|
{
|
|
}
|
|
|
|
int View::zOrder() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Size View::size() const
|
|
{
|
|
return geometry().size();
|
|
}
|
|
|
|
Point View::pos() const
|
|
{
|
|
return geometry().topLeft();
|
|
}
|
|
|
|
Rect View::rect() const
|
|
{
|
|
return Rect(Point(0, 0), size());
|
|
}
|
|
|
|
int View::x() const
|
|
{
|
|
return geometry().x();
|
|
}
|
|
|
|
int View::y() const
|
|
{
|
|
return geometry().y();
|
|
}
|
|
|
|
int View::height() const
|
|
{
|
|
return geometry().height();
|
|
}
|
|
|
|
int View::width() const
|
|
{
|
|
return geometry().width();
|
|
}
|
|
|
|
void View::move(Point pt)
|
|
{
|
|
move(pt.x(), pt.y());
|
|
}
|
|
|
|
void View::resize(Size sz)
|
|
{
|
|
setSize(sz.width(), sz.height());
|
|
}
|
|
|
|
void View::setSize(Size sz)
|
|
{
|
|
setSize(sz.width(), sz.height());
|
|
}
|
|
|
|
void View::resize(int w, int h)
|
|
{
|
|
setSize(w, h);
|
|
}
|
|
|
|
int View::minimumWidth() const
|
|
{
|
|
return minSize().width();
|
|
}
|
|
|
|
int View::minimumHeight() const
|
|
{
|
|
return minSize().height();
|
|
}
|
|
|
|
Size View::screenSize() const
|
|
{
|
|
if (auto screen = d->screen())
|
|
return screen->size();
|
|
|
|
return {};
|
|
}
|
|
|
|
Controller *View::controller() const
|
|
{
|
|
return m_controller;
|
|
}
|
|
|
|
/** static */
|
|
Size View::hardcodedMinimumSize()
|
|
{
|
|
return Core::Item::hardcodedMinimumSize;
|
|
}
|
|
|
|
bool View::is(ViewType t) const
|
|
{
|
|
return int(d->m_type) & int(t);
|
|
}
|
|
|
|
Core::FloatingWindow *View::asFloatingWindowController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::FloatingWindow))
|
|
return object_cast<Core::FloatingWindow *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::Group *View::asGroupController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::Group))
|
|
return object_cast<Core::Group *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::TitleBar *View::asTitleBarController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::TitleBar))
|
|
return object_cast<Core::TitleBar *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::TabBar *View::asTabBarController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::TabBar))
|
|
return object_cast<Core::TabBar *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::Stack *View::asStackController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::Stack))
|
|
return object_cast<Core::Stack *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::DockWidget *View::asDockWidgetController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::DockWidget))
|
|
return object_cast<Core::DockWidget *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::MainWindow *View::asMainWindowController() const
|
|
{
|
|
if (m_controller && m_controller->is(ViewType::MainWindow))
|
|
return object_cast<Core::MainWindow *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::DropArea *View::asDropAreaController() const
|
|
{
|
|
if (!m_inDtor && m_controller && m_controller->is(ViewType::DropArea)) {
|
|
return object_cast<Core::DropArea *>(m_controller);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Core::MDILayout *View::asMDILayoutController() const
|
|
{
|
|
if (!m_inDtor && m_controller && m_controller->is(ViewType::MDILayout))
|
|
return object_cast<Core::MDILayout *>(m_controller);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Core::Layout *View::asLayout() const
|
|
{
|
|
if (Core::DropArea *da = asDropAreaController()) {
|
|
return da;
|
|
} else if (Core::MDILayout *mdi = asMDILayoutController()) {
|
|
return mdi;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool View::equals(const View *other) const
|
|
{
|
|
return other && handle() == other->handle();
|
|
}
|
|
|
|
bool View::equals(const std::shared_ptr<View> &other) const
|
|
{
|
|
if (isNull() || !other || other->isNull()) {
|
|
// We don't care about nullity for identity
|
|
return false;
|
|
}
|
|
|
|
return handle() == other->handle();
|
|
}
|
|
|
|
bool View::isNull() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool View::Private::isInWindow(std::shared_ptr<Core::Window> window) const
|
|
{
|
|
if (!window)
|
|
return false;
|
|
|
|
if (auto ourWindow = q->window())
|
|
return ourWindow->equals(window);
|
|
|
|
return false;
|
|
}
|
|
|
|
Size View::Private::parentSize() const
|
|
{
|
|
if (auto p = q->parentView())
|
|
return p->size();
|
|
return {};
|
|
}
|
|
|
|
Rect View::Private::windowGeometry() const
|
|
{
|
|
if (Core::Window::Ptr window = q->window())
|
|
return window->geometry();
|
|
|
|
return {};
|
|
}
|
|
|
|
void View::Private::closeRootView()
|
|
{
|
|
if (auto view = q->rootView())
|
|
view->close();
|
|
}
|
|
|
|
Screen::Ptr View::Private::screen() const
|
|
{
|
|
if (Core::Window::Ptr window = q->window())
|
|
return window->screen();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void View::Private::setAboutToBeDestroyed()
|
|
{
|
|
m_aboutToBeDestroyed = true;
|
|
}
|
|
|
|
bool View::Private::aboutToBeDestroyed() const
|
|
{
|
|
return m_aboutToBeDestroyed;
|
|
}
|
|
|
|
void View::dumpDebug()
|
|
{
|
|
KDDW_DEBUG("View::dumpDebug: controller={}, type={}, rootController={}\n", ( void * )m_controller, int(d->type()), ( void * )rootView()->controller());
|
|
}
|
|
|
|
bool View::isFixedWidth() const
|
|
{
|
|
return !m_inDtor && minSize().width() == maxSizeHint().width();
|
|
}
|
|
|
|
bool View::isFixedHeight() const
|
|
{
|
|
return !m_inDtor && minSize().height() == maxSizeHint().height();
|
|
}
|
|
|
|
/** static */
|
|
Controller *View::firstParentOfType(View *view, ViewType type)
|
|
{
|
|
auto p = view->asWrapper();
|
|
while (p) {
|
|
if (p->is(type))
|
|
return p->controller();
|
|
|
|
// Ignore QObject hierarchies spanning though multiple windows
|
|
if (p->isRootView())
|
|
return nullptr;
|
|
|
|
p = p->parentView();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Controller *View::Private::firstParentOfType(ViewType type) const
|
|
{
|
|
return View::firstParentOfType(const_cast<View *>(q), type);
|
|
}
|
|
|
|
void View::Private::requestClose(CloseEvent *e)
|
|
{
|
|
if (m_emittingClose)
|
|
return;
|
|
|
|
m_emittingClose = true;
|
|
closeRequested.emit(e);
|
|
m_emittingClose = false;
|
|
}
|
|
|
|
Rect View::Private::globalGeometry() const
|
|
{
|
|
Rect geo = q->geometry();
|
|
if (!q->isRootView())
|
|
geo.moveTopLeft(q->mapToGlobal(Point(0, 0)));
|
|
return geo;
|
|
}
|
|
|
|
void View::createPlatformWindow()
|
|
{
|
|
// Only qtwidgets need this
|
|
KDDW_ERROR("Shouldn't be called on this platform");
|
|
std::abort();
|
|
}
|
|
|
|
std::shared_ptr<Core::Window> View::Private::transientWindow() const
|
|
{
|
|
if (auto w = q->window())
|
|
return w->transientParent();
|
|
|
|
return {};
|
|
}
|
|
|
|
bool View::onResize(int w, int h)
|
|
{
|
|
d->resized.emit(Size(w, h));
|
|
return false;
|
|
}
|
|
|
|
bool View::onResize(Size sz)
|
|
{
|
|
return onResize(sz.width(), sz.height());
|
|
}
|
|
|
|
/** static */
|
|
bool View::equals(const View *one, const View *two)
|
|
{
|
|
if ((one && !two) || (!one && two))
|
|
return false;
|
|
|
|
if (!one && !two)
|
|
return true;
|
|
|
|
return one->equals(two);
|
|
}
|
|
|
|
void View::installViewEventFilter(EventFilterInterface *filter)
|
|
{
|
|
d->m_viewEventFilters.push_back(filter);
|
|
}
|
|
|
|
void View::removeViewEventFilter(EventFilterInterface *filter)
|
|
{
|
|
d->m_viewEventFilters.erase(
|
|
std::remove(d->m_viewEventFilters.begin(), d->m_viewEventFilters.end(), filter),
|
|
d->m_viewEventFilters.end());
|
|
}
|
|
|
|
bool View::deliverViewEventToFilters(Event *ev)
|
|
{
|
|
for (Core::EventFilterInterface *filter : std::as_const(d->m_viewEventFilters)) {
|
|
if (ev->type() == Event::Move) {
|
|
if (filter->onMoveEvent(this))
|
|
return true;
|
|
} else if (auto me = mouseEvent(ev)) {
|
|
if (filter->onMouseEvent(this, me))
|
|
return true;
|
|
|
|
switch (ev->type()) {
|
|
case Event::MouseButtonPress:
|
|
if (filter->onMouseButtonPress(this, me))
|
|
return true;
|
|
break;
|
|
case Event::MouseButtonRelease:
|
|
if (filter->onMouseButtonRelease(this, me))
|
|
return true;
|
|
break;
|
|
case Event::MouseMove:
|
|
if (filter->onMouseButtonMove(this, me))
|
|
return true;
|
|
break;
|
|
case Event::MouseButtonDblClick:
|
|
if (filter->onMouseDoubleClick(this, me))
|
|
return true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void View::Private::emitLayoutInvalidated()
|
|
{
|
|
if (m_emittingLayoutInvalidated)
|
|
return;
|
|
m_emittingLayoutInvalidated = true;
|
|
layoutInvalidated.emit();
|
|
m_emittingLayoutInvalidated = false;
|
|
}
|