2026-04-06 00:20:51 -05:00

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