459 lines
10 KiB
C++
459 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.
|
|
*/
|
|
|
|
#ifndef KD_VIEW_QTWIDGETS_H
|
|
#define KD_VIEW_QTWIDGETS_H
|
|
|
|
#pragma once
|
|
|
|
#include "kddockwidgets/core/Controller.h"
|
|
#include "kddockwidgets/qtcommon/View.h"
|
|
#include "kddockwidgets/qtcommon/Platform.h"
|
|
|
|
#include <QDebug>
|
|
#include <QEvent>
|
|
#include <QResizeEvent>
|
|
#include <QSizePolicy>
|
|
#include <QWidget>
|
|
#include <QApplication>
|
|
#include <QWindow>
|
|
|
|
#include <memory>
|
|
|
|
namespace KDDockWidgets::QtWidgets {
|
|
|
|
DOCKS_EXPORT QSize boundedMaxSize(QSize min, QSize max);
|
|
|
|
template<typename Base>
|
|
class DOCKS_EXPORT View : public Base, public QtCommon::View_qt
|
|
{
|
|
public:
|
|
using Core::View::close;
|
|
using Core::View::height;
|
|
using Core::View::minimumHeight;
|
|
using Core::View::minimumWidth;
|
|
using Core::View::move;
|
|
using Core::View::pos;
|
|
using Core::View::rect;
|
|
using Core::View::resize;
|
|
using Core::View::setSize;
|
|
using Core::View::size;
|
|
using Core::View::width;
|
|
using Core::View::x;
|
|
using Core::View::y;
|
|
|
|
explicit View(Core::Controller *controller, Core::ViewType type,
|
|
QWidget *parent = nullptr, Qt::WindowFlags windowFlags = {});
|
|
|
|
~View() override = default;
|
|
|
|
QSize minSize() const override
|
|
{
|
|
const int minW =
|
|
Base::minimumWidth() > 0 ? Base::minimumWidth() : minimumSizeHint().width();
|
|
|
|
const int minH =
|
|
Base::minimumHeight() > 0 ? Base::minimumHeight() : minimumSizeHint().height();
|
|
|
|
return QSize(minW, minH).expandedTo(Core::View::hardcodedMinimumSize());
|
|
}
|
|
|
|
QSize minimumSizeHint() const override
|
|
{
|
|
return Base::minimumSizeHint();
|
|
}
|
|
|
|
void setMinimumSize(QSize sz) override;
|
|
|
|
QSize maxSizeHint() const override
|
|
{
|
|
// The max size is usually QWidget::maximumSize(), but we also honour the
|
|
// QSizePolicy::Fixed+sizeHint() case as widgets don't need to have QWidget::maximumSize()
|
|
// to have a max size honoured
|
|
|
|
const QSize min = minSize();
|
|
QSize max = Base::maximumSize();
|
|
max = QtWidgets::boundedMaxSize(min, max); // for safety against weird values
|
|
|
|
const auto vPolicy = QWidget::sizePolicy().verticalPolicy();
|
|
const auto hPolicy = QWidget::sizePolicy().horizontalPolicy();
|
|
|
|
if (vPolicy == QSizePolicy::Fixed || vPolicy == QSizePolicy::Maximum)
|
|
max.setHeight(qMin(max.height(), Base::sizeHint().height()));
|
|
if (hPolicy == QSizePolicy::Fixed || hPolicy == QSizePolicy::Maximum)
|
|
max.setWidth(qMin(max.width(), Base::sizeHint().width()));
|
|
|
|
max = QtWidgets::boundedMaxSize(min, max); // for safety against weird values
|
|
return max;
|
|
}
|
|
|
|
QRect geometry() const override
|
|
{
|
|
return Base::geometry();
|
|
}
|
|
|
|
QRect normalGeometry() const override
|
|
{
|
|
return Base::normalGeometry();
|
|
}
|
|
|
|
void setGeometry(QRect geo) override
|
|
{
|
|
Base::setGeometry(geo);
|
|
}
|
|
|
|
void setMaximumSize(QSize sz) override;
|
|
|
|
bool isVisible() const override
|
|
{
|
|
return Base::isVisible();
|
|
}
|
|
|
|
bool isExplicitlyHidden() const override
|
|
{
|
|
return Base::isHidden();
|
|
}
|
|
|
|
void setVisible(bool is) override
|
|
{
|
|
Base::setVisible(is);
|
|
}
|
|
|
|
void move(int x, int y) override
|
|
{
|
|
Base::move(x, y);
|
|
}
|
|
|
|
void setSize(int width, int height) override
|
|
{
|
|
Base::resize(width, height);
|
|
}
|
|
|
|
void setWidth(int width) override
|
|
{
|
|
setSize(width, QWidget::height());
|
|
}
|
|
|
|
void setHeight(int height) override
|
|
{
|
|
setSize(QWidget::width(), height);
|
|
}
|
|
|
|
void setFixedWidth(int w) override
|
|
{
|
|
QWidget::setFixedWidth(w);
|
|
}
|
|
|
|
void setFixedHeight(int h) override
|
|
{
|
|
QWidget::setFixedHeight(h);
|
|
}
|
|
|
|
void show() override
|
|
{
|
|
Base::show();
|
|
}
|
|
|
|
void createPlatformWindow() override
|
|
{
|
|
QWidget::create();
|
|
}
|
|
|
|
void hide() override
|
|
{
|
|
Base::hide();
|
|
}
|
|
|
|
void update() override
|
|
{
|
|
Base::update();
|
|
}
|
|
|
|
int zOrder() const override
|
|
{
|
|
if (auto p = QWidget::parentWidget()) {
|
|
// Some of them might be non-QtWidget QObject, but this is good enough
|
|
// to unit test raise() in MDI area
|
|
// TODO: Remove cast to int, and make method return qsizetype after we add /WX
|
|
return int(p->children().indexOf(const_cast<QtWidgets::View<Base> *>(this)));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void setParentFor(QWidget *widget, Core::View *parent)
|
|
{
|
|
if (!parent) {
|
|
widget->QWidget::setParent(nullptr);
|
|
return;
|
|
}
|
|
|
|
if (auto qwidget = View_qt::asQWidget(parent)) {
|
|
widget->QWidget::setParent(qwidget);
|
|
} else {
|
|
qWarning() << Q_FUNC_INFO << "parent is not a widget, you have a bug";
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void setParent(Core::View *parent) override
|
|
{
|
|
setParentFor(this, parent);
|
|
}
|
|
|
|
static void raiseAndActivate(QWidget *w)
|
|
{
|
|
w->window()->raise();
|
|
const bool isWayland = qGuiApp->platformName() == QLatin1String("wayland");
|
|
if (!isWayland)
|
|
w->window()->activateWindow();
|
|
}
|
|
|
|
void raiseAndActivate() override
|
|
{
|
|
raiseAndActivate(this);
|
|
}
|
|
|
|
void activateWindow() override
|
|
{
|
|
Base::activateWindow();
|
|
}
|
|
|
|
void raise() override
|
|
{
|
|
Base::raise();
|
|
}
|
|
|
|
bool isRootView() const override
|
|
{
|
|
return QWidget::isWindow();
|
|
}
|
|
|
|
QPoint mapToGlobal(QPoint localPt) const override
|
|
{
|
|
return Base::mapToGlobal(localPt);
|
|
}
|
|
|
|
QPoint mapFromGlobal(QPoint globalPt) const override
|
|
{
|
|
return Base::mapFromGlobal(globalPt);
|
|
}
|
|
|
|
QPoint mapTo(Core::View *someAncestor, QPoint pos) const override
|
|
{
|
|
auto *ancestorWidget = View_qt::asQWidget(someAncestor);
|
|
if (ancestorWidget && isAncestorOf(ancestorWidget, this))
|
|
return QWidget::mapTo(ancestorWidget, pos);
|
|
// Fallback via global coordinates when not in same hierarchy
|
|
return ancestorWidget ? ancestorWidget->mapFromGlobal(QWidget::mapToGlobal(pos)) : pos;
|
|
}
|
|
|
|
static bool isAncestorOf(const QWidget *ancestor, const QWidget *child)
|
|
{
|
|
for (auto *w = child->parentWidget(); w; w = w->parentWidget()) {
|
|
if (w == ancestor)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void setWindowOpacity(double v) override
|
|
{
|
|
QWidget::setWindowOpacity(v);
|
|
}
|
|
|
|
void render(QPainter *p) override
|
|
{
|
|
Base::render(p);
|
|
}
|
|
|
|
void setCursor(Qt::CursorShape shape) override
|
|
{
|
|
QWidget::setCursor(shape);
|
|
}
|
|
|
|
void setMouseTracking(bool enable) override
|
|
{
|
|
QWidget::setMouseTracking(enable);
|
|
}
|
|
|
|
bool close() override
|
|
{
|
|
return QWidget::close();
|
|
}
|
|
|
|
void setFlag(Qt::WindowType flag, bool on = true) override
|
|
{
|
|
QWidget::setWindowFlag(flag, on);
|
|
}
|
|
|
|
void enableAttribute(Qt::WidgetAttribute attr, bool enable = true) override
|
|
{
|
|
QWidget::setAttribute(attr, enable);
|
|
}
|
|
|
|
bool hasAttribute(Qt::WidgetAttribute attr) const override
|
|
{
|
|
return QWidget::testAttribute(attr);
|
|
}
|
|
|
|
Qt::WindowFlags flags() const override
|
|
{
|
|
return QWidget::windowFlags();
|
|
}
|
|
|
|
void setWindowTitle(const QString &title) override
|
|
{
|
|
QWidget::setWindowTitle(title);
|
|
}
|
|
|
|
void setWindowIcon(const QIcon &icon) override
|
|
{
|
|
QWidget::setWindowIcon(icon);
|
|
}
|
|
|
|
bool isActiveWindow() const override
|
|
{
|
|
return QWidget::isActiveWindow();
|
|
}
|
|
|
|
void showNormal() override
|
|
{
|
|
return QWidget::showNormal();
|
|
}
|
|
|
|
void showMinimized() override
|
|
{
|
|
return QWidget::showMinimized();
|
|
}
|
|
|
|
void showMaximized() override
|
|
{
|
|
return QWidget::showMaximized();
|
|
}
|
|
|
|
bool isMinimized() const override
|
|
{
|
|
return QWidget::isMinimized();
|
|
}
|
|
|
|
bool isMaximized() const override
|
|
{
|
|
return QWidget::isMaximized();
|
|
}
|
|
|
|
Qt::FocusPolicy focusPolicy() const override
|
|
{
|
|
return QWidget::focusPolicy();
|
|
}
|
|
|
|
bool hasFocus() const override
|
|
{
|
|
return QWidget::hasFocus();
|
|
}
|
|
|
|
std::shared_ptr<Core::View> childViewAt(QPoint localPos) const override;
|
|
|
|
std::shared_ptr<Core::Window> window() const override;
|
|
|
|
std::shared_ptr<Core::View> rootView() const override;
|
|
|
|
std::shared_ptr<Core::View> parentView() const override;
|
|
|
|
std::shared_ptr<Core::View> asWrapper() override;
|
|
|
|
void grabMouse() override
|
|
{
|
|
QWidget::grabMouse();
|
|
}
|
|
|
|
void releaseMouse() override
|
|
{
|
|
QWidget::releaseMouse();
|
|
}
|
|
|
|
void releaseKeyboard() override
|
|
{
|
|
QWidget::releaseKeyboard();
|
|
}
|
|
|
|
void setFocus(Qt::FocusReason reason) override
|
|
{
|
|
QWidget::setFocus(reason);
|
|
}
|
|
|
|
void setFocusPolicy(Qt::FocusPolicy policy) override
|
|
{
|
|
QWidget::setFocusPolicy(policy);
|
|
}
|
|
|
|
QString viewName() const override
|
|
{
|
|
return QWidget::objectName();
|
|
}
|
|
|
|
static QVector<std::shared_ptr<Core::View>> childViewsFor(const QWidget *parent);
|
|
|
|
QVector<std::shared_ptr<Core::View>> childViews() const override
|
|
{
|
|
return childViewsFor(this);
|
|
}
|
|
|
|
protected:
|
|
bool event(QEvent *e) override;
|
|
void closeEvent(QCloseEvent *ev) override;
|
|
|
|
void resizeEvent(QResizeEvent *ev) override
|
|
{
|
|
if (!onResize(ev->size()))
|
|
Base::resizeEvent(ev);
|
|
}
|
|
|
|
private:
|
|
Q_DISABLE_COPY(View)
|
|
};
|
|
|
|
inline qreal logicalDpiFactor(const QWidget *w)
|
|
{
|
|
#ifdef DOCKS_DEVELOPER_MODE
|
|
if (QtCommon::Platform_qt::s_logicalDpiFactorOverride > 0)
|
|
return QtCommon::Platform_qt::s_logicalDpiFactorOverride;
|
|
#endif
|
|
|
|
#ifdef Q_OS_MACOS
|
|
// It's always 72 on mac
|
|
Q_UNUSED(w);
|
|
return 1;
|
|
#else
|
|
return w->logicalDpiX() / 96.0;
|
|
#endif
|
|
}
|
|
|
|
inline QWindow *windowForWidget(const QWidget *w)
|
|
{
|
|
return w ? w->window()->windowHandle() : nullptr;
|
|
}
|
|
|
|
|
|
/// @brief sets the geometry on the QWindow containing the specified item
|
|
inline void setTopLevelGeometry(QRect geometry, const QWidget *widget)
|
|
{
|
|
if (!widget)
|
|
return;
|
|
|
|
if (QWidget *topLevel = widget->window())
|
|
topLevel->setGeometry(geometry);
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|