Merge "fix availableScreenGeometryForPosition()"

This commit is contained in:
Marco Martin 2014-09-23 12:50:55 +02:00 committed by Gerrit Code Review
commit d28871cb65
11 changed files with 824 additions and 184 deletions

View File

@ -48,6 +48,8 @@ PLASMA_UNIT_TESTS(
add_executable(storagetest storagetest.cpp ../src/plasma/private/storage.cpp ../src/plasma/private/storagethread.cpp) add_executable(storagetest storagetest.cpp ../src/plasma/private/storage.cpp ../src/plasma/private/storagethread.cpp)
target_link_libraries(storagetest Qt5::Gui Qt5::Test Qt5::Sql KF5::KIOCore KF5::Plasma KF5::CoreAddons) target_link_libraries(storagetest Qt5::Gui Qt5::Test Qt5::Sql KF5::KIOCore KF5::Plasma KF5::CoreAddons)
set(dialogtest_srcs dialogtest.cpp)
ecm_add_test(${dialogtest_srcs} TEST_NAME dialogtest LINK_LIBRARIES Qt5::Gui Qt5::Test Qt5::Qml Qt5::Quick KF5::WindowSystem KF5::Plasma KF5::PlasmaQuick)
set(coronatest_srcs coronatest.cpp) set(coronatest_srcs coronatest.cpp)
qt5_add_resources(coronatest_srcs coronatestresources.qrc) qt5_add_resources(coronatest_srcs coronatestresources.qrc)

57
autotests/dialogtest.cpp Normal file
View File

@ -0,0 +1,57 @@
/********************************************************************************
* Copyright 2014 Marco Martin <mart@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*********************************************************************************/
#include "dialogtest.h"
#include <KWindowSystem>
void DialogTest::initTestCase()
{
m_dialog = new PlasmaQuick::Dialog;
m_panel = new QQuickView;
m_panel->setGeometry(0, 0, 50, 50);
m_panel->setFlags(Qt::FramelessWindowHint|Qt::WindowDoesNotAcceptFocus);
m_content = new QQuickItem;
m_content->setWidth(100);
m_content->setHeight(100);
m_dialog->setMainItem(m_content);
m_panel->show();
KWindowSystem::setType(m_panel->winId(), NET::Dock);
m_dialog->setVisualParent(m_panel->contentItem());
m_dialog->show();
}
void DialogTest::cleanupTestCase()
{
delete m_dialog;
delete m_panel;
}
void DialogTest::position()
{
QTest::qWaitForWindowExposed(m_dialog);
QCOMPARE(m_dialog->x(), 0);
QCOMPARE(m_dialog->y(), 49);
}
QTEST_MAIN(DialogTest)

48
autotests/dialogtest.h Normal file
View File

@ -0,0 +1,48 @@
/******************************************************************************
* Copyright 2014 Marco Martin <mart@kde.org> *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public License *
* along with this library; see the file COPYING.LIB. If not, write to *
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02110-1301, USA. *
*******************************************************************************/
#ifndef DIALOGTEST_H
#define DIALOGTEST_H
#include <QtTest/QtTest>
#include <QQuickView>
#include <QQuickItem>
#include "plasmaquick/dialog.h"
class DialogTest : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
void cleanupTestCase();
private Q_SLOTS:
void position();
private:
QQuickView *m_panel;
QQuickItem *m_content;
PlasmaQuick::Dialog *m_dialog;
};
#endif

View File

@ -2,6 +2,7 @@
* Copyright 2011 Marco Martin <mart@kde.org> * * Copyright 2011 Marco Martin <mart@kde.org> *
* Copyright 2013 Sebastian Kügler <sebas@kde.org> * * Copyright 2013 Sebastian Kügler <sebas@kde.org> *
* Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> * * Copyright 2014 Martin Gräßlin <mgraesslin@kde.org> *
* Copyright 2014 Vishesh Handa <vhanda@kde.org> *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
@ -29,6 +30,7 @@
#include <QLayout> #include <QLayout>
#include <QScreen> #include <QScreen>
#include <QMenu> #include <QMenu>
#include <QPointer>
#include <kwindowsystem.h> #include <kwindowsystem.h>
#include <KWindowSystem/KWindowInfo> #include <KWindowSystem/KWindowInfo>
@ -61,21 +63,27 @@ public:
hideOnWindowDeactivate(false), hideOnWindowDeactivate(false),
outputOnly(false), outputOnly(false),
componentComplete(dialog->parent() == 0), componentComplete(dialog->parent() == 0),
resizeOrigin(Undefined),
backgroundHints(Dialog::StandardBackground) backgroundHints(Dialog::StandardBackground)
{ {
} }
enum ResizeOrigin {
Undefined,
MainItem,
Window
};
void updateInputShape(); void updateInputShape();
//SLOTS //SLOTS
void syncBorders(); /**
* Sync Borders updates the enabled borders of the frameSvgItem depending
* on the geometry of the window.
*
* \param windowGeometry The window geometry which should be taken into
* consideration when activating/deactivating certain borders
*/
void syncBorders(const QRect& windowGeometry);
/**
* This function sets the blurBehind, background contrast and shadows. It
* does so wrt the frameSvgItem. So make sure the frameSvgItem is the
* correct size before calling this function.
*/
void updateTheme(); void updateTheme();
void updateVisibility(bool visible); void updateVisibility(bool visible);
@ -84,16 +92,32 @@ public:
void updateMaximumWidth(); void updateMaximumWidth();
void updateMaximumHeight(); void updateMaximumHeight();
void syncMainItemToSize(); /**
* This function is an optimized version of updateMaximumHeight,
* updateMaximumWidth,updateMinimumWidth and updateMinimumHeight.
* It should be called when you need to call all 4 of these functions
* AND you have called syncToMainItemSize before.
*/
void updateLayoutParameters();
QRect availableScreenGeometryForPosition(const QPoint& pos) const;
/**
* This function checks the current position of the dialog and repositions
* it so that no part of it is not on the screen
*/
void repositionIfOffScreen();
void slotMainItemSizeChanged();
void slotWindowPositionChanged();
void syncToMainItemSize(); void syncToMainItemSize();
void requestSizeSync(bool delayed = false);
Dialog *q; Dialog *q;
QTimer *syncTimer;
Plasma::Types::Location location; Plasma::Types::Location location;
Plasma::FrameSvgItem *frameSvgItem; Plasma::FrameSvgItem *frameSvgItem;
QWeakPointer<QQuickItem> mainItem; QPointer<QQuickItem> mainItem;
QWeakPointer<QQuickItem> visualParent; QPointer<QQuickItem> visualParent;
QRect cachedGeometry; QRect cachedGeometry;
Dialog::WindowType type; Dialog::WindowType type;
@ -101,14 +125,13 @@ public:
bool outputOnly; bool outputOnly;
Plasma::Theme theme; Plasma::Theme theme;
bool componentComplete; bool componentComplete;
ResizeOrigin resizeOrigin;
Dialog::BackgroundHints backgroundHints; Dialog::BackgroundHints backgroundHints;
//Attached Layout property of mainItem, if any //Attached Layout property of mainItem, if any
QWeakPointer <QObject> mainItemLayout; QPointer <QObject> mainItemLayout;
}; };
void DialogPrivate::syncBorders() QRect DialogPrivate::availableScreenGeometryForPosition(const QPoint& pos) const
{ {
// FIXME: QWindow::screen() never ever changes if the window is moved across // FIXME: QWindow::screen() never ever changes if the window is moved across
// virtual screens (normal two screens with X), this seems to be intentional // virtual screens (normal two screens with X), this seems to be intentional
@ -117,42 +140,43 @@ void DialogPrivate::syncBorders()
// we simply iterate over the virtual screens and pick the one our QWindow // we simply iterate over the virtual screens and pick the one our QWindow
// says it's at. // says it's at.
QRect avail; QRect avail;
QPoint pos = q->position();
Q_FOREACH (QScreen *screen, q->screen()->virtualSiblings()) { Q_FOREACH (QScreen *screen, q->screen()->virtualSiblings()) {
if (screen->availableGeometry().contains(pos)) { //we check geometry() but then take availableGeometry()
//to reliably check in which screen a position is, we need the full
//geometry, including areas for panels
if (screen->geometry().contains(pos)) {
avail = screen->availableGeometry(); avail = screen->availableGeometry();
break; break;
} }
} }
return avail;
}
void DialogPrivate::syncBorders(const QRect& geom)
{
QRect avail = availableScreenGeometryForPosition(geom.topLeft());
int borders = Plasma::FrameSvg::AllBorders; int borders = Plasma::FrameSvg::AllBorders;
//Tooltips always have all the borders //Tooltips always have all the borders
// floating windows have all borders // floating windows have all borders
if ((q->flags() & Qt::ToolTip) != Qt::ToolTip && location != Plasma::Types::Floating) { if ((q->flags() & Qt::ToolTip) != Qt::ToolTip && location != Plasma::Types::Floating) {
if (q->x() <= avail.x() || location == Plasma::Types::LeftEdge) { if (geom.x() <= avail.x() || location == Plasma::Types::LeftEdge) {
borders = borders & ~Plasma::FrameSvg::LeftBorder; borders = borders & ~Plasma::FrameSvg::LeftBorder;
} }
if (q->y() <= avail.y() || location == Plasma::Types::TopEdge) { if (geom.y() <= avail.y() || location == Plasma::Types::TopEdge) {
borders = borders & ~Plasma::FrameSvg::TopBorder; borders = borders & ~Plasma::FrameSvg::TopBorder;
} }
if (avail.right() <= q->x() + q->width() || location == Plasma::Types::RightEdge) { if (avail.right() <= geom.x() + geom.width() || location == Plasma::Types::RightEdge) {
borders = borders & ~Plasma::FrameSvg::RightBorder; borders = borders & ~Plasma::FrameSvg::RightBorder;
} }
if (avail.bottom() <= q->y() + q->height() || location == Plasma::Types::BottomEdge) { if (avail.bottom() <= geom.y() + geom.height() || location == Plasma::Types::BottomEdge) {
borders = borders & ~Plasma::FrameSvg::BottomBorder; borders = borders & ~Plasma::FrameSvg::BottomBorder;
} }
} }
if (frameSvgItem->enabledBorders() != (Plasma::FrameSvg::EnabledBorder)borders) { if (frameSvgItem->enabledBorders() != (Plasma::FrameSvg::EnabledBorder)borders) {
frameSvgItem->setEnabledBorders((Plasma::FrameSvg::EnabledBorder)borders); frameSvgItem->setEnabledBorders((Plasma::FrameSvg::EnabledBorder)borders);
if (mainItemLayout) {
updateMinimumWidth();
updateMinimumHeight();
updateMaximumWidth();
updateMaximumHeight();
}
} }
} }
@ -194,8 +218,8 @@ void DialogPrivate::updateTheme()
void DialogPrivate::updateVisibility(bool visible) void DialogPrivate::updateVisibility(bool visible)
{ {
if (visible) { if (visible) {
if (visualParent && visualParent.data()->window()) { if (visualParent && visualParent->window()) {
q->setTransientParent(visualParent.data()->window()); q->setTransientParent(visualParent->window());
} }
if (q->location() == Plasma::Types::FullScreen) { if (q->location() == Plasma::Types::FullScreen) {
@ -210,11 +234,19 @@ void DialogPrivate::updateVisibility(bool visible)
} else { } else {
if (!cachedGeometry.isNull()) { if (!cachedGeometry.isNull()) {
q->resize(cachedGeometry.size()); q->resize(cachedGeometry.size());
syncMainItemToSize(); slotWindowPositionChanged();
if (visualParent) {
q->setPosition(q->popupPosition(visualParent, q->size()));
}
cachedGeometry = QRect(); cachedGeometry = QRect();
} }
if (mainItem) {
syncToMainItemSize(); syncToMainItemSize();
} }
if (mainItemLayout) {
updateLayoutParameters();
}
}
} }
if (!(q->flags() & Qt::ToolTip)) { if (!(q->flags() & Qt::ToolTip)) {
@ -259,68 +291,211 @@ void DialogPrivate::updateVisibility(bool visible)
void DialogPrivate::updateMinimumWidth() void DialogPrivate::updateMinimumWidth()
{ {
if (mainItemLayout) { Q_ASSERT(mainItem);
Q_ASSERT(mainItemLayout);
mainItem->disconnect(q);
syncBorders(q->geometry());
int minimumWidth = mainItemLayout->property("minimumWidth").toInt();
auto margin = frameSvgItem->margins();
int oldWidth = q->width(); int oldWidth = q->width();
q->setMinimumWidth(mainItemLayout.data()->property("minimumWidth").toInt() + frameSvgItem->margins()->left() + frameSvgItem->margins()->right());
//Sometimes setMinimumWidth doesn't actually resize: Qt bug? q->setMinimumWidth(minimumWidth + margin->left() + margin->right());
resizeOrigin = DialogPrivate::Window;
q->setWidth(qMax(q->width(), q->minimumWidth())); q->setWidth(qMax(q->width(), q->minimumWidth()));
mainItem->setWidth(q->width() - margin->left() - margin->right());
frameSvgItem->setWidth(q->width());
if (location == Plasma::Types::RightEdge) { if (location == Plasma::Types::RightEdge) {
q->setX(q->x() + (oldWidth - q->size().width())); q->setX(q->x() + (oldWidth - q->size().width()));
} }
} else { repositionIfOffScreen();
q->setMinimumWidth(-1); if (visualParent) {
const QRect geom(q->popupPosition(visualParent, q->size()), q->size());
q->setGeometry(geom);
} }
updateTheme();
QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
} }
void DialogPrivate::updateMinimumHeight() void DialogPrivate::updateMinimumHeight()
{ {
if (mainItemLayout) { if (!componentComplete) {
int oldHeight = q->height(); return;
q->setMinimumHeight(mainItemLayout.data()->property("minimumHeight").toInt() + frameSvgItem->margins()->top() + frameSvgItem->margins()->bottom()); }
//Sometimes setMinimumHeight doesn't actually resize: Qt bug? Q_ASSERT(mainItem);
resizeOrigin = DialogPrivate::Window; Q_ASSERT(mainItemLayout);
mainItem->disconnect(q);
syncBorders(q->geometry());
int minimumHeight = mainItemLayout->property("minimumHeight").toInt();
auto margin = frameSvgItem->margins();
int oldHeight = mainItem->height();
q->setMinimumHeight(minimumHeight + margin->top() + margin->bottom());
q->setHeight(qMax(q->height(), q->minimumHeight())); q->setHeight(qMax(q->height(), q->minimumHeight()));
mainItem->setHeight(q->height() - margin->top() - margin->bottom());
frameSvgItem->setHeight(q->height());
if (location == Plasma::Types::BottomEdge) { if (location == Plasma::Types::BottomEdge) {
q->setY(q->y() + (oldHeight - q->size().height())); q->setY(q->y() + (oldHeight - q->size().height()));
} }
} else { repositionIfOffScreen();
q->setMinimumHeight(-1); if (visualParent) {
const QRect geom(q->popupPosition(visualParent, q->size()), q->size());
q->setGeometry(geom);
} }
updateTheme();
QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
} }
void DialogPrivate::updateMaximumWidth() void DialogPrivate::updateMaximumWidth()
{ {
if (mainItemLayout) { if (!componentComplete) {
const int hint = mainItemLayout.data()->property("maximumWidth").toInt(); return;
resizeOrigin = DialogPrivate::Window;
if (hint > 0) {
q->setMaximumWidth(hint + frameSvgItem->margins()->left() + frameSvgItem->margins()->right());
q->setWidth(qMin(q->width(), q->maximumWidth()));
} else {
q->setMaximumWidth(DIALOGSIZE_MAX);
} }
} else { Q_ASSERT(mainItem);
q->setMaximumWidth(DIALOGSIZE_MAX); Q_ASSERT(mainItemLayout);
mainItem->disconnect(q);
syncBorders(q->geometry());
int maximumWidth = mainItemLayout->property("maximumWidth").toInt();
maximumWidth = maximumWidth ? maximumWidth : DIALOGSIZE_MAX;
auto margin = frameSvgItem->margins();
q->setMaximumWidth(maximumWidth + margin->left() + margin->right());
q->setWidth(qBound(q->minimumWidth(), q->width(), q->maximumWidth()));
mainItem->setWidth(q->width() - margin->left() - margin->right());
frameSvgItem->setWidth(q->width());
repositionIfOffScreen();
if (visualParent) {
const QRect geom(q->popupPosition(visualParent, q->size()), q->size());
q->setGeometry(geom);
} }
updateTheme();
QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
} }
void DialogPrivate::updateMaximumHeight() void DialogPrivate::updateMaximumHeight()
{ {
if (mainItemLayout) { if (!componentComplete) {
const int hint = mainItemLayout.data()->property("maximumHeight").toInt(); return;
resizeOrigin = DialogPrivate::Window;
if (hint > 0) {
q->setMaximumHeight(hint + frameSvgItem->margins()->top() + frameSvgItem->margins()->bottom());
q->setHeight(qMin(q->height(), q->maximumHeight()));
} else {
q->setMaximumHeight(DIALOGSIZE_MAX);
} }
} else { Q_ASSERT(mainItem);
q->setMaximumHeight(DIALOGSIZE_MAX); Q_ASSERT(mainItemLayout);
mainItem->disconnect(q);
syncBorders(q->geometry());
int maximumHeight = mainItemLayout->property("maximumHeight").toInt();
maximumHeight = maximumHeight ? maximumHeight : DIALOGSIZE_MAX;
auto margin = frameSvgItem->margins();
q->setMaximumHeight(maximumHeight + margin->top() + margin->bottom());
q->setHeight(qBound(q->minimumHeight(), q->height(), q->maximumHeight()));
mainItem->setHeight(q->height() - margin->top() - margin->bottom());
frameSvgItem->setHeight(q->height());
repositionIfOffScreen();
if (visualParent) {
const QRect geom(q->popupPosition(visualParent, q->size()), q->size());
q->setGeometry(geom);
} }
updateTheme();
QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
}
void DialogPrivate::updateLayoutParameters()
{
if (!componentComplete || !mainItem || !q->isVisible()) {
return;
}
Q_ASSERT(mainItem);
Q_ASSERT(mainItemLayout);
mainItem->disconnect(q);
int minimumHeight = mainItemLayout->property("minimumHeight").toInt();
int maximumHeight = mainItemLayout->property("maximumHeight").toInt();
maximumHeight = maximumHeight ? maximumHeight : DIALOGSIZE_MAX;
int minimumWidth = mainItemLayout->property("minimumWidth").toInt();
int maximumWidth = mainItemLayout->property("maximumWidth").toInt();
maximumWidth = maximumWidth ? maximumWidth : DIALOGSIZE_MAX;
auto margin = frameSvgItem->margins();
q->setMinimumHeight(minimumHeight + margin->top() + margin->bottom());
q->setMaximumHeight(maximumHeight + margin->top() + margin->bottom());
q->setHeight(qBound(q->minimumHeight(), q->height(), q->maximumHeight()));
q->setMinimumWidth(minimumWidth + margin->left() + margin->right());
q->setMaximumWidth(maximumWidth + margin->left() + margin->right());
q->setWidth(qBound(q->minimumWidth(), q->width(), q->maximumWidth()));
mainItem->setX(margin->left());
mainItem->setY(margin->top());
mainItem->setWidth(q->width() - margin->left() - margin->right());
mainItem->setHeight(q->height() - margin->top() - margin->bottom());
frameSvgItem->setWidth(q->width());
frameSvgItem->setHeight(q->height());
repositionIfOffScreen();
updateTheme();
QObject::connect(mainItem, SIGNAL(widthChanged()), q, SLOT(slotMainItemSizeChanged()));
QObject::connect(mainItem, SIGNAL(heightChanged()), q, SLOT(slotMainItemSizeChanged()));
}
void DialogPrivate::repositionIfOffScreen()
{
if (!componentComplete) {
return;
}
const QRect avail = availableScreenGeometryForPosition(q->position());
int x = q->x();
int y = q->y();
if (x < avail.left()) {
x = avail.left();
} else if (x + q->width() > avail.right()) {
x = avail.right() - q->width() + 1;
}
if (y < avail.top()) {
y = avail.top();
} else if (y + q->height() > avail.bottom()) {
y = avail.bottom() - q->height() + 1;
}
q->setX(x);
q->setY(y);
} }
void DialogPrivate::updateInputShape() void DialogPrivate::updateInputShape()
@ -367,34 +542,38 @@ void DialogPrivate::updateInputShape()
#endif #endif
} }
void DialogPrivate::syncMainItemToSize()
{
syncBorders();
updateTheme();
if (mainItem) {
mainItem.data()->setX(frameSvgItem->margins()->left());
mainItem.data()->setY(frameSvgItem->margins()->top());
mainItem.data()->setWidth(q->width() - frameSvgItem->margins()->left() - frameSvgItem->margins()->right());
mainItem.data()->setHeight(q->height() - frameSvgItem->margins()->top() - frameSvgItem->margins()->bottom());
}
if (q->visualParent()) {
q->setPosition(q->popupPosition(q->visualParent(), q->size()));
}
}
void DialogPrivate::syncToMainItemSize() void DialogPrivate::syncToMainItemSize()
{ {
//if manually sync a sync timer was running cancel it so we don't get called twice Q_ASSERT(mainItem);
syncTimer->stop();
if (!mainItem) { if (!componentComplete || !q->isVisible()) {
return; return;
} }
const QSize s = QSize(mainItem.data()->width(), mainItem.data()->height()) + if (visualParent) {
// Get the full size with ALL the borders
frameSvgItem->setEnabledBorders(Plasma::FrameSvg::AllBorders);
auto margins = frameSvgItem->margins();
const QSize fullSize = QSize(mainItem->width(), mainItem->height()) +
QSize(margins->left() + margins->right(),
margins->top() + margins->bottom());
// We get the popup position with the fullsize as we need the popup
// position in order to determine our actual size, as the position
// determines which borders will be shown.
const QRect geom(q->popupPosition(visualParent, fullSize), fullSize);
// We're then moving the window to where we think we would be with all
// the borders. This way when syncBorders is called, it has a geometry
// to work with.
syncBorders(geom);
}
else {
syncBorders(q->geometry());
}
const QSize s = QSize(mainItem->width(), mainItem->height()) +
QSize(frameSvgItem->margins()->left() + frameSvgItem->margins()->right(), QSize(frameSvgItem->margins()->left() + frameSvgItem->margins()->right(),
frameSvgItem->margins()->top() + frameSvgItem->margins()->bottom()); frameSvgItem->margins()->top() + frameSvgItem->margins()->bottom());
@ -403,34 +582,43 @@ void DialogPrivate::syncToMainItemSize()
frameSvgItem->setWidth(s.width()); frameSvgItem->setWidth(s.width());
frameSvgItem->setHeight(s.height()); frameSvgItem->setHeight(s.height());
if (q->visualParent()) { if (visualParent) {
const QRect geom(q->popupPosition(q->visualParent(), s), s); const QRect geom(q->popupPosition(visualParent, s), s);
if (geom == q->geometry()) { if (geom == q->geometry()) {
return; return;
} }
q->adjustGeometry(geom); q->adjustGeometry(geom);
// The borders will instantly be updated but the geometry might take a
// while as sub-classes can reimplement adjustGeometry and animate it.
syncBorders(geom);
} else { } else {
q->resize(s); q->resize(s);
} }
syncBorders(); mainItem->setX(frameSvgItem->margins()->left());
mainItem.data()->setX(frameSvgItem->margins()->left()); mainItem->setY(frameSvgItem->margins()->top());
mainItem.data()->setY(frameSvgItem->margins()->top());
updateTheme(); updateTheme();
} }
void DialogPrivate::requestSizeSync(bool delayed) void DialogPrivate::slotWindowPositionChanged()
{ {
if (!componentComplete) { // Tooltips always have all the borders
// floating windows have all borders
if ((q->flags() & Qt::ToolTip) || location == Plasma::Types::Floating) {
return; return;
} }
if (delayed && !syncTimer->isActive()) { syncBorders(q->geometry());
syncTimer->start(150); updateTheme();
} else {
syncTimer->start(0); if (mainItem) {
auto margin = frameSvgItem->margins();
mainItem->setX(margin->left());
mainItem->setY(margin->top());
mainItem->setWidth(q->width() - margin->left() - margin->right());
mainItem->setHeight(q->height() - margin->top() - margin->bottom());
} }
} }
@ -444,35 +632,9 @@ Dialog::Dialog(QQuickItem *parent)
setIcon(QIcon::fromTheme("plasma")); setIcon(QIcon::fromTheme("plasma"));
d->syncTimer = new QTimer(this); connect(this, &QWindow::xChanged, [=]() { d->slotWindowPositionChanged(); });
d->syncTimer->setSingleShot(true); connect(this, &QWindow::yChanged, [=]() { d->slotWindowPositionChanged(); });
d->syncTimer->setInterval(0);
connect(d->syncTimer, &QTimer::timeout,
[ = ]() {
if (d->resizeOrigin == DialogPrivate::MainItem) {
d->syncToMainItemSize();
} else {
d->syncMainItemToSize();
}
d->resizeOrigin = DialogPrivate::Undefined;
});
connect(this, &QWindow::xChanged, [ = ]() {
//Tooltips always have all the borders
// floating windows have all borders
if (!(flags() & Qt::ToolTip) && d->location != Plasma::Types::Floating) {
d->resizeOrigin = DialogPrivate::Window;
d->requestSizeSync(true);
}
});
connect(this, &QWindow::yChanged, [ = ]() {
//Tooltips always have all the borders
// floating windows have all borders
if (!(flags() & Qt::ToolTip) && d->location != Plasma::Types::Floating) {
d->resizeOrigin = DialogPrivate::Window;
d->requestSizeSync(true);
}
});
connect(this, SIGNAL(visibleChanged(bool)), connect(this, SIGNAL(visibleChanged(bool)),
this, SLOT(updateInputShape())); this, SLOT(updateInputShape()));
connect(this, SIGNAL(outputOnlyChanged()), connect(this, SIGNAL(outputOnlyChanged()),
@ -497,14 +659,14 @@ Dialog::~Dialog()
QQuickItem *Dialog::mainItem() const QQuickItem *Dialog::mainItem() const
{ {
return d->mainItem.data(); return d->mainItem;
} }
void Dialog::setMainItem(QQuickItem *mainItem) void Dialog::setMainItem(QQuickItem *mainItem)
{ {
if (d->mainItem.data() != mainItem) { if (d->mainItem != mainItem) {
if (d->mainItem) { if (d->mainItem) {
d->mainItem.data()->setParent(parent()); d->mainItem->setParent(parent());
} }
d->mainItem = mainItem; d->mainItem = mainItem;
@ -513,20 +675,9 @@ void Dialog::setMainItem(QQuickItem *mainItem)
mainItem->setParent(contentItem()); mainItem->setParent(contentItem());
mainItem->setProperty("parent", QVariant::fromValue(contentItem())); mainItem->setProperty("parent", QVariant::fromValue(contentItem()));
if (mainItem->metaObject()->indexOfSignal("widthChanged")) { connect(mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged()));
connect(mainItem, &QQuickItem::widthChanged, [ = ]() { connect(mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged()));
d->resizeOrigin = DialogPrivate::MainItem; d->slotMainItemSizeChanged();
d->syncTimer->start(0);
});
}
if (mainItem->metaObject()->indexOfSignal("heightChanged")) {
connect(mainItem, &QQuickItem::heightChanged, [ = ]() {
d->resizeOrigin = DialogPrivate::MainItem;
d->syncTimer->start(0);
});
}
d->resizeOrigin = DialogPrivate::MainItem;
d->requestSizeSync();
//Extract the representation's Layout, if any //Extract the representation's Layout, if any
QObject *layout = 0; QObject *layout = 0;
@ -546,7 +697,7 @@ void Dialog::setMainItem(QQuickItem *mainItem)
} }
} }
if (d->mainItemLayout) { if (d->mainItemLayout) {
disconnect(d->mainItemLayout.data(), 0, this, 0); disconnect(d->mainItemLayout, 0, this, 0);
} }
d->mainItemLayout = layout; d->mainItemLayout = layout;
@ -556,10 +707,7 @@ void Dialog::setMainItem(QQuickItem *mainItem)
connect(layout, SIGNAL(maximumWidthChanged()), this, SLOT(updateMaximumWidth())); connect(layout, SIGNAL(maximumWidthChanged()), this, SLOT(updateMaximumWidth()));
connect(layout, SIGNAL(maximumHeightChanged()), this, SLOT(updateMaximumHeight())); connect(layout, SIGNAL(maximumHeightChanged()), this, SLOT(updateMaximumHeight()));
d->updateMinimumWidth(); d->updateLayoutParameters();
d->updateMinimumHeight();
d->updateMaximumWidth();
d->updateMaximumHeight();
} }
} }
@ -569,14 +717,19 @@ void Dialog::setMainItem(QQuickItem *mainItem)
} }
} }
void DialogPrivate::slotMainItemSizeChanged()
{
syncToMainItemSize();
}
QQuickItem *Dialog::visualParent() const QQuickItem *Dialog::visualParent() const
{ {
return d->visualParent.data(); return d->visualParent;
} }
void Dialog::setVisualParent(QQuickItem *visualParent) void Dialog::setVisualParent(QQuickItem *visualParent)
{ {
if (d->visualParent.data() == visualParent) { if (d->visualParent == visualParent) {
return; return;
} }
@ -586,8 +739,9 @@ void Dialog::setVisualParent(QQuickItem *visualParent)
if (visualParent->window()) { if (visualParent->window()) {
setTransientParent(visualParent->window()); setTransientParent(visualParent->window());
} }
d->resizeOrigin = DialogPrivate::MainItem; if (d->mainItem) {
d->requestSizeSync(true); d->syncToMainItemSize();
}
} }
} }
@ -734,8 +888,10 @@ void Dialog::setLocation(Plasma::Types::Location location)
} }
d->location = location; d->location = location;
emit locationChanged(); emit locationChanged();
d->resizeOrigin = DialogPrivate::MainItem;
d->requestSizeSync(); if (d->mainItem) {
d->syncToMainItemSize();
}
} }
QObject *Dialog::margins() const QObject *Dialog::margins() const
@ -756,17 +912,20 @@ void Dialog::adjustGeometry(const QRect &geom)
void Dialog::resizeEvent(QResizeEvent* re) void Dialog::resizeEvent(QResizeEvent* re)
{ {
d->frameSvgItem->setX(0);
d->frameSvgItem->setY(0);
d->frameSvgItem->setWidth(re->size().width());
d->frameSvgItem->setHeight(re->size().height());
QQuickWindow::resizeEvent(re); QQuickWindow::resizeEvent(re);
if (d->resizeOrigin == DialogPrivate::Undefined) { d->mainItem->disconnect(this);
d->resizeOrigin = DialogPrivate::Window;
} d->frameSvgItem->setWidth(re->size().width());
d->requestSizeSync(true); d->frameSvgItem->setHeight(re->size().height());
auto margin = d->frameSvgItem->margins();
d->mainItem->setX(margin->left());
d->mainItem->setY(margin->top());
d->mainItem->setWidth(re->size().width() - margin->left() - margin->right());
d->mainItem->setHeight(re->size().height() - margin->top() - margin->bottom());
QObject::connect(d->mainItem, SIGNAL(widthChanged()), this, SLOT(slotMainItemSizeChanged()));
QObject::connect(d->mainItem, SIGNAL(heightChanged()), this, SLOT(slotMainItemSizeChanged()));
} }
void Dialog::setType(WindowType type) void Dialog::setType(WindowType type)
@ -833,7 +992,6 @@ void Dialog::focusOutEvent(QFocusEvent *ev)
bool childHasFocus = focusWindow && ((focusWindow->isActive() && isAncestorOf(focusWindow)) || focusWindow->type() & Qt::Popup); bool childHasFocus = focusWindow && ((focusWindow->isActive() && isAncestorOf(focusWindow)) || focusWindow->type() & Qt::Popup);
if (qobject_cast<const View *>(focusWindow) || (!parentHasFocus && !childHasFocus)) { if (qobject_cast<const View *>(focusWindow) || (!parentHasFocus && !childHasFocus)) {
qDebug() << "DIALOG: hiding dialog.";
setVisible(false); setVisible(false);
emit windowDeactivated(); emit windowDeactivated();
} }
@ -872,15 +1030,24 @@ void Dialog::hideEvent(QHideEvent *event)
void Dialog::classBegin() void Dialog::classBegin()
{ {
d->componentComplete = false;
} }
void Dialog::componentComplete() void Dialog::componentComplete()
{ {
d->componentComplete = true; d->componentComplete = true;
d->updateTheme();
if (d->mainItem) {
d->syncToMainItemSize(); d->syncToMainItemSize();
} }
if (d->mainItemLayout) {
d->updateLayoutParameters();
}
}
bool Dialog::hideOnWindowDeactivate() const bool Dialog::hideOnWindowDeactivate() const
{ {
return d->hideOnWindowDeactivate; return d->hideOnWindowDeactivate;

View File

@ -22,7 +22,6 @@
#include <QQuickItem> #include <QQuickItem>
#include <QQuickWindow> #include <QQuickWindow>
#include <QWeakPointer>
#include <QPoint> #include <QPoint>
#include <QQmlParserStatus> #include <QQmlParserStatus>
@ -236,15 +235,16 @@ private:
friend class DialogPrivate; friend class DialogPrivate;
DialogPrivate *const d; DialogPrivate *const d;
Q_PRIVATE_SLOT(d, void syncBorders()) Q_PRIVATE_SLOT(d, void updateInputShape())
Q_PRIVATE_SLOT(d, void updateTheme()) Q_PRIVATE_SLOT(d, void updateTheme())
Q_PRIVATE_SLOT(d, void updateVisibility(bool visible)) Q_PRIVATE_SLOT(d, void updateVisibility(bool visible))
Q_PRIVATE_SLOT(d, void updateInputShape())
Q_PRIVATE_SLOT(d, void updateMinimumWidth()) Q_PRIVATE_SLOT(d, void updateMinimumWidth())
Q_PRIVATE_SLOT(d, void updateMinimumHeight()) Q_PRIVATE_SLOT(d, void updateMinimumHeight())
Q_PRIVATE_SLOT(d, void updateMaximumWidth()) Q_PRIVATE_SLOT(d, void updateMaximumWidth())
Q_PRIVATE_SLOT(d, void updateMaximumHeight()) Q_PRIVATE_SLOT(d, void updateMaximumHeight())
Q_PRIVATE_SLOT(d, void slotMainItemSizeChanged())
}; };
} }

View File

@ -0,0 +1,59 @@
/*
* Copyright 2014 Vishesh Handa <vhanda@kde.org>
*
* 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; either version 2,
* 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 for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Controls 1.1 as Controls
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
PlasmaCore.Dialog {
id: dialog
location: PlasmaCore.Types.Floating
ColumnLayout {
Controls.Label {
Layout.maximumWidth: rect.width
wrapMode: Text.WordWrap
text: "Clicking on the rectangle should toggle the full screen mode. Make sure it retains its original geometry when jumping in between full screen and normal"
}
Rectangle {
id: rect
color: "green"
width: 500
height: 500
MouseArea {
anchors.fill: parent
onClicked: {
if (dialog.location != PlasmaCore.Types.FullScreen) {
dialog.location = PlasmaCore.Types.FullScreen;
}
else {
dialog.location = PlasmaCore.Types.Floating;
}
}
}
}
}
}

View File

@ -25,6 +25,7 @@ import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.core 2.0 as PlasmaCore
PlasmaCore.Dialog { PlasmaCore.Dialog {
id: root
location: PlasmaCore.Types.Floating location: PlasmaCore.Types.Floating
Rectangle { Rectangle {
@ -34,6 +35,10 @@ PlasmaCore.Dialog {
color: "red" color: "red"
Rectangle {
width: rect.Layout.minimumWidth
height: rect.Layout.minimumHeight
}
ColumnLayout { ColumnLayout {
anchors.top: parent.top anchors.top: parent.top
Controls.Label { Controls.Label {
@ -53,6 +58,18 @@ PlasmaCore.Dialog {
rect.Layout.minimumHeight = rect.Layout.minimumHeight + 10 rect.Layout.minimumHeight = rect.Layout.minimumHeight + 10
} }
} }
Controls.Button {
text: "Increase dialog width"
onClicked: {
root.width = root.width + 10
}
}
Controls.Button {
text: "Increase dialog height"
onClicked: {
root.height = root.height + 10
}
}
} }
} }
} }

View File

@ -45,10 +45,11 @@ PlasmaCore.Dialog {
id: innerRect id: innerRect
color: "#ddffdd" color: "#ddffdd"
width: 200 width: 200
height: 200 height: layout.height
anchors.centerIn: parent anchors.centerIn: parent
ColumnLayout { ColumnLayout {
id: layout
anchors.margins: 5 anchors.margins: 5
anchors.top: parent.top anchors.top: parent.top
anchors.left:parent.left anchors.left:parent.left

View File

@ -0,0 +1,89 @@
/*
* Copyright 2014 Vishesh Handa <vhanda@kde.org>
*
* 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; either version 2,
* 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 for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Controls 1.1 as Controls
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
ColumnLayout
{
Controls.Label {
text: "Press the buttom and make sure the popup is on the correct place"
wrapMode: Text.WordWrap
}
PlasmaComponents.Button {
id: settingsButton
iconSource: "configure"
text: "Press Me"
Layout.alignment: Qt.AlignHCenter
onClicked: {
contextMenu.visible = !contextMenu.visible;
}
}
PlasmaCore.Dialog {
id: contextMenu
visualParent: settingsButton
location: PlasmaCore.Types.BottomEdge
type: PlasmaCore.Dialog.PopupMenu
flags: Qt.Popup | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus
mainItem: ColumnLayout {
id: menuColumn
Layout.minimumWidth: menuColumn.implicitWidth
Layout.minimumHeight: menuColumn.implicitHeight
spacing: units.smallSpacing
PlasmaExtras.Heading {
level: 3
text: "Panel Alignment"
}
PlasmaComponents.ButtonColumn {
spacing: 0
Layout.fillWidth: true
PlasmaComponents.ToolButton {
anchors {
left: parent.left
right: parent.right
}
text: "Left"
checkable: true
flat: false
}
PlasmaComponents.ToolButton {
anchors {
left: parent.left
right: parent.right
}
text: "Center"
checkable: true
flat: false
}
}
}
}
}

80
tests/dialog_tooltip.qml Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright 2014 Vishesh Handa <vhanda@kde.org>
*
* 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; either version 2,
* 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 for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.2
import QtQuick.Controls 1.1 as Controls
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
ColumnLayout {
Controls.Label {
Layout.maximumWidth: mainLayout.width
wrapMode: Text.WordWrap
text: "Hover over every rectangle so that the tooltip pops up. It should popup in the correct position"
}
RowLayout {
id: mainLayout
Rectangle {
width: 300
height: 100
color: "red"
PlasmaCore.ToolTipArea {
width: 300
height: 50
mainText: "Title Number 1"
subText: "subtext"
icon: "plasma"
}
}
Rectangle {
width: 300
height: 100
color: "blue"
PlasmaCore.ToolTipArea {
width: 500
height: 110
mainText: "Title Number 2"
subText: "This is some really really really long subtext. So lets write stores about the woods and the trees and how we're going hiking. Yaye!"
icon: "configure"
}
}
Rectangle {
width: 300
height: 100
color: "green"
PlasmaCore.ToolTipArea {
width: 350
height: 70
mainText: "Wakka Wakka"
subText: "It's time for Africa"
}
}
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright 2014 Vishesh Handa <vhanda@kde.org>
*
* 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; either version 2,
* 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 for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Controls 1.1 as Controls
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
ColumnLayout {
Controls.Label {
Layout.maximumWidth: mainLayout.width
wrapMode: Text.WordWrap
text: "Click on each coloured box to make a dialog popup. It should popup in the correct position. The popup should also move from one rectanlge to the other on hovering"
}
RowLayout {
id: mainLayout
Rectangle {
width: 300
height: 100
color: "red"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
dialog.visualParent = parent;
dialog.visible = !dialog.visible;
}
onEntered: {
dialog.visualParent = parent;
}
}
}
Rectangle {
width: 300
height: 100
color: "blue"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
dialog.visualParent = parent;
dialog.visible = !dialog.visible;
}
onEntered: {
dialog.visualParent = parent;
}
}
}
Rectangle {
width: 300
height: 100
color: "green"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
dialog.visualParent = parent;
dialog.visible = !dialog.visible;
}
onEntered: {
dialog.visualParent = parent;
}
}
}
Rectangle {
width: 300
height: 100
color: "yellow"
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
dialog.visualParent = parent;
dialog.visible = !dialog.visible;
}
onEntered: {
dialog.visualParent = parent;
}
}
}
PlasmaCore.Dialog {
id: dialog
location: PlasmaCore.Types.BottomEdge
visible: false
Rectangle {
color: "black"
width: 150
height: 150
}
}
}
}