15.1.413. tablet_qt/qobjects/widgetpositioner.cpp

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.

    Based on the DialogPositioner class in https://github.com/f4exb/sdrangel/
    v7.21.1, which has the following licence:

    Copyright (C) 2022-2023 Jon Beniston, M7RCE <jon@beniston.com>
    Copyright (C) 2023 Mohamed <mohamedadlyi@github.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation as version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License V3 for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

#include <QEvent>
#include <QObject>
#include <QScreen>
#include <QtDebug>
#include <QTimer>
#include <QWidget>

// #define DEBUG_WIDGET_POSITIONER
// #define DEBUG_WIDGET_POSITIONER_LAYOUT

#ifdef DEBUG_WIDGET_POSITIONER_LAYOUT
    #include "lib/layoutdumper.h"
#endif

#include "widgetpositioner.h"

WidgetPositioner::WidgetPositioner(QWidget* widget) :
    QObject(widget),  // make widget our parent; it will own us
    m_widget(widget)
{
    Q_ASSERT(widget);

    centre();
    m_widget->installEventFilter(this);

    connect(
        m_widget->screen(),
        &QScreen::orientationChanged,
        this,
        &WidgetPositioner::orientationChanged
    );
}

void WidgetPositioner::orientationChanged(Qt::ScreenOrientation orientation)
{
#ifdef DEBUG_WIDGET_POSITIONER
    qDebug().nospace() << Q_FUNC_INFO;
#endif

    Q_UNUSED(orientation)

    // Not reliable to retrieve the screen or dialog geometry at this point.
    // https://bugreports.qt.io/browse/QTBUG-91363
    // https://bugreports.qt.io/browse/QTBUG-109127

    // hide() followed by show() will resize and reposition the dialog
    // correctly on iOS but this crashes on Android. So we do this ourselves
    // after a short delay.
    QTimer::singleShot(200, this, &WidgetPositioner::centre);
}

void WidgetPositioner::centre()
{
    Q_ASSERT(m_widget);

#ifdef DEBUG_WIDGET_POSITIONER
    qDebug().nospace() << Q_FUNC_INFO;
#endif

    sizeToScreen();

    const QRect available_geometry = m_widget->screen()->availableGeometry();
    const int x = (available_geometry.width() - m_widget->width()) / 2;
    const int y = (available_geometry.height() - m_widget->height()) / 2;

    const QPoint new_pos = QPoint(x, y);

    if (new_pos != m_widget->pos()) {
#ifdef DEBUG_WIDGET_POSITIONER
        qDebug("Moving widget to %d, %d", x, y);
#endif
        m_widget->move(x, y);
    } else {
#ifdef DEBUG_WIDGET_POSITIONER
        qDebug("No need to move widget");
#endif
    }

#ifdef DEBUG_WIDGET_POSITIONER_LAYOUT
    layoutdumper::dumpWidgetHierarchy(m_widget);
#endif
}

void WidgetPositioner::sizeToScreen()
{
#ifdef DEBUG_WIDGET_POSITIONER
    qDebug().nospace() << Q_FUNC_INFO;
#endif

    Q_ASSERT(m_widget);

    const QRect available_geometry = m_widget->screen()->availableGeometry();
    const int screen_width = available_geometry.width();
    const int screen_height = available_geometry.height();

    bool changed = false;

    int new_width = m_widget->width();
    int new_height = m_widget->height();

    if (new_width > screen_width) {
        new_width = screen_width;
        changed = true;
    }
    if (new_height > screen_height) {
        new_height = screen_height;
        changed = true;
    }
    if (changed) {
#ifdef DEBUG_WIDGET_POSITIONER
        qDebug("Resizing widget to %d, %d", new_width, new_height);
#endif
        m_widget->resize(new_width, new_height);
    } else {
#ifdef DEBUG_WIDGET_POSITIONER
        qDebug().nospace() << "No need to resize widget";
#endif
    }
}

bool WidgetPositioner::eventFilter(QObject* obj, QEvent* event)
{
    if (event->type() == QEvent::Show) {
#ifdef DEBUG_WIDGET_POSITIONER
        qDebug() << "Event filter calling centre()";
#endif
        centre();
    }

    return QObject::eventFilter(obj, event);
}