diff --git a/src/plasmaquick/configview.cpp b/src/plasmaquick/configview.cpp index b410e9593..ff437531b 100644 --- a/src/plasmaquick/configview.cpp +++ b/src/plasmaquick/configview.cpp @@ -1,361 +1,361 @@ /* * Copyright 2013 Marco Martin * * This program 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, 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. */ #include "private/configcategory_p.h" #include "configview.h" #include "configmodel.h" #include "Plasma/Applet" #include "Plasma/Containment" //#include "plasmoid/wallpaperinterface.h" #include "kdeclarative/configpropertymap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include //Unfortunately QWINDOWSIZE_MAX is not exported #define DIALOGSIZE_MAX ((1<<24)-1) namespace PlasmaQuick { //////////////////////////////ConfigView class ConfigViewPrivate { public: ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view); - ~ConfigViewPrivate(); + ~ConfigViewPrivate() = default; void init(); void updateMinimumWidth(); void updateMinimumHeight(); void updateMaximumWidth(); void updateMaximumHeight(); void mainItemLoaded(); ConfigView *q; QPointer applet; ConfigModel *configModel; ConfigModel *kcmConfigModel; Plasma::Corona *corona; //Attached Layout property of mainItem, if any QPointer mainItemLayout; }; ConfigViewPrivate::ConfigViewPrivate(Plasma::Applet *appl, ConfigView *view) : q(view), applet(appl), corona(nullptr) { } void ConfigViewPrivate::init() { if (!applet) { qWarning() << "Null applet passed to constructor"; return; } if (!applet.data()->pluginMetaData().isValid()) { qWarning() << "Invalid applet passed to constructor"; return; } applet.data()->setUserConfiguring(true); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(q->engine()); const QString rootPath = applet.data()->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); if (!rootPath.isEmpty()) { kdeclarative.setTranslationDomain(QStringLiteral("plasma_applet_") + rootPath); } else { kdeclarative.setTranslationDomain(QStringLiteral("plasma_applet_") + applet.data()->pluginMetaData().pluginId()); } kdeclarative.setupContext(); KDeclarative::KDeclarative::setupEngine(q->engine()); // ### how to make sure to do this only once per engine? //FIXME: problem on nvidia, all windows should be transparent or won't show q->setColor(Qt::transparent); q->setTitle(i18n("%1 Settings", applet.data()->title())); //systray case if (!applet.data()->containment()->corona()) { Plasma::Applet *a = qobject_cast(applet.data()->containment()->parent()); if (a) { corona = a->containment()->corona(); } } else if (!applet.data()->containment()->corona()->kPackage().isValid()) { qWarning() << "Invalid home screen package"; } else { corona = applet.data()->containment()->corona(); } if (!corona) { qWarning() << "Cannot find a Corona, this should never happen!"; return; } const auto pkg = corona->kPackage(); if (pkg.isValid()) { PackageUrlInterceptor *interceptor = new PackageUrlInterceptor(q->engine(), pkg); interceptor->addAllowedPath(applet.data()->kPackage().path()); q->engine()->setUrlInterceptor(interceptor); new QQmlFileSelector(q->engine(), q->engine()); } q->setResizeMode(QQuickView::SizeViewToRootObject); //config model local of the applet QQmlComponent *component = new QQmlComponent(q->engine(), applet.data()->kPackage().fileUrl("configmodel"), q); QObject *object = component->beginCreate(q->engine()->rootContext()); configModel = qobject_cast(object); if (configModel) { configModel->setApplet(applet.data()); } else { delete object; } QStringList kcms = KPluginMetaData::readStringList(applet.data()->pluginMetaData().rawData(), QStringLiteral("X-Plasma-ConfigPlugins")); // filter out non-authorized KCMs // KAuthorized expects KCMs with .desktop suffix, so we can't just pass everything // to KAuthorized::authorizeControlModules verbatim kcms.erase(std::remove_if(kcms.begin(), kcms.end(), [](const QString &kcm) { return !KAuthorized::authorizeControlModule(kcm + QLatin1String(".desktop")); }), kcms.end()); if (!kcms.isEmpty()) { if (!configModel) { configModel = new ConfigModel(q); } foreach (const QString &kcm, kcms) { KPluginLoader loader(KPluginLoader::findPlugin(QLatin1String("kcms/") + kcm)); KPluginMetaData md(loader.fileName()); if (!md.isValid()) { qWarning() << "Could not find" << kcm << "specified in X-Plasma-ConfigPlugins"; continue; } configModel->appendCategory(md.iconName(), md.name(), QString(), loader.fileName()); } } q->engine()->rootContext()->setContextProperty(QStringLiteral("plasmoid"), applet.data()->property("_plasma_graphicObject").value()); q->engine()->rootContext()->setContextProperty(QStringLiteral("configDialog"), q); component->completeCreate(); delete component; } void ConfigViewPrivate::updateMinimumWidth() { if (mainItemLayout) { q->setMinimumWidth(mainItemLayout.data()->property("minimumWidth").toInt()); //Sometimes setMinimumWidth doesn't actually resize: Qt bug? q->setWidth(qMax(q->width(), q->minimumWidth())); } else { q->setMinimumWidth(-1); } } void ConfigViewPrivate::updateMinimumHeight() { if (mainItemLayout) { q->setMinimumHeight(mainItemLayout.data()->property("minimumHeight").toInt()); //Sometimes setMinimumHeight doesn't actually resize: Qt bug? q->setHeight(qMax(q->height(), q->minimumHeight())); } else { q->setMinimumHeight(-1); } } void ConfigViewPrivate::updateMaximumWidth() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumWidth").toInt(); if (hint > 0) { q->setMaximumWidth(hint); } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } else { q->setMaximumWidth(DIALOGSIZE_MAX); } } void ConfigViewPrivate::updateMaximumHeight() { if (mainItemLayout) { const int hint = mainItemLayout.data()->property("maximumHeight").toInt(); if (hint > 0) { q->setMaximumHeight(hint); } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } else { q->setMaximumHeight(DIALOGSIZE_MAX); } } void ConfigViewPrivate::mainItemLoaded() { if (applet) { KConfigGroup cg = applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); q->resize(cg.readEntry("DialogWidth", q->width()), cg.readEntry("DialogHeight", q->height())); } //Extract the representation's Layout, if any QObject *layout = nullptr; //Search a child that has the needed Layout properties //HACK: here we are not type safe, but is the only way to access to a pointer of Layout foreach (QObject *child, q->rootObject()->children()) { //find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight if (child->property("minimumWidth").isValid() && child->property("minimumHeight").isValid() && child->property("preferredWidth").isValid() && child->property("preferredHeight").isValid() && child->property("maximumWidth").isValid() && child->property("maximumHeight").isValid() && child->property("fillWidth").isValid() && child->property("fillHeight").isValid() ) { layout = child; break; } } mainItemLayout = layout; if (layout) { QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(updateMinimumWidth())); QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(updateMinimumHeight())); QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(updateMaximumWidth())); QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(updateMaximumHeight())); updateMinimumWidth(); updateMinimumHeight(); updateMaximumWidth(); updateMaximumHeight(); } } ConfigView::ConfigView(Plasma::Applet *applet, QWindow *parent) : QQuickView(parent), d(new ConfigViewPrivate(applet, this)) { setIcon(QIcon::fromTheme(QStringLiteral("configure"))); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigModel"); qmlRegisterType("org.kde.plasma.configuration", 2, 0, "ConfigCategory"); d->init(); connect(applet, &QObject::destroyed, this, &ConfigView::close); connect(this, &QQuickView::statusChanged, [=](QQuickView::Status status) { if (status == QQuickView::Ready) { d->mainItemLoaded(); } }); } ConfigView::~ConfigView() { if (d->applet) { d->applet.data()->setUserConfiguring(false); if (d->applet.data()->containment() && d->applet.data()->containment()->corona()) { d->applet.data()->containment()->corona()->requestConfigSync(); } } } void ConfigView::init() { setSource(d->corona->kPackage().fileUrl("appletconfigurationui")); } Plasma::Applet *ConfigView::applet() { return d->applet.data(); } ConfigModel *ConfigView::configModel() const { return d->configModel; } QString ConfigView::appletGlobalShortcut() const { if (!d->applet) { return QString(); } return d->applet.data()->globalShortcut().toString(); } void ConfigView::setAppletGlobalShortcut(const QString &shortcut) { if (!d->applet || d->applet.data()->globalShortcut().toString().toLower() == shortcut.toLower()) { return; } d->applet.data()->setGlobalShortcut(shortcut); emit appletGlobalShortcutChanged(); } //To emulate Qt::WA_DeleteOnClose that QWindow doesn't have void ConfigView::hideEvent(QHideEvent *ev) { QQuickWindow::hideEvent(ev); deleteLater(); } void ConfigView::resizeEvent(QResizeEvent *re) { if (!rootObject()) { return; } rootObject()->setSize(re->size()); if (d->applet) { KConfigGroup cg = d->applet.data()->config(); cg = KConfigGroup(&cg, "ConfigDialog"); cg.writeEntry("DialogWidth", re->size().width()); cg.writeEntry("DialogHeight", re->size().height()); } QQuickWindow::resizeEvent(re); } } #include "moc_configview.cpp" diff --git a/src/plasmaquick/configview.h b/src/plasmaquick/configview.h index 52dea11c8..d559da739 100644 --- a/src/plasmaquick/configview.h +++ b/src/plasmaquick/configview.h @@ -1,94 +1,95 @@ /* * Copyright 2013 Marco Martin * * This program 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, 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. */ #ifndef CONFIGVIEW_H #define CONFIGVIEW_H #include +#include #include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // namespace Plasma { class Applet; } namespace PlasmaQuick { class ConfigViewPrivate; class ConfigModel; class PLASMAQUICK_EXPORT ConfigView : public QQuickView { Q_OBJECT Q_PROPERTY(PlasmaQuick::ConfigModel *configModel READ configModel CONSTANT) Q_PROPERTY(QString appletGlobalShortcut READ appletGlobalShortcut WRITE setAppletGlobalShortcut NOTIFY appletGlobalShortcutChanged) public: /** * @param applet the applet of this ConfigView * @param parent the QWindow in which this ConfigView is parented to **/ ConfigView(Plasma::Applet *applet, QWindow *parent = nullptr); ~ConfigView() override; virtual void init(); Plasma::Applet *applet(); QString appletGlobalShortcut() const; void setAppletGlobalShortcut(const QString &shortcut); /** * @return the ConfigModel of the ConfigView **/ PlasmaQuick::ConfigModel *configModel() const; Q_SIGNALS: void appletGlobalShortcutChanged(); protected: void hideEvent(QHideEvent *ev) override; void resizeEvent(QResizeEvent *re) override; private: - ConfigViewPrivate *const d; + QScopedPointer const d; Q_PRIVATE_SLOT(d, void updateMinimumWidth()) Q_PRIVATE_SLOT(d, void updateMinimumHeight()) Q_PRIVATE_SLOT(d, void updateMaximumWidth()) Q_PRIVATE_SLOT(d, void updateMaximumHeight()) }; } #endif // multiple inclusion guard diff --git a/src/plasmaquick/dialog.h b/src/plasmaquick/dialog.h index 727976bb4..ff432f81e 100644 --- a/src/plasmaquick/dialog.h +++ b/src/plasmaquick/dialog.h @@ -1,261 +1,262 @@ /*************************************************************************** * Copyright 2011 Marco Martin * * Copyright 2013 Sebastian Kügler * * * * 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 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 for more details. * * * * You should have received a copy of the GNU 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 . * ***************************************************************************/ #ifndef DIALOG_PROXY_P #define DIALOG_PROXY_P #include #include #include #include +#include #include #include #include #include // // W A R N I N G // ------------- // // This file is not part of the public Plasma API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // class QQuickItem; class QScreen; namespace PlasmaQuick { class DialogPrivate; /** * @class Dialog * * Dialog creates a Plasma themed top level window that can contain any QML component. * * It can be automatically positioned relative to a visual parent * The dialog will resize to the size of the main item * * @code{.qml} * import QtQuick 2.0 * import org.kde.plasma.core 2.0 as PlasmaCore * Item { * PlasmaCore.Dialog { * visible: true * mainItem: Item { * width: 500 * height: 500 * * Text { * anchors.centerIn: parent * color: "red" * text: "text" * } * } * } * } * @endcode * */ class PLASMAQUICK_EXPORT Dialog : public QQuickWindow, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) /** * The main QML item that will be displayed in the Dialog */ Q_PROPERTY(QQuickItem *mainItem READ mainItem WRITE setMainItem NOTIFY mainItemChanged) /** * The main QML item that will be displayed in the Dialog */ Q_PROPERTY(QQuickItem *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged) /** * Margins of the dialog around the mainItem. * @see DialogMargins */ Q_PROPERTY(QObject *margins READ margins CONSTANT) /** * Plasma Location of the dialog window. Useful if this dialog is a popup for a panel */ Q_PROPERTY(Plasma::Types::Location location READ location WRITE setLocation NOTIFY locationChanged) /** * Type of the window */ Q_PROPERTY(WindowType type READ type WRITE setType NOTIFY typeChanged) /** * Whether the dialog should be hidden when the dialog loses focus. * * The default value is @c false. **/ Q_PROPERTY(bool hideOnWindowDeactivate READ hideOnWindowDeactivate WRITE setHideOnWindowDeactivate NOTIFY hideOnWindowDeactivateChanged) /** * Whether the dialog is output only. Default value is @c false. If it is @c true * the dialog does not accept input and all pointer events are not accepted, thus the dialog * is click through. * * This property is currently only supported on the X11 platform. On any other platform the * property has no effect. **/ Q_PROPERTY(bool outputOnly READ isOutputOnly WRITE setOutputOnly NOTIFY outputOnlyChanged) /** * This property holds the window flags of the window. * The window flags control the window's appearance in the windowing system, * whether it's a dialog, popup, or a regular window, and whether it should * have a title bar, etc. * Regardless to what the user sets, the flags will always have the * FramelessWindowHint flag set */ Q_PROPERTY(Qt::WindowFlags flags READ flags WRITE setFramelessFlags NOTIFY flagsChanged) /** * This property holds how (and if at all) the dialog should draw its own background * or if it is complete responsibility of the content item to render a background. * Note that in case of NoBackground it loses kwin side shadows and blur */ Q_PROPERTY(BackgroundHints backgroundHints READ backgroundHints WRITE setBackgroundHints NOTIFY backgroundHintsChanged) Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChangedProxy) Q_CLASSINFO("DefaultProperty", "mainItem") public: enum WindowType { Normal = NET::Normal, Dock = NET::Dock, DialogWindow = NET::Dialog, PopupMenu = NET::PopupMenu, Tooltip = NET::Tooltip, Notification = NET::Notification, OnScreenDisplay = NET::OnScreenDisplay, CriticalNotification = NET::CriticalNotification }; Q_ENUM(WindowType) enum BackgroundHints { NoBackground = 0, /**< Not drawing a background under the applet, the dialog has its own implementation */ StandardBackground = 1 /**< The standard background from the theme is drawn */ }; Q_ENUM(BackgroundHints) explicit Dialog(QQuickItem *parent = nullptr); ~Dialog() override; //PROPERTIES ACCESSORS QQuickItem *mainItem() const; void setMainItem(QQuickItem *mainItem); QQuickItem *visualParent() const; void setVisualParent(QQuickItem *visualParent); Plasma::Types::Location location() const; void setLocation(Plasma::Types::Location location); QObject *margins() const; void setFramelessFlags(Qt::WindowFlags flags); void setType(WindowType type); WindowType type() const; bool hideOnWindowDeactivate() const; void setHideOnWindowDeactivate(bool hide); void setOutputOnly(bool outputOnly); bool isOutputOnly() const; BackgroundHints backgroundHints() const; void setBackgroundHints(BackgroundHints hints); bool isVisible() const; void setVisible(bool visible); /** * @returns The suggested screen position for the popup * @param item the item the popup has to be positioned relatively to. if null, the popup will be positioned in the center of the window * @param size the size that the popup will have, which influences the final position */ virtual QPoint popupPosition(QQuickItem *item, const QSize &size); Q_SIGNALS: void mainItemChanged(); void locationChanged(); void visualParentChanged(); void typeChanged(); void hideOnWindowDeactivateChanged(); void outputOnlyChanged(); void flagsChanged(); void backgroundHintsChanged(); void visibleChangedProxy(); //redeclaration of QQuickWindow::visibleChanged /** * Emitted when the @see hideOnWindowDeactivate property is @c true and this dialog lost focus to a * window that is neither a parent dialog to nor a child dialog of this dialog. */ void windowDeactivated(); protected: /** * set the dialog position. subclasses may change it. ToolTipDialog adjusts the position in an animated way */ virtual void adjustGeometry(const QRect &geom); //Reimplementations void classBegin() override; void componentComplete() override; void resizeEvent(QResizeEvent *re) override; void focusInEvent(QFocusEvent *ev) override; void focusOutEvent(QFocusEvent *ev) override; void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; bool event(QEvent *event) override; private: friend class DialogPrivate; - DialogPrivate *const d; + const QScopedPointer d; Q_PRIVATE_SLOT(d, void updateInputShape()) Q_PRIVATE_SLOT(d, void updateTheme()) Q_PRIVATE_SLOT(d, void updateVisibility(bool visible)) Q_PRIVATE_SLOT(d, void updateMinimumWidth()) Q_PRIVATE_SLOT(d, void updateMinimumHeight()) Q_PRIVATE_SLOT(d, void updateMaximumWidth()) Q_PRIVATE_SLOT(d, void updateMaximumHeight()) Q_PRIVATE_SLOT(d, void updateLayoutParameters()) Q_PRIVATE_SLOT(d, void slotMainItemSizeChanged()) }; } #endif