diff --git a/src/plasma/applet.cpp b/src/plasma/applet.cpp index 7f0043c4b..37ee1296a 100644 --- a/src/plasma/applet.cpp +++ b/src/plasma/applet.cpp @@ -1,846 +1,920 @@ /* * Copyright 2005 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * Copyright 2008 by Ménard Alexis * Copyright (c) 2009 Chani Armitage * * 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 "applet.h" #include "private/applet_p.h" #include "config-plasma.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include "containment.h" #include "corona.h" #include "package.h" #include "plasma.h" #include "scripting/appletscript.h" #include "pluginloader.h" #include "private/associatedapplicationmanager_p.h" #include "private/containment_p.h" #include "private/package_p.h" #include "debug_p.h" namespace Plasma { static KPluginMetaData appletMetadataForDirectory(const QString &path) { return QFile::exists(path + QLatin1String("/metadata.json")) ? KPluginMetaData(path + QLatin1String("/metadata.json")) : KPluginMetaData::fromDesktopFile(path + QLatin1String("/metadata.desktop"), { QStringLiteral("plasma-applet.desktop") }); } Applet::Applet(const KPluginMetaData &info, QObject *parent, uint appletId) : QObject(parent), d(new AppletPrivate(info, appletId, this)) { qCDebug(LOG_PLASMA) << " From KPluginMetaData, valid? " << info.isValid(); // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(); d->setupPackage(); } Applet::Applet(const KPluginInfo &info, QObject *parent, uint appletId) : Applet(info.toMetaData(), parent, appletId) { } Applet::Applet(QObject *parent, const QString &serviceID, uint appletId) : QObject(parent), d(new AppletPrivate(KPluginMetaData(serviceID), appletId, this)) { // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(); d->setupPackage(); } Applet::Applet(QObject *parentObject, const QVariantList &args) : QObject(nullptr), d(new AppletPrivate(KPluginMetaData(), args.count() > 2 ? args[2].toInt() : 0, this)) { setParent(parentObject); if (!args.isEmpty()) { const QVariant first = args.first(); if (first.canConvert()) { d->package = first.value(); } } if (args.count() > 1) { const QVariant second = args[1]; if (second.canConvert()) { d->appletDescription = KPluginMetaData(second.toString()); } else if (second.canConvert()) { auto metadata = second.toMap().value(QStringLiteral("MetaData")).toMap(); d->appletDescription = KPluginMetaData(QJsonObject::fromVariantMap(metadata), {}); } } d->icon = d->appletDescription.iconName(); if (args.contains(QVariant::fromValue(QStringLiteral("org.kde.plasma:force-create")))) { setProperty("org.kde.plasma:force-create", true); } // WARNING: do not access config() OR globalConfig() in this method! // that requires a scene, which is not available at this point d->init(QString(), args.mid(3)); d->setupPackage(); } Applet::Applet(const QString &packagePath, uint appletId) : QObject(nullptr), d(new AppletPrivate(appletMetadataForDirectory(packagePath), appletId, this)) { d->init(packagePath); d->setupPackage(); } Applet::~Applet() { if (d->transient) { d->resetConfigurationObject(); } //let people know that i will die emit appletDeleted(this); // ConfigLoader is deleted when AppletPrivate closes not Applet // It saves on closure and emits a signal. // disconnect early to avoid a crash. See 411221 disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); delete d; } void Applet::init() { //Don't implement anything here, it will be overridden by subclasses } uint Applet::id() const { return d->appletId; } void Applet::save(KConfigGroup &g) const { if (d->transient || !d->appletDescription.isValid()) { return; } KConfigGroup group = g; if (!group.isValid()) { group = *d->mainConfigGroup(); } //qCDebug(LOG_PLASMA) << "saving" << pluginName() << "to" << group.name(); // we call the dptr member directly for locked since isImmutable() // also checks kiosk and parent containers group.writeEntry("immutability", (int)d->immutability); group.writeEntry("plugin", d->appletDescription.pluginId()); if (!d->started) { return; } KConfigGroup appletConfigGroup(&group, "Configuration"); saveState(appletConfigGroup); if (d->configLoader) { // we're saving so we know its changed, we don't need or want the configChanged // signal bubbling up at this point due to that disconnect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); d->configLoader->save(); connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); } } void Applet::restore(KConfigGroup &group) { setImmutability((Types::ImmutabilityType)group.readEntry("immutability", (int)Types::Mutable)); KConfigGroup shortcutConfig(&group, "Shortcuts"); QString shortcutText = shortcutConfig.readEntryUntranslated("global", QString()); if (!shortcutText.isEmpty()) { setGlobalShortcut(QKeySequence(shortcutText)); /* #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "got global shortcut for" << name() << "of" << QKeySequence(shortcutText); #endif #ifndef NDEBUG // qCDebug(LOG_PLASMA) << "set to" << d->activationAction->objectName() #endif << d->activationAction->globalShortcut().primary(); */ } // local shortcut, if any //TODO: implement; the shortcut will need to be registered with the containment /* #include "accessmanager.h" #include "private/plasmoidservice_p.h" #include "authorizationmanager.h" #include "authorizationmanager.h" shortcutText = shortcutConfig.readEntryUntranslated("local", QString()); if (!shortcutText.isEmpty()) { //TODO: implement; the shortcut } */ + + // User background hints + // TODO support flags in the config + QByteArray hintsString = config().readEntry("UserBackgroundHints", QString()).toUtf8(); + QMetaEnum hintEnum = QMetaEnum::fromType(); + bool ok; + int value = hintEnum.keyToValue(hintsString.constData(), &ok); + if (ok) { + d->userBackgroundHints = Plasma::Types::BackgroundHints(value); + d->userBackgroundHintsInitialized = true; + emit userBackgroundHintsChanged(); + if (d->backgroundHints & Plasma::Types::ConfigurableBackground) { + emit effectiveBackgroundHintsChanged(); + } + } } void Applet::setLaunchErrorMessage(const QString &message) { if (message == d->launchErrorMessage) { return; } d->failed = true; d->launchErrorMessage = message; } void Applet::saveState(KConfigGroup &group) const { if (d->script) { emit d->script->saveState(group); } if (group.config()->name() != config().config()->name()) { // we're being saved to a different file! // let's just copy the current values in our configuration over KConfigGroup c = config(); c.copyTo(&group); } } KConfigGroup Applet::config() const { if (d->transient) { return KConfigGroup(KSharedConfig::openConfig(), "PlasmaTransientsConfig"); } if (isContainment()) { return *(d->mainConfigGroup()); } return KConfigGroup(d->mainConfigGroup(), "Configuration"); } KConfigGroup Applet::globalConfig() const { KConfigGroup globalAppletConfig; QString group = isContainment() ? QStringLiteral("ContainmentGlobals") : QStringLiteral("AppletGlobals"); Containment *cont = containment(); Corona *corona = nullptr; if (cont) { corona = cont->corona(); } if (corona) { KSharedConfig::Ptr coronaConfig = corona->config(); globalAppletConfig = KConfigGroup(coronaConfig, group); } else { globalAppletConfig = KConfigGroup(KSharedConfig::openConfig(), group); } return KConfigGroup(&globalAppletConfig, d->globalName()); } void Applet::destroy() { if (immutability() != Types::Mutable || d->transient || !d->started) { return; //don't double delete } d->setDestroyed(true); //FIXME: an animation on leave if !isContainment() would be good again .. which should be handled by the containment class d->cleanUpAndDelete(); } bool Applet::destroyed() const { return d->transient; } KConfigLoader *Applet::configScheme() const { if (!d->configLoader) { const QString xmlPath = d->package.isValid() ? d->package.filePath("mainconfigxml") : QString(); KConfigGroup cfg = config(); if (xmlPath.isEmpty()) { d->configLoader = new KConfigLoader(cfg, nullptr); } else { QFile file(xmlPath); d->configLoader = new KConfigLoader(cfg, &file); QObject::connect(d->configLoader, SIGNAL(configChanged()), this, SLOT(propagateConfigChanged())); } } return d->configLoader; } Package Applet::package() const { Package p; p.d->internalPackage = new KPackage::Package(d->package); return p; } KPackage::Package Applet::kPackage() const { return d->package; } void Applet::updateConstraints(Plasma::Types::Constraints constraints) { d->scheduleConstraintsUpdate(constraints); } void Applet::constraintsEvent(Plasma::Types::Constraints constraints) { //NOTE: do NOT put any code in here that reacts to constraints updates // as it will not get called for any applet that reimplements constraintsEvent // without calling the Applet:: version as well, which it shouldn't need to. // INSTEAD put such code into flushPendingConstraintsEvents Q_UNUSED(constraints) //qCDebug(LOG_PLASMA) << constraints << "constraints are FormFactor: " << formFactor() // << ", Location: " << location(); if (d->script) { d->script->constraintsEvent(constraints); } } QString Applet::title() const { if (!d->customTitle.isEmpty()) { return d->customTitle; } if (d->appletDescription.isValid()) { return d->appletDescription.name(); } return i18n("Unknown"); } void Applet::setTitle(const QString &title) { if (title == d->customTitle) { return; } d->customTitle = title; emit titleChanged(title); } QString Applet::icon() const { return d->icon; } void Applet::setIcon(const QString &icon) { if (icon == d->icon) { return; } d->icon = icon; emit iconChanged(icon); } bool Applet::isBusy() const { return d->busy; } void Applet::setBusy(bool busy) { if (busy == d->busy) { return; } d->busy = busy; emit busyChanged(busy); } +Plasma::Types::BackgroundHints Applet::backgroundHints() const +{ + return d->backgroundHints; +} + +void Applet::setBackgroundHints(Plasma::Types::BackgroundHints hint) +{ + if (d->backgroundHints == hint) { + return; + } + + Plasma::Types::BackgroundHints oldeffectiveHints = effectiveBackgroundHints(); + + d->backgroundHints = hint; + emit backgroundHintsChanged(); + + if (oldeffectiveHints != effectiveBackgroundHints()) { + emit effectiveBackgroundHintsChanged(); + } +} + +Plasma::Types::BackgroundHints Applet::effectiveBackgroundHints() const +{ + if (d->userBackgroundHintsInitialized + && (d->backgroundHints & Plasma::Types::ConfigurableBackground)) { + return d->userBackgroundHints; + } else { + return d->backgroundHints; + } +} + +Plasma::Types::BackgroundHints Applet::userBackgroundHints() const +{ + return d->userBackgroundHints; +} + +void Applet::setUserBackgroundHints(Plasma::Types::BackgroundHints hint) +{ + if (d->userBackgroundHints == hint && d->userBackgroundHintsInitialized) { + return; + } + + d->userBackgroundHints = hint; + d->userBackgroundHintsInitialized = true; + QMetaEnum hintEnum = QMetaEnum::fromType(); + config().writeEntry("UserBackgroundHints", hintEnum.valueToKey(d->userBackgroundHints)); + if (containment() && containment()->corona()) { + containment()->corona()->requestConfigSync(); + } + + emit userBackgroundHintsChanged(); + + if (d->backgroundHints & Plasma::Types::ConfigurableBackground) { + emit effectiveBackgroundHintsChanged(); + } +} + + KPluginInfo Applet::pluginInfo() const { return KPluginInfo(d->appletDescription); } KPluginMetaData Applet::pluginMetaData() const { return d->appletDescription; } Types::ImmutabilityType Applet::immutability() const { // if this object is itself system immutable, then just return that; it's the most // restrictive setting possible and will override anything that might be happening above it // in the Corona->Containment->Applet hierarchy if (d->transient || (d->mainConfig && d->mainConfig->isImmutable())) { return Types::SystemImmutable; } //Returning the more strict immutability between the applet immutability, Containment and Corona Types::ImmutabilityType upperImmutability = Types::Mutable; if (isContainment()) { Corona *cor = static_cast(const_cast(this))->corona(); if (cor) { upperImmutability = cor->immutability(); } } else { const Containment *cont = containment(); if (cont) { if (cont->corona()) { upperImmutability = cont->corona()->immutability(); } else { upperImmutability = cont->immutability(); } } } if (upperImmutability != Types::Mutable) { // it's either system or user immutable, and we already check for local system immutability, // so upperImmutability is guaranteed to be as or more severe as this object's immutability return upperImmutability; } else { return d->immutability; } } void Applet::setImmutability(const Types::ImmutabilityType immutable) { if (d->immutability == immutable || immutable == Types::SystemImmutable) { // we do not store system immutability in d->immutability since that gets saved // out to the config file; instead, we check with // the config group itself for this information at all times. this differs from // corona, where SystemImmutability is stored in d->immutability. return; } d->immutability = immutable; updateConstraints(Types::ImmutableConstraint); } QString Applet::launchErrorMessage() const { return d->launchErrorMessage; } bool Applet::failedToLaunch() const { return d->failed; } bool Applet::configurationRequired() const { return d->needsConfig; } QString Applet::configurationRequiredReason() const { return d->configurationRequiredReason; } void Applet::setConfigurationRequired(bool needsConfig, const QString &reason) { if (d->needsConfig == needsConfig && reason == d->configurationRequiredReason) { return; } d->needsConfig = needsConfig; d->configurationRequiredReason = reason; emit configurationRequiredChanged(needsConfig, reason); } bool Applet::isUserConfiguring() const { return d->userConfiguring; } void Applet::setUserConfiguring(bool configuring) { if (configuring == d->userConfiguring) { return; } d->userConfiguring = configuring; emit userConfiguringChanged(configuring); } Types::ItemStatus Applet::status() const { return d->itemStatus; } void Applet::setStatus(const Types::ItemStatus status) { if (status == d->itemStatus) { return; } d->itemStatus = status; emit statusChanged(status); } void Applet::flushPendingConstraintsEvents() { if (d->pendingConstraints == Types::NoConstraint) { return; } if (d->constraintsTimer.isActive()) { d->constraintsTimer.stop(); } //qCDebug(LOG_PLASMA) << "flushing constraints: " << d->pendingConstraints << "!!!!!!!!!!!!!!!!!!!!!!!!!!!"; Plasma::Types::Constraints c = d->pendingConstraints; d->pendingConstraints = Types::NoConstraint; if (c & Plasma::Types::UiReadyConstraint) { d->setUiReady(); } if (c & Plasma::Types::StartupCompletedConstraint) { //common actions bool unlocked = immutability() == Types::Mutable; QAction *closeApplet = d->actions->action(QStringLiteral("remove")); if (closeApplet) { closeApplet->setEnabled(unlocked); closeApplet->setVisible(unlocked); connect(closeApplet, SIGNAL(triggered(bool)), this, SLOT(askDestroy()), Qt::UniqueConnection); } QAction *configAction = d->actions->action(QStringLiteral("configure")); if (configAction) { if (d->hasConfigurationInterface) { bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); configAction->setVisible(canConfig); configAction->setEnabled(canConfig); } } QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { connect(runAssociatedApplication, &QAction::triggered, this, &Applet::runAssociatedApplication, Qt::UniqueConnection); } d->updateShortcuts(); } if (c & Plasma::Types::ImmutableConstraint) { bool unlocked = immutability() == Types::Mutable; QAction *action = d->actions->action(QStringLiteral("remove")); if (action) { action->setVisible(unlocked); action->setEnabled(unlocked); } action = d->actions->action(QStringLiteral("configure")); if (action && d->hasConfigurationInterface) { bool canConfig = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); action->setVisible(canConfig); action->setEnabled(canConfig); } //an immutable constraint will always happen at startup //make sure don't emit a change signal for nothing if (d->oldImmutability != immutability()) { emit immutabilityChanged(immutability()); } d->oldImmutability = immutability(); } // now take care of constraints in special subclass: Containment Containment *containment = qobject_cast(this); if (containment) { containment->d->containmentConstraintsEvent(c); } // pass the constraint on to the actual subclass constraintsEvent(c); if (c & Types::StartupCompletedConstraint) { // start up is done, we can now go do a mod timer if (d->modificationsTimer) { if (d->modificationsTimer->isActive()) { d->modificationsTimer->stop(); } } else { d->modificationsTimer = new QBasicTimer; } } if (c & Plasma::Types::FormFactorConstraint) { emit formFactorChanged(formFactor()); } if (c & Plasma::Types::LocationConstraint) { emit locationChanged(location()); } } QList Applet::contextualActions() { //qCDebug(LOG_PLASMA) << "empty context actions"; return d->script ? d->script->contextualActions() : QList(); } KActionCollection *Applet::actions() const { return d->actions; } Types::FormFactor Applet::formFactor() const { Containment *c = containment(); QObject *pw = qobject_cast(parent()); Plasma::Applet *parentApplet = qobject_cast(pw); //assumption: this loop is usually is -really- short or doesn't run at all while (!parentApplet && pw && pw->parent()) { pw = pw->parent(); parentApplet = qobject_cast(pw); } return c ? c->d->formFactor : Plasma::Types::Planar; } Containment *Applet::containment() const { Containment *c = qobject_cast(const_cast(this)); if (c && c->isContainment()) { return c; } else { c = nullptr; } QObject *parent = this->parent(); while (parent) { Containment *possibleC = qobject_cast(parent); if (possibleC && possibleC->isContainment()) { c = possibleC; break; } parent = parent->parent(); } return c; } void Applet::setGlobalShortcut(const QKeySequence &shortcut) { if (!d->activationAction) { d->activationAction = new QAction(this); d->activationAction->setText(i18n("Activate %1 Widget", title())); d->activationAction->setObjectName(QStringLiteral("activate widget %1").arg(id())); // NO I18N connect(d->activationAction, &QAction::triggered, this, &Applet::activated); connect(d->activationAction, SIGNAL(changed()), this, SLOT(globalShortcutChanged())); } else if (d->activationAction->shortcut() == shortcut) { return; } d->activationAction->setShortcut(shortcut); d->globalShortcutEnabled = true; QList seqs; seqs << shortcut; KGlobalAccel::self()->setShortcut(d->activationAction, seqs, KGlobalAccel::NoAutoloading); d->globalShortcutChanged(); } QKeySequence Applet::globalShortcut() const { if (d->activationAction) { QList shortcuts = KGlobalAccel::self()->shortcut(d->activationAction); if (!shortcuts.isEmpty()) { return shortcuts.first(); } } return QKeySequence(); } Types::Location Applet::location() const { Containment *c = containment(); return c ? c->d->location : Plasma::Types::Desktop; } bool Applet::hasConfigurationInterface() const { return d->hasConfigurationInterface; } void Applet::setHasConfigurationInterface(bool hasInterface) { if (hasInterface == d->hasConfigurationInterface) { return; } QAction *configAction = d->actions->action(QStringLiteral("configure")); if (configAction) { bool enable = hasInterface; if (enable) { const bool unlocked = immutability() == Types::Mutable; enable = unlocked || KAuthorized::authorize(QStringLiteral("plasma/allow_configure_when_locked")); } configAction->setEnabled(enable); } d->hasConfigurationInterface = hasInterface; } void Applet::configChanged() { if (d->script) { if (d->configLoader) { d->configLoader->load(); } d->script->configChanged(); } } void Applet::setAssociatedApplication(const QString &string) { AssociatedApplicationManager::self()->setApplication(this, string); QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); runAssociatedApplication->setVisible(valid); runAssociatedApplication->setEnabled(valid); } } void Applet::setAssociatedApplicationUrls(const QList &urls) { AssociatedApplicationManager::self()->setUrls(this, urls); QAction *runAssociatedApplication = d->actions->action(QStringLiteral("run associated application")); if (runAssociatedApplication) { bool valid = AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); runAssociatedApplication->setVisible(valid); runAssociatedApplication->setEnabled(valid); } } QString Applet::associatedApplication() const { return AssociatedApplicationManager::self()->application(this); } QList Applet::associatedApplicationUrls() const { return AssociatedApplicationManager::self()->urls(this); } void Applet::runAssociatedApplication() { AssociatedApplicationManager::self()->run(this); } bool Applet::hasValidAssociatedApplication() const { return AssociatedApplicationManager::self()->appletHasValidAssociatedApplication(this); } Applet *Applet::loadPlasmoid(const QString &path, uint appletId) { const KPluginMetaData md = appletMetadataForDirectory(path); if (md.isValid()) { QStringList types = md.serviceTypes(); if (types.contains(QLatin1String("Plasma/Containment"))) { return new Containment(md, appletId); } else { return new Applet(md, nullptr, appletId); } } return nullptr; } void Applet::timerEvent(QTimerEvent *event) { if (d->transient) { d->constraintsTimer.stop(); if (d->modificationsTimer) { d->modificationsTimer->stop(); } return; } if (event->timerId() == d->constraintsTimer.timerId()) { d->constraintsTimer.stop(); // Don't flushPendingConstraints if we're just starting up // flushPendingConstraints will be called by Corona if (!(d->pendingConstraints & Plasma::Types::StartupCompletedConstraint)) { flushPendingConstraintsEvents(); } } else if (d->modificationsTimer && event->timerId() == d->modificationsTimer->timerId()) { d->modificationsTimer->stop(); // invalid group, will result in save using the default group KConfigGroup cg; save(cg); emit configNeedsSaving(); } } bool Applet::isContainment() const { //HACK: this is a special case for the systray //containment in an applet that is not a containment Applet *pa = qobject_cast(parent()); if (pa && !pa->isContainment()) { return true; } //normal "acting as a containment" condition return qobject_cast(this) && qobject_cast(parent()); } } // Plasma namespace #include "moc_applet.cpp" diff --git a/src/plasma/applet.h b/src/plasma/applet.h index 0d4ab0f86..3cf310b22 100644 --- a/src/plasma/applet.h +++ b/src/plasma/applet.h @@ -1,736 +1,802 @@ /* * Copyright 2006-2007 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * Copyright 2008 by Ménard Alexis * 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 PLASMA_APPLET_H #define PLASMA_APPLET_H #include #include #include #include #include #include #include #include #include class KActionCollection; class KConfigLoader; namespace Plasma { class AppletPrivate; class Containment; class DataEngine; class Package; /** * @class Applet plasma/applet.h * * @short The base Applet class * * Applet provides several important roles for add-ons widgets in Plasma. * * First, it is the base class for the plugin system and therefore is the * interface to applets for host applications. It also handles the life time * management of data engines (e.g. all data engines accessed via * Applet::dataEngine(const QString&) are properly deref'd on Applet * destruction), background painting (allowing for consistent and complex * look and feel in just one line of code for applets), loading and starting * of scripting support for each applet, providing access to the associated * plasmoid package (if any) and access to configuration data. * * See techbase.kde.org for tutorials on writing Applets using this class. */ class PLASMA_EXPORT Applet : public QObject { Q_OBJECT Q_PROPERTY(Plasma::Types::ItemStatus status READ status WRITE setStatus NOTIFY statusChanged) Q_PROPERTY(Plasma::Types::ImmutabilityType immutability READ immutability WRITE setImmutability NOTIFY immutabilityChanged) Q_PROPERTY(Plasma::Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged) Q_PROPERTY(Plasma::Types::Location location READ location NOTIFY locationChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged FINAL) Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged FINAL) Q_PROPERTY(bool busy READ isBusy WRITE setBusy NOTIFY busyChanged FINAL) + /** + * How the applet wants its background to be drawn. The containment may chose to ignore this hint. + */ + Q_PROPERTY(Plasma::Types::BackgroundHints backgroundHints WRITE setBackgroundHints READ backgroundHints NOTIFY backgroundHintsChanged FINAL) + + /** + * The containment (and/or the user) may decide to use another kind of background instead (if supported by the applet) + */ + Q_PROPERTY(Plasma::Types::BackgroundHints userBackgroundHints WRITE setUserBackgroundHints READ userBackgroundHints NOTIFY userBackgroundHintsChanged FINAL) + + /** + * The effective background hints the applet has, internally decided how to mix with userBackgroundHints + */ + Q_PROPERTY(Plasma::Types::BackgroundHints effectiveBackgroundHints READ effectiveBackgroundHints NOTIFY effectiveBackgroundHintsChanged FINAL) + public: //CONSTRUCTORS /** * @param parent the QObject this applet is parented to * @param serviceId the name of the .desktop file containing the * information about the widget * @param appletId a unique id used to differentiate between multiple * instances of the same Applet type */ explicit Applet(QObject *parent = nullptr, const QString &serviceId = QString(), uint appletId = 0); #if PLASMA_ENABLE_DEPRECATED_SINCE(5, 28) /** * @param parent the QObject this applet is parented to * @param info the plugin information object for this Applet * @param appletId a unique id used to differentiate between multiple * instances of the same Applet type * @since 4.6 * * @deprecated Since 5.28, prefer using KPluginMetaData */ PLASMA_DEPRECATED_VERSION(5, 28, "Use Applet(const KPluginMetaData &, QObject *, uint") explicit Applet(const KPluginInfo &info, QObject *parent = nullptr, uint appletId = 0); #endif /** * @param parent the QObject this applet is parented to * @param metadata the plugin information object for this Applet * @param appletId a unique id used to differentiate between multiple * instances of the same Applet type * @since 5.27 */ explicit Applet(const KPluginMetaData &metadata, QObject *parent = nullptr, uint appletId = 0); ~Applet() override; //BOOKKEEPING /** * @return the id of this applet */ uint id() const; /** * @return The type of immutability of this applet */ Types::ImmutabilityType immutability() const; /** * If for some reason, the applet fails to get up on its feet (the * library couldn't be loaded, necessary hardware support wasn't found, * etc..) this method returns the reason why, in an user-readable way. * @since 5.0 **/ QString launchErrorMessage() const; /** * If for some reason, the applet fails to get up on its feet (the * library couldn't be loaded, necessary hardware support wasn't found, * etc..) this method returns true. **/ bool failedToLaunch() const; /** * @return true if destroy() was called; useful for Applets which should avoid * certain tasks if they are about to be deleted permanently */ bool destroyed() const; /** * @return the Containment, if any, this applet belongs to **/ Containment *containment() const; /** * @return true if this Applet is currently being used as a Containment, false otherwise */ bool isContainment() const; /** * @return the status of the applet * @since 4.4 */ Types::ItemStatus status() const; /** * Returns the current form factor the applet is being displayed in. * * @see Plasma::FormFactor */ Types::FormFactor formFactor() const; /** * Returns the location of the scene which is displaying applet. * * @see Plasma::Types::Location */ Types::Location location() const; //CONFIGURATION /** * Returns the KConfigGroup to access the applets configuration. * * This config object will write to an instance * specific config file named \\rc * in the Plasma appdata directory. **/ KConfigGroup config() const; /** * Returns a KConfigGroup object to be shared by all applets of this * type. * * This config object will write to an applet-specific config object * named plasma_\rc in the local config directory. */ KConfigGroup globalConfig() const; /** * Returns the config skeleton object from this applet's package, * if any. * * @return config skeleton object, or 0 if none **/ KConfigLoader *configScheme() const; /** * Saves state information about this applet that will * be accessed when next instantiated in the restore(KConfigGroup&) method. * * This method does not need to be reimplmented by Applet * subclasses, but can be useful for Applet specializations * (such as Containment) to do so. * * Applet subclasses may instead want to reimplement saveState(). **/ virtual void save(KConfigGroup &group) const; /** * Restores state information about this applet saved previously * in save(KConfigGroup&). * * This method does not need to be reimplmented by Applet * subclasses, but can be useful for Applet specializations * (such as Containment) to do so. **/ virtual void restore(KConfigGroup &group); /** * @return true if the applet currently needs to be configured, * otherwise, false */ bool configurationRequired() const; /** * @return A translated message for the user explaining that the * applet needs configuring; this should note what needs * to be configured * * @see setConfigurationRequired * @since 5.20 */ QString configurationRequiredReason() const; /** * @return true when the configuration interface is being shown * @since 4.5 */ bool isUserConfiguring() const; /** * Tells the applet the user is configuring * @param configuring true if the configuration ui is showing */ void setUserConfiguring(bool configuring); //UTILS #if PLASMA_ENABLE_DEPRECATED_SINCE(5, 6) /** * Accessor for the associated Package object if any. * Generally, only Plasmoids come in a Package. * * @return the Package object, or an invalid one if none * @deprecated Since 5.6, use kPackage() instead **/ PLASMA_DEPRECATED_VERSION(5, 6, "Use Applet::kPackage()") Package package() const; #endif /** * Accessor for the associated Package object if any. * Generally, only Plasmoids come in a Package. * * @return the Package object, or an invalid one if none * @since 5.6 **/ KPackage::Package kPackage() const; /** * Called when any of the geometry constraints have been updated. * This method calls constraintsEvent, which may be reimplemented, * once the Applet has been prepared for updating the constraints. * * @param constraints the type of constraints that were updated */ void updateConstraints(Plasma::Types::Constraints constraints = Plasma::Types::AllConstraints); //METADATA #if PLASMA_ENABLE_DEPRECATED_SINCE(5, 28) /** * @return metadata information about this plugin * @see KPluginInfo, pluginMetaData * @since 5.0 * @deprecated Since 5.28, use pluginMetaData instead */ PLASMA_DEPRECATED_VERSION(5, 28, "Use Applet::pluginMetaData()") KPluginInfo pluginInfo() const; #endif /** * @return metadata information about this plugin * * @since 5.27 */ KPluginMetaData pluginMetaData() const; /** * Returns the user-visible title for the applet, as specified in the * Name field of the .desktop file. Can be changed with @see setTitle * * @since 5.0 * @return the user-visible title for the applet. **/ QString title() const; /** * Sets a custom title for this instance of the applet. E.g. a clock might * use the timezone as its name rather than the .desktop file * * @since 5.0 * @param title the user-visible title for the applet. */ void setTitle(const QString &title); #if PLASMA_ENABLE_DEPRECATED_SINCE(5, 19) /** * Attempts to load an applet from a package * * Returns a pointer to the applet if successful. * The caller takes responsibility for the applet, including * deleting it when no longer needed. * If you instance a plasmoid with this deprecated API, the * automated default setup scripts won't be executed for that plasmoid * * @param path the path to the package * @param appletId unique ID to assign the applet, or zero to have one * assigned automatically. * @return a pointer to the loaded applet, or 0 on load failure * @since 4.3 * * @deprecated Since 5.19, use Containment::createApplet() instead, you are not * supposed to have applets without containments **/ PLASMA_DEPRECATED_VERSION(5, 19, "Use Containment::createApplet(...)") static Applet *loadPlasmoid(const QString &path, uint appletId = 0); #endif /** * @returns The icon name related to this applet * By default is the one in the plasmoid desktop file **/ QString icon() const; /** * Sets an icon name for this applet * @param icon Freedesktop compatible icon name */ void setIcon(const QString &icon); /** * @returns true if the applet should show a busy status, for instance doing * some network operation * @since 5.21 */ bool isBusy() const; /** * Sets the Applet to have a busy status hint, for instance the applet doing * some network operation. * The graphical representation of the busy status depends completely from * the visualization. * @param busy true if the applet is busy * @since 5.21 */ void setBusy(bool busy); + /** + * How the applet wants its background to be drawn. The containment may chose to ignore this hint. + * @since 5.65 + */ + Plasma::Types::BackgroundHints backgroundHints() const; + + /** + * Sets the applet background hints. Only Applet implementations should write this property + * @since 5.65 + */ + void setBackgroundHints(Plasma::Types::BackgroundHints hint); + + /** + * The containment (and/or the user) may decide to use another kind of background instead if supported by the applet. + * In order for an applet to support user configuration of the + * background, it needs to have the Plasma::Types::ConfigurableBackground flag set in its backgroundHints + * @since 5.65 + */ + Plasma::Types::BackgroundHints userBackgroundHints() const; + + /** + * Sets the hints the user wished the background style for the applet to be. + * @since 5.65 + */ + void setUserBackgroundHints(Plasma::Types::BackgroundHints hint); + + /** + * The effective background hints the applet will have: it will follow userBackgroundHints only if backgroundHints has the Plasma::Types::ConfigurableBackground flag set + * @since 5.65 + */ + Plasma::Types::BackgroundHints effectiveBackgroundHints() const; + //ACTIONS /** * Returns a list of context-related QAction instances. * * This is used e.g. within the \a DesktopView to display a * contextmenu. * * @return A list of actions. The default implementation returns an * empty list. **/ virtual QList contextualActions(); /** * Returns the collection of actions for this Applet */ KActionCollection *actions() const; /** * Sets the global shortcut to associate with this widget. */ void setGlobalShortcut(const QKeySequence &shortcut); /** * @return the global shortcut associated with this widget, or * an empty shortcut if no global shortcut is associated. */ QKeySequence globalShortcut() const; // ASSOCIATED APPLICATION /** * Sets an application associated to this applet, that will be * regarded as a full view of what is represented in the applet * * @param string the name of the application. it can be * \li a name understood by KService::serviceByDesktopName * (e.g. "konqueror") * \li a command in $PATH * \li or an absolute path to an executable * @since 4.4 */ void setAssociatedApplication(const QString &string); /** * Sets a list of urls associated to this application, * they will be used as parameters for the associated application * @see setAssociatedApplication() * * @param urls */ void setAssociatedApplicationUrls(const QList &urls); /** * @return the application associated to this applet * @since 4.4 */ QString associatedApplication() const; /** * @return the urls associated to this applet * @since 4.4 */ QList associatedApplicationUrls() const; /** * @return true if the applet has a valid associated application or urls * @since 4.4 */ bool hasValidAssociatedApplication() const; //Completely UI-specific, remove or move to scriptengine /** * @return true if this plasmoid provides a GUI configuration **/ bool hasConfigurationInterface() const; Q_SIGNALS: //BOOKEEPING /** * Emitted when the immutability changes * @since 4.4 */ void immutabilityChanged(Plasma::Types::ImmutabilityType immutable); /** * Emitted when the applet status changes * @since 4.4 */ void statusChanged(Plasma::Types::ItemStatus status); /** * Emitted when the applet has been scheduled for destruction * or the destruction has been undone * @since 5.4 */ void destroyedChanged(bool destroyed); /** * Emitted when the title has changed * @since 5.20 */ void titleChanged(const QString &title); /** * Emitted when the icon name for the applet has changed * @since 5.20 */ void iconChanged(const QString &icon); /** * Emitted when the busy status has changed * @since 5.21 */ void busyChanged(bool busy); + /** + * Emitted when the background hints have changed + * @since 5.65 + */ + void backgroundHintsChanged(); + + /** + * Emitted when the user background hints have changed + * @since 5.65 + */ + void userBackgroundHintsChanged(); + + /** + * Emitted when the effective background hints have changed + * @since 5.65 + */ + void effectiveBackgroundHintsChanged(); + //CONFIGURATION /** * Emitted when an applet has changed values in its configuration * and wishes for them to be saved at the next save point. As this implies * disk activity, this signal should be used with care. * * @note This does not need to be emitted from saveState by individual * applets. */ void configNeedsSaving(); /** * emitted when the config ui appears or disappears */ void userConfiguringChanged(bool configuring); //ACTIONS /** * Emitted just before the contextual actions are about to show * For instance just before the context menu containing the actions * added with setAction() is shown */ void contextualActionsAboutToShow(); /** * Emitted when activation is requested due to, for example, a global * keyboard shortcut. By default the widget is given focus. */ void activated(); //TODO: fix usage in containment, port to QObject::destroyed /** * Emitted when the applet is deleted */ void appletDeleted(Plasma::Applet *applet); /** * Emitted when the formfactor changes */ void formFactorChanged(Plasma::Types::FormFactor formFactor); /** * Emitted when the location changes */ void locationChanged(Plasma::Types::Location location); /** * Emitted when setConfigurationRequired was called * @see setConfigurationRequired * @since 5.20 */ void configurationRequiredChanged(bool needsConfig, const QString &reason); public Q_SLOTS: //BOOKKEEPING /** * Call this method when the applet fails to launch properly. An * optional reason can be provided. * * Not that all children items will be deleted when this method is * called. If you have pointers to these items, you will need to * reset them after calling this method. * * @param failed true when the applet failed, false when it succeeded * @param reason an optional reason to show the user why the applet * failed to launch * @since 5.0 **/ void setLaunchErrorMessage(const QString &reason = QString()); /** * Sets the immutability type for this applet (not immutable, * user immutable or system immutable) * @param immutable the new immutability type of this applet */ void setImmutability(const Types::ImmutabilityType immutable); /** * Destroys the applet; it will be removed nicely and deleted. * Its configuration will also be deleted. * If you want to remove the Applet configuration, use this, don't just delete the Applet * */ void destroy(); /** * sets the status for this applet * @since 4.4 */ void setStatus(const Types::ItemStatus stat); //CONFIGURATION /** * Called when applet configuration values have changed. */ + //TODO KF6: make it not a slot anymore and protected virtual void configChanged(); //UTILS /** * Sends all pending constraints updates to the applet. Will usually * be called automatically, but can also be called manually if needed. */ void flushPendingConstraintsEvents(); /** * This method is called once the applet is loaded and added to a Corona. * If the applet requires a Scene or has an particularly intensive * set of initialization routines to go through, consider implementing it * in this method instead of the constructor. * * Note: paintInterface may get called before init() depending on initialization * order. Painting is managed by the canvas (QGraphisScene), and may schedule a * paint event prior to init() being called. **/ virtual void init(); //ASSOCIATED APPLICATION /** * Open the application associated to this applet, if it's not set * but some urls are, open those urls with the proper application * for their mimetype * @see setAssociatedApplication() * @see setAssociatedApplicationUrls() * @since 4.4 */ void runAssociatedApplication(); protected: //CONSTRUCTORS /** * This constructor is to be used with the plugin loading systems * found in KPluginInfo and KService. The argument list is expected * to have two elements: the KService service ID for the desktop entry * and an applet ID which must be a base 10 number. * * @param parent a QObject parent; you probably want to pass in 0 * @param args a list of strings containing two entries: the service id * and the applet id */ Applet(QObject *parent, const QVariantList &args); //CONFIGURATION /** * When called, the Applet should write any information needed as part * of the Applet's running state to the configuration object in config() * and/or globalConfig(). * * Applets that always sync their settings/state with the config * objects when these settings/states change do not need to reimplement * this method. **/ virtual void saveState(KConfigGroup &config) const; /** * Sets whether or not this applet provides a user interface for * configuring the applet. * * It defaults to false, and if true is passed in you should * also reimplement createConfigurationInterface() * * @param hasInterface whether or not there is a user interface available **/ void setHasConfigurationInterface(bool hasInterface); /** * When the applet needs to be configured before being usable, this * method can be called to show a standard interface prompting the user * to configure the applet * * @param needsConfiguring true if the applet needs to be configured, * or false if it doesn't * @param reason a translated message for the user explaining that the * applet needs configuring; this should note what needs * to be configured */ void setConfigurationRequired(bool needsConfiguring, const QString &reason = QString()); //UTILS /** * Called when any of the constraints for the applet have been updated. These constraints * range from notifying when the applet has officially "started up" to when geometry changes * to when the form factor changes. * * Each constraint that has been changed is passed in the constraints flag. * All of the constraints and how they work is documented in the @see Plasma::Constraints * enumeration. * * On applet creation, this is always called prior to painting and can be used as an * opportunity to layout the widget, calculate sizings, etc. * * Do not call update() from this method; an update() will be triggered * at the appropriate time for the applet. * * @param constraints the type of constraints that were updated * @property constraint */ virtual void constraintsEvent(Plasma::Types::Constraints constraints); //TODO: timerEvent should go into AppletPrivate /** * Reimplemented from QObject */ void timerEvent(QTimerEvent *event) override; private: /** * @internal This constructor is to be used with the Package loading system. * * @param parent a QObject parent; you probably want to pass in 0 * @param args a list of strings containing two entries: the service id * and the applet id * @since 4.3 */ Applet(const QString &packagePath, uint appletId); Q_PRIVATE_SLOT(d, void cleanUpAndDelete()) Q_PRIVATE_SLOT(d, void askDestroy()) Q_PRIVATE_SLOT(d, void updateShortcuts()) Q_PRIVATE_SLOT(d, void globalShortcutChanged()) Q_PRIVATE_SLOT(d, void propagateConfigChanged()) Q_PRIVATE_SLOT(d, void requestConfiguration()) AppletPrivate *const d; //Corona needs to access setLaunchErrorMessage and init friend class Corona; friend class CoronaPrivate; friend class Containment; friend class ContainmentPrivate; friend class AppletScript; friend class AppletPrivate; friend class AccessAppletJobPrivate; friend class GraphicsViewAppletPrivate; friend class PluginLoader; friend class AssociatedApplicationManager; }; } // Plasma namespace /** * Register an applet when it is contained in a loadable module */ #define K_EXPORT_PLASMA_APPLET(libname, classname) \ K_PLUGIN_FACTORY(factory, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) #define K_EXPORT_PLASMA_APPLET_WITH_JSON(libname, classname, jsonFile) \ K_PLUGIN_FACTORY_WITH_JSON(factory, jsonFile, registerPlugin();) \ K_EXPORT_PLUGIN_VERSION(PLASMA_VERSION) #endif // multiple inclusion guard diff --git a/src/plasma/private/applet_p.h b/src/plasma/private/applet_p.h index 8c4221cf7..8514d0659 100644 --- a/src/plasma/private/applet_p.h +++ b/src/plasma/private/applet_p.h @@ -1,127 +1,131 @@ /* * Copyright 2005 by Aaron Seigo * Copyright 2007 by Riccardo Iaconelli * Copyright 2008 by Ménard Alexis * * 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 PLASMA_APPLET_P_H #define PLASMA_APPLET_P_H #include #include #include #include #include #include #include "plasma/applet.h" class KKeySequenceWidget; namespace Plasma { class AppletScript; class Service; class AppletPrivate { public: AppletPrivate(const KPluginMetaData &info, int uniqueID, Applet *applet); virtual ~AppletPrivate(); void init(const QString &packagePath = QString(), const QVariantList &args = QVariantList()); void setDestroyed(bool destroyed); void askDestroy(); virtual void cleanUpAndDelete(); // put all setup routines for script here. at this point we can assume that // package exists and that we have a script engin void setupPackage(); void setupScripting(); QString globalName() const; void scheduleConstraintsUpdate(Plasma::Types::Constraints c); void scheduleModificationNotification(); KConfigGroup *mainConfigGroup(); void resetConfigurationObject(); void updateShortcuts(); void globalShortcutChanged(); void propagateConfigChanged(); void setUiReady(); static KActionCollection *defaultActions(QObject *parent); void requestConfiguration(); static uint s_maxAppletId; uint appletId; Applet *q; // applet attributes Types::ImmutabilityType immutability; Types::ImmutabilityType oldImmutability; QString launchErrorMessage; // applet info we keep around in case its needed KPluginMetaData appletDescription; QString customTitle; QString icon; // bookkeeping KConfigGroup *mainConfig; Plasma::Types::Constraints pendingConstraints; // scripting and package stuff AppletScript *script; KPackage::Package package; KConfigLoader *configLoader; // actions stuff; put activationAction into actions? KActionCollection *actions; QAction *activationAction; Types::ItemStatus itemStatus; // timerEvent bookkeeping QBasicTimer constraintsTimer; QBasicTimer *modificationsTimer; QPointer deleteNotification; QTimer *deleteNotificationTimer; QString configurationRequiredReason; + Types::BackgroundHints backgroundHints = Types::DefaultBackground; + Types::BackgroundHints userBackgroundHints = Types::DefaultBackground; + // a great green field of booleans :) + bool userBackgroundHintsInitialized = false; bool hasConfigurationInterface : 1; bool failed : 1; bool transient : 1; bool needsConfig : 1; bool started : 1; bool globalShortcutEnabled : 1; bool userConfiguring : 1; bool busy : 1; }; } // Plasma namespace #endif diff --git a/src/scriptengines/qml/plasmoid/appletinterface.cpp b/src/scriptengines/qml/plasmoid/appletinterface.cpp index 989c8fbd9..c2974748f 100644 --- a/src/scriptengines/qml/plasmoid/appletinterface.cpp +++ b/src/scriptengines/qml/plasmoid/appletinterface.cpp @@ -1,924 +1,884 @@ /* * Copyright 2008-2013 Aaron Seigo * Copyright 2010-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 "appletinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "containmentinterface.h" #include "wallpaperinterface.h" #include #include Q_DECLARE_METATYPE(AppletInterface *) AppletInterface::AppletInterface(DeclarativeAppletScript *script, const QVariantList &args, QQuickItem *parent) : AppletQuickItem(script->applet(), parent), m_configuration(nullptr), m_appletScriptEngine(script), m_toolTipTextFormat(0), m_toolTipItem(nullptr), m_args(args), - m_backgroundHints(Plasma::Types::StandardBackground), - m_userBackgroundHints(Plasma::Types::StandardBackground), m_hideOnDeactivate(true), m_oldKeyboardShortcut(0), m_dummyNativeInterface(nullptr), m_positionBeforeRemoval(QPointF(-1, -1)) { qmlRegisterType(); connect(this, &AppletInterface::configNeedsSaving, applet(), &Plasma::Applet::configNeedsSaving); connect(applet(), &Plasma::Applet::immutabilityChanged, this, &AppletInterface::immutabilityChanged); connect(applet(), &Plasma::Applet::userConfiguringChanged, this, &AppletInterface::userConfiguringChanged); connect(applet(), &Plasma::Applet::contextualActionsAboutToShow, this, &AppletInterface::contextualActionsAboutToShow); connect(applet(), &Plasma::Applet::statusChanged, this, &AppletInterface::statusChanged); connect(applet(), &Plasma::Applet::destroyedChanged, this, &AppletInterface::destroyedChanged); connect(applet(), &Plasma::Applet::titleChanged, this, &AppletInterface::titleChanged); connect(applet(), &Plasma::Applet::titleChanged, this, [this]() { if (m_toolTipMainText.isNull()) { emit toolTipMainTextChanged(); } }); connect(applet(), &Plasma::Applet::iconChanged, this, &AppletInterface::iconChanged); connect(applet(), &Plasma::Applet::busyChanged, this, &AppletInterface::busyChanged); + connect(applet(), &Plasma::Applet::backgroundHintsChanged, + this, &AppletInterface::backgroundHintsChanged); + connect(applet(), &Plasma::Applet::effectiveBackgroundHintsChanged, + this, &AppletInterface::effectiveBackgroundHintsChanged); + connect(applet(), &Plasma::Applet::userBackgroundHintsChanged, + this, &AppletInterface::userBackgroundHintsChanged); + connect(applet(), &Plasma::Applet::configurationRequiredChanged, this, [this](bool configurationRequired, const QString &reason) { Q_UNUSED(configurationRequired); Q_UNUSED(reason); emit configurationRequiredChanged(); emit configurationRequiredReasonChanged(); }); connect(applet(), &Plasma::Applet::activated, this, &AppletInterface::activated); connect(appletScript(), &DeclarativeAppletScript::formFactorChanged, this, &AppletInterface::formFactorChanged); connect(appletScript(), &DeclarativeAppletScript::locationChanged, this, &AppletInterface::locationChanged); connect(appletScript(), &DeclarativeAppletScript::contextChanged, this, &AppletInterface::contextChanged); if (applet()->containment()) { connect(applet()->containment(), &Plasma::Containment::screenChanged, this, &AppletInterface::screenChanged); // Screen change implies geo change for good measure. connect(applet()->containment(), &Plasma::Containment::screenChanged, this, &AppletInterface::screenGeometryChanged); connect(applet()->containment()->corona(), &Plasma::Corona::screenGeometryChanged, this, [this](int id) { if (id == applet()->containment()->screen()) { emit screenGeometryChanged(); } }); connect(applet()->containment()->corona(), &Plasma::Corona::availableScreenRegionChanged, this, &ContainmentInterface::availableScreenRegionChanged); connect(applet()->containment()->corona(), &Plasma::Corona::availableScreenRectChanged, this, &ContainmentInterface::availableScreenRectChanged); } connect(this, &AppletInterface::expandedChanged, [=](bool expanded) { //if both compactRepresentationItem and fullRepresentationItem exist, //the applet is in a popup if (expanded) { if (compactRepresentationItem() && fullRepresentationItem() && fullRepresentationItem()->window() && compactRepresentationItem()->window() && fullRepresentationItem()->window() != compactRepresentationItem()->window() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->installEventFilter(this); } else if (fullRepresentationItem() && fullRepresentationItem()->parentItem()) { fullRepresentationItem()->parentItem()->removeEventFilter(this); } } }); } AppletInterface::~AppletInterface() { } DeclarativeAppletScript *AppletInterface::appletScript() const { return m_appletScriptEngine; } void AppletInterface::init() { if (qmlObject()->rootObject() && m_configuration) { return; } m_configuration = new KDeclarative::ConfigPropertyMap(applet()->configScheme(), this); AppletQuickItem::init(); geometryChanged(QRectF(), QRectF(x(), y(), width(), height())); emit busyChanged(); updateUiReadyConstraint(); connect(this, &AppletInterface::isLoadingChanged, this, &AppletInterface::updateUiReadyConstraint); connect(applet(), &Plasma::Applet::activated, this, [ = ]() { // in case the applet doesn't want to get shrunk on reactivation, // we always expand it again (only in order to conform with legacy behaviour) bool activate = !( isExpanded() && isActivationTogglesExpanded() ); setExpanded(activate); if (activate) { if (QQuickItem *i = qobject_cast(fullRepresentationItem())) { // Bug 372476: never pull focus away from it, only setFocus(true) i->setFocus(true, Qt::ShortcutFocusReason); } } }); if (m_args.count() == 1) { emit externalData(QString(), m_args.first()); } else if (!m_args.isEmpty()) { emit externalData(QString(), m_args); } - - QByteArray hintsString = applet()->config().readEntry("UserBackgroundHints", QString()).toUtf8(); - QMetaEnum hintEnum = QMetaEnum::fromType(); - bool ok; - int value = hintEnum.keyToValue(hintsString.constData(), &ok); - if (ok) { - m_userBackgroundHints = Plasma::Types::BackgroundHints(value); - m_userBackgroundHintsInitialized = true; - emit userBackgroundHintsChanged(); - if (m_backgroundHints & Plasma::Types::ConfigurableBackground) { - emit effectiveBackgroundHintsChanged(); - } - } } void AppletInterface::destroyedChanged(bool destroyed) { //if an item loses its scene before losing the focus, will never //be able to gain focus again if (destroyed && window() && window()->activeFocusItem()) { QQuickItem *focus = window()->activeFocusItem(); QQuickItem *candidate = focus; bool isAncestor = false; //search if the current focus item is a child or grandchild of the applet while (candidate) { if (candidate == this) { isAncestor = true; break; } candidate = candidate->parentItem(); } if (isAncestor) { //Found? remove focus for the whole hierarchy candidate = focus; while (candidate && candidate != this) { candidate->setFocus(false); candidate = candidate->parentItem(); } } } setVisible(!destroyed); } Plasma::Types::FormFactor AppletInterface::formFactor() const { return applet()->formFactor(); } Plasma::Types::Location AppletInterface::location() const { return applet()->location(); } QString AppletInterface::currentActivity() const { if (applet()->containment()) { return applet()->containment()->activity(); } else { return QString(); } } QObject *AppletInterface::configuration() const { return m_configuration; } uint AppletInterface::id() const { return applet()->id(); } QString AppletInterface::pluginName() const { return applet()->pluginMetaData().isValid() ? applet()->pluginMetaData().pluginId() : QString(); } QString AppletInterface::icon() const { return applet()->icon(); } void AppletInterface::setIcon(const QString &icon) { if (applet()->icon() == icon) { return; } applet()->setIcon(icon); } QString AppletInterface::title() const { return applet()->title(); } void AppletInterface::setTitle(const QString &title) { if (applet()->title() == title) { return; } applet()->setTitle(title); } QString AppletInterface::toolTipMainText() const { if (m_toolTipMainText.isNull()) { return title(); } else { return m_toolTipMainText; } } void AppletInterface::setToolTipMainText(const QString &text) { //Here we are abusing the difference between a null and an empty string. //by default is null so falls back to the name //the fist time it gets set, an empty non null one is set, and won't fallback anymore if (!m_toolTipMainText.isNull() && m_toolTipMainText == text) { return; } if (text.isEmpty()) { m_toolTipMainText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipMainText = text; } emit toolTipMainTextChanged(); } QString AppletInterface::toolTipSubText() const { if (m_toolTipSubText.isNull() && applet()->pluginMetaData().isValid()) { return applet()->pluginMetaData().description(); } else { return m_toolTipSubText; } } void AppletInterface::setToolTipSubText(const QString &text) { //Also there the difference between null and empty gets exploited if (!m_toolTipSubText.isNull() && m_toolTipSubText == text) { return; } if (text.isEmpty()) { m_toolTipSubText = QStringLiteral("");//this "" makes it non-null } else { m_toolTipSubText = text; } emit toolTipSubTextChanged(); } int AppletInterface::toolTipTextFormat() const { return m_toolTipTextFormat; } void AppletInterface::setToolTipTextFormat(int format) { if (m_toolTipTextFormat == format) { return; } m_toolTipTextFormat = format; emit toolTipTextFormatChanged(); } QQuickItem *AppletInterface::toolTipItem() const { return m_toolTipItem.data(); } void AppletInterface::setToolTipItem(QQuickItem *toolTipItem) { if (m_toolTipItem.data() == toolTipItem) { return; } m_toolTipItem = toolTipItem; connect(m_toolTipItem.data(), &QObject::destroyed, this, &AppletInterface::toolTipItemChanged); emit toolTipItemChanged(); } bool AppletInterface::isBusy() const { return applet()->isBusy(); } void AppletInterface::setBusy(bool busy) { applet()->setBusy(busy); } Plasma::Types::BackgroundHints AppletInterface::backgroundHints() const { - return m_backgroundHints; + return applet()->backgroundHints(); } void AppletInterface::setBackgroundHints(Plasma::Types::BackgroundHints hint) { - if (m_backgroundHints == hint) { - return; - } - - Plasma::Types::BackgroundHints oldeffectiveHints = effectiveBackgroundHints(); - - m_backgroundHints = hint; - emit backgroundHintsChanged(); - - if (oldeffectiveHints != effectiveBackgroundHints()) { - emit effectiveBackgroundHintsChanged(); - } + applet()->setBackgroundHints(hint); } Plasma::Types::BackgroundHints AppletInterface::effectiveBackgroundHints() const { - if (m_userBackgroundHintsInitialized - && (m_backgroundHints & Plasma::Types::ConfigurableBackground)) { - return m_userBackgroundHints; - } else { - return m_backgroundHints; - } + return applet()->effectiveBackgroundHints(); } Plasma::Types::BackgroundHints AppletInterface::userBackgroundHints() const { - return m_userBackgroundHints; + return applet()->userBackgroundHints(); } void AppletInterface::setUserBackgroundHints(Plasma::Types::BackgroundHints hint) { - if (m_userBackgroundHints == hint && m_userBackgroundHintsInitialized) { - return; - } - - m_userBackgroundHints = hint; - m_userBackgroundHintsInitialized = true; - QMetaEnum hintEnum = QMetaEnum::fromType(); - applet()->config().writeEntry("UserBackgroundHints", hintEnum.valueToKey(m_userBackgroundHints)); - if (applet()->containment() && applet()->containment()->corona()) { - applet()->containment()->corona()->requestConfigSync(); - } - - emit userBackgroundHintsChanged(); - - if (m_backgroundHints & Plasma::Types::ConfigurableBackground) { - emit effectiveBackgroundHintsChanged(); - } + applet()->setUserBackgroundHints(hint); } void AppletInterface::setConfigurationRequired(bool needsConfiguring, const QString &reason) { appletScript()->setConfigurationRequired(needsConfiguring, reason); } QString AppletInterface::file(const QString &fileType) { return appletScript()->filePath(fileType, QString()); } QString AppletInterface::file(const QString &fileType, const QString &filePath) { return appletScript()->filePath(fileType, filePath); } QList AppletInterface::contextualActions() const { QList actions; Plasma::Applet *a = applet(); if (a->failedToLaunch()) { return actions; } foreach (const QString &name, m_actions) { QAction *action = a->actions()->action(name); if (action) { actions << action; } } return actions; } void AppletInterface::setActionSeparator(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setSeparator(true); } else { action = new QAction(this); action->setSeparator(true); a->actions()->addAction(name, action); m_actions.append(name); } } void AppletInterface::setAction(const QString &name, const QString &text, const QString &icon, const QString &shortcut) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); if (action) { action->setText(text); } else { action = new QAction(text, this); a->actions()->addAction(name, action); Q_ASSERT(!m_actions.contains(name)); m_actions.append(name); connect(action, &QAction::triggered, this, [this, name] { executeAction(name); }); } if (!icon.isEmpty()) { action->setIcon(QIcon::fromTheme(icon)); } if (!shortcut.isEmpty()) { action->setShortcut(shortcut); } action->setObjectName(name); } void AppletInterface::removeAction(const QString &name) { Plasma::Applet *a = applet(); QAction *action = a->actions()->action(name); delete action; m_actions.removeAll(name); } void AppletInterface::clearActions() { Q_FOREACH (const QString &action, m_actions) { removeAction(action); } } QAction *AppletInterface::action(QString name) const { return applet()->actions()->action(name); } bool AppletInterface::immutable() const { return applet()->immutability() != Plasma::Types::Mutable; } Plasma::Types::ImmutabilityType AppletInterface::immutability() const { return applet()->immutability(); } bool AppletInterface::userConfiguring() const { return applet()->isUserConfiguring(); } int AppletInterface::apiVersion() const { // Look for C++ plugins first auto filter = [](const KPluginMetaData &md) -> bool { return md.value(QStringLiteral("X-Plasma-API")) == QLatin1String("declarativeappletscript") && md.value(QStringLiteral("X-Plasma-ComponentTypes")).contains(QLatin1String("Applet")); }; QVector plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"), filter); if (plugins.isEmpty()) { return -1; } return plugins.first().value(QStringLiteral("X-KDE-PluginInfo-Version")).toInt(); } void AppletInterface::setAssociatedApplication(const QString &string) { if (applet()->associatedApplication() == string) { return; } applet()->setAssociatedApplication(string); emit associatedApplicationChanged(); } QString AppletInterface::associatedApplication() const { return applet()->associatedApplication(); } void AppletInterface::setAssociatedApplicationUrls(const QList &urls) { if (applet()->associatedApplicationUrls() == urls) { return; } applet()->setAssociatedApplicationUrls(urls); emit associatedApplicationUrlsChanged(); } QList AppletInterface::associatedApplicationUrls() const { return applet()->associatedApplicationUrls(); } void AppletInterface::setStatus(const Plasma::Types::ItemStatus &status) { applet()->setStatus(status); } Plasma::Types::ItemStatus AppletInterface::status() const { return applet()->status(); } int AppletInterface::screen() const { if (Plasma::Containment* c = applet()->containment()) { return c->screen(); } return -1; } QRect AppletInterface::screenGeometry() const { if (!applet() || !applet()->containment() || !applet()->containment()->corona()) { return QRect(); } return applet()->containment()->corona()->screenGeometry(applet()->containment()->screen()); } void AppletInterface::setHideOnWindowDeactivate(bool hide) { if (m_hideOnDeactivate != hide) { m_hideOnDeactivate = hide; emit hideOnWindowDeactivateChanged(); } } bool AppletInterface::hideOnWindowDeactivate() const { return m_hideOnDeactivate; } QKeySequence AppletInterface::globalShortcut() const { return applet()->globalShortcut(); } void AppletInterface::setGlobalShortcut(const QKeySequence &sequence) { applet()->setGlobalShortcut(sequence); } QObject *AppletInterface::nativeInterface() { if (qstrcmp(applet()->metaObject()->className(),"Plasma::Applet") != 0) { return applet(); } else { if (!m_dummyNativeInterface) { m_dummyNativeInterface = new QObject(this); } return m_dummyNativeInterface; } } bool AppletInterface::configurationRequired() const { return applet()->configurationRequired(); } void AppletInterface::setConfigurationRequiredProperty(bool needsConfiguring) { appletScript()->setConfigurationRequired(needsConfiguring, applet()->configurationRequiredReason()); } QString AppletInterface::configurationRequiredReason() const { return applet()->configurationRequiredReason(); } void AppletInterface::setConfigurationRequiredReason(const QString &reason) { appletScript()->setConfigurationRequired(applet()->configurationRequired(), reason); } QString AppletInterface::downloadPath(const QString &file) { Q_UNUSED(file); return downloadPath(); } QString AppletInterface::downloadPath() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/Plasma/") + applet()->pluginMetaData().pluginId() + QLatin1Char('/'); if (!QFile::exists(downloadDir)) { QDir dir({ QLatin1Char('/') }); dir.mkpath(downloadDir); } return downloadDir; } QStringList AppletInterface::downloadedFiles() const { const QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/Plasma/") + applet()->pluginMetaData().pluginId() + QLatin1Char('/'); QDir dir(downloadDir); return dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::Readable); } void AppletInterface::executeAction(const QString &name) { if (qmlObject()->rootObject()) { const QMetaObject *metaObj = qmlObject()->rootObject()->metaObject(); const QByteArray actionMethodName = "action_" + name.toUtf8(); const QByteArray actionFunctionName = actionMethodName + QByteArray("()"); if (metaObj->indexOfMethod(QMetaObject::normalizedSignature(actionFunctionName.constData()).constData()) != -1) { QMetaObject::invokeMethod(qmlObject()->rootObject(), actionMethodName.constData(), Qt::DirectConnection); } else { QMetaObject::invokeMethod(qmlObject()->rootObject(), "actionTriggered", Qt::DirectConnection, Q_ARG(QVariant, name)); } } } QVariantList AppletInterface::availableScreenRegion() const { QVariantList regVal; if (!applet()->containment() || !applet()->containment()->corona()) { return regVal; } QRegion reg = QRect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { reg = applet()->containment()->corona()->availableScreenRegion(screenId); } auto it = reg.begin(); const auto itEnd = reg.end(); for (; it != itEnd; ++it) { QRect rect = *it; //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); regVal << QVariant::fromValue(QRectF(rect)); } return regVal; } QRect AppletInterface::availableScreenRect() const { if (!applet()->containment() || !applet()->containment()->corona()) { return QRect(); } QRect rect(0, 0, width(), height()); int screenId = screen(); if (screenId > -1) { rect = applet()->containment()->corona()->availableScreenRect(screenId); //make it relative QRect geometry = applet()->containment()->corona()->screenGeometry(screenId); rect.moveTo(rect.topLeft() - geometry.topLeft()); } return rect; } bool AppletInterface::event(QEvent *event) { // QAction keyboard shortcuts cannot work with QML2 (and probably newver will // since in Qt qtquick and qwidgets cannot depend from each other in any way) // so do a simple keyboard shortcut matching here if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); QKeySequence seq(ke->key()|ke->modifiers()); QList actions = applet()->actions()->actions(); //find the wallpaper action if we are a containment ContainmentInterface *ci = qobject_cast(this); if (ci) { WallpaperInterface *wi = ci->wallpaperInterface(); if (wi) { actions << wi->contextualActions(); } } //add any actions of the corona if (applet()->containment() && applet()->containment()->corona()) { actions << applet()->containment()->corona()->actions()->actions(); } bool keySequenceUsed = false; foreach (auto a, actions) { if (a->shortcut().isEmpty()) { continue; } //this will happen on a normal, non emacs shortcut if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); m_oldKeyboardShortcut = 0; return true; //first part of an emacs style shortcut? } else if (seq.matches(a->shortcut()) == QKeySequence::PartialMatch) { keySequenceUsed = true; m_oldKeyboardShortcut = ke->key()|ke->modifiers(); //no match at all, but it can be the second part of an emacs style shortcut } else { QKeySequence seq(m_oldKeyboardShortcut, ke->key()|ke->modifiers()); if (seq.matches(a->shortcut()) == QKeySequence::ExactMatch) { event->accept(); a->trigger(); return true; } } } if (!keySequenceUsed) { m_oldKeyboardShortcut = 0; } } return AppletQuickItem::event(event); } void AppletInterface::prepareContextualActions() { emit applet()->contextualActionsAboutToShow(); } bool AppletInterface::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *e = static_cast(event); //pass it up to the applet //well, actually we have to pass it to the *containment* //because all the code for showing an applet's contextmenu is actually in Containment. Plasma::Containment *c = applet()->containment(); if (c) { const QString trigger = Plasma::ContainmentActions::eventToString(event); Plasma::ContainmentActions *plugin = c->containmentActions().value(trigger); if (!plugin) { return false; } ContainmentInterface *ci = c->property("_plasma_graphicObject").value(); if (!ci) { return false; } //the plugin can be a single action or a context menu //Don't have an action list? execute as single action //and set the event position as action data if (plugin->contextualActions().length() == 1) { // but first check whether we are not a popup // we don't want to randomly creates applets without confirmation if (static_cast(watched)->window() != ci->window()) { return true; } QAction *action = plugin->contextualActions().at(0); action->setData(e->globalPos()); action->trigger(); return true; } QMenu *desktopMenu = new QMenu; if (desktopMenu->winId()) { desktopMenu->windowHandle()->setTransientParent(window()); } prepareContextualActions(); ci->addAppletActions(desktopMenu, applet(), event); if (!desktopMenu->isEmpty()) { desktopMenu->setAttribute(Qt::WA_DeleteOnClose); desktopMenu->popup(e->globalPos()); return true; } delete desktopMenu; return false; } } return AppletQuickItem::eventFilter(watched, event); } void AppletInterface::updateUiReadyConstraint() { if (!isLoading()) { applet()->updateConstraints(Plasma::Types::UiReadyConstraint); } } bool AppletInterface::isLoading() const { return m_loading; } #include "moc_appletinterface.cpp" diff --git a/src/scriptengines/qml/plasmoid/appletinterface.h b/src/scriptengines/qml/plasmoid/appletinterface.h index 2043e5bf1..f495bde88 100644 --- a/src/scriptengines/qml/plasmoid/appletinterface.h +++ b/src/scriptengines/qml/plasmoid/appletinterface.h @@ -1,521 +1,518 @@ /* * Copyright 2008 Chani Armitage * Copyright 2008, 2009 Aaron Seigo * Copyright 2010 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 APPLETINTERFACE_H #define APPLETINTERFACE_H #include #include #include #include #include #include "declarativeappletscript.h" class QAction; class QmlAppletScript; class QSizeF; namespace KDeclarative { class ConfigPropertyMap; class QmlObject; } namespace Plasma { class ConfigLoader; } // namespace Plasma /** * @class AppletInterface * * @short This class is exposed to applets in QML as the attached property Plasmoid * * \@import org.kde.plasma.Plasmoid */ class AppletInterface : public PlasmaQuick::AppletQuickItem { Q_OBJECT /** * The QML root object defined in the applet main.qml will be direct child of an AppletInterface instance */ /** * Version of the QML2 script engine */ Q_PROPERTY(int apiVersion READ apiVersion CONSTANT) /** * Plugin name of the plasmoid */ Q_PROPERTY(QString pluginName READ pluginName CONSTANT) /** * User friendly title for the plasmoid: it's the localized applet name by default */ Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) /** * Main title for the plasmoid tooltip or other means of quick information: * it's the same as the title property by default, but it can be personalized */ Q_PROPERTY(QString toolTipMainText READ toolTipMainText WRITE setToolTipMainText NOTIFY toolTipMainTextChanged) /** * Description for the plasmoid tooltip or other means of quick information: * it comes from the pluginifo comment by default, but it can be personalized */ Q_PROPERTY(QString toolTipSubText READ toolTipSubText WRITE setToolTipSubText NOTIFY toolTipSubTextChanged) /** * how to handle the text format of the tooltip subtext: * * Text.AutoText (default) * * Text.PlainText * * Text.StyledText * * Text.RichText * Note: in the default implementation the main text is always plain text */ Q_PROPERTY(int toolTipTextFormat READ toolTipTextFormat WRITE setToolTipTextFormat NOTIFY toolTipTextFormatChanged) /** * This allows to set custom full QML items as the tooltip. * It will ignore all texts set by setToolTipMainText or setToolTipSubText * * @since: 5.19 */ Q_PROPERTY(QQuickItem *toolTipItem READ toolTipItem WRITE setToolTipItem NOTIFY toolTipItemChanged) /** * Icon to represent the plasmoid */ Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged) /** * Applet id: is unique in the whole Plasma session and will never change across restarts */ Q_PROPERTY(uint id READ id CONSTANT) /** * FormFactor for the plasmoid */ Q_PROPERTY(Plasma::Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged) /** * Location for the plasmoid */ Q_PROPERTY(Plasma::Types::Location location READ location NOTIFY locationChanged) /** * Current activity name the plasmoid is in */ Q_PROPERTY(QString currentActivity READ currentActivity NOTIFY contextChanged) /** * Configuration object: each config key will be a writable property of this object. property bindings work. */ Q_PROPERTY(QObject *configuration READ configuration CONSTANT) /** * When true the plasmoid is busy. The containment may graphically indicate that drawing for instance a spinner busy widget over it */ Q_PROPERTY(bool busy WRITE setBusy READ isBusy NOTIFY busyChanged) /** * How the applet wants its background to be drawn. The containment may chose to ignore this hint. */ Q_PROPERTY(Plasma::Types::BackgroundHints backgroundHints WRITE setBackgroundHints READ backgroundHints NOTIFY backgroundHintsChanged) /** * The containment (and/or the user) may decide to use another kind of background instead (if supported by the applet) */ Q_PROPERTY(Plasma::Types::BackgroundHints userBackgroundHints WRITE setUserBackgroundHints READ userBackgroundHints NOTIFY userBackgroundHintsChanged) /** * The effective background hints the applet has, internally decided how to mix with userBackgroundHints */ Q_PROPERTY(Plasma::Types::BackgroundHints effectiveBackgroundHints READ effectiveBackgroundHints NOTIFY effectiveBackgroundHintsChanged) /** * Whether the Corona is immutable. The plasmoid implementation should avoid allowing "dangerous" modifications from the user when in an immutable mode * * This is true when immutability is not Mutable */ Q_PROPERTY(bool immutable READ immutable NOTIFY immutabilityChanged) /** * The immutability of the Corona. * * Use this if you need more granular control than just using the immutable property * * @see immutable * @since 5.23 */ Q_PROPERTY(Plasma::Types::ImmutabilityType immutability READ immutability NOTIFY immutabilityChanged) /** * True when the user is configuring, for instance when the configuration dialog is open. */ Q_PROPERTY(bool userConfiguring READ userConfiguring NOTIFY userConfiguringChanged) /** * Status of the plasmoid: useful to instruct the shell if this plasmoid is requesting attention, if is accepting input, or if is in an idle, inactive state */ Q_PROPERTY(Plasma::Types::ItemStatus status READ status WRITE setStatus NOTIFY statusChanged) /** * Sets the associated application of this plasmoid, if the plasmoid is representing the "compact" view for some application or for some document type. */ Q_PROPERTY(QString associatedApplication WRITE setAssociatedApplication READ associatedApplication NOTIFY associatedApplicationChanged) /** * Sets the associated application of this plasmoid, if the plasmoid is representing the "compact" view for some application or for some document type. */ Q_PROPERTY(QList associatedApplicationUrls WRITE setAssociatedApplicationUrls READ associatedApplicationUrls NOTIFY associatedApplicationUrlsChanged) // TODO: This was moved up from ContainmentInterface because it is required by the // Task Manager applet (for "Show only tasks from this screen") and no Qt API exposes // screen numbering. An alternate solution that doesn't extend the applet interface // would be preferable if found. Q_PROPERTY(int screen READ screen NOTIFY screenChanged) /** * Provides access to the geometry of the applet is in. * Can be useful to figure out what's the absolute position of the applet. */ Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) /** * 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) /** * The global shortcut to activate the plasmoid * * This is typically only used by the default configuration module * */ Q_PROPERTY(QKeySequence globalShortcut READ globalShortcut WRITE setGlobalShortcut NOTIFY globalShortcutChanged) /** * An interface to the native C++ plasmoid, if implemented */ Q_PROPERTY(QObject *nativeInterface READ nativeInterface CONSTANT) /** * If true the applet requires manual configuration from the user */ Q_PROPERTY(bool configurationRequired READ configurationRequired WRITE setConfigurationRequiredProperty NOTIFY configurationRequiredChanged) /** * Reason why the manual user configuration is required */ Q_PROPERTY(QString configurationRequiredReason READ configurationRequiredReason WRITE setConfigurationRequiredReason NOTIFY configurationRequiredReasonChanged) /** * screen area free of panels: the coordinates are relative to the containment, * it's independent from the screen position * For more precise available geometry use availableScreenRegion() */ Q_PROPERTY(QRect availableScreenRect READ availableScreenRect NOTIFY availableScreenRectChanged) /** * The available region of this screen, panels excluded. It's a list of rectangles */ Q_PROPERTY(QVariantList availableScreenRegion READ availableScreenRegion NOTIFY availableScreenRegionChanged) Q_PROPERTY(bool loading MEMBER m_loading NOTIFY isLoadingChanged) public: AppletInterface(DeclarativeAppletScript *script, const QVariantList &args = QVariantList(), QQuickItem *parent = nullptr); ~AppletInterface() override; //API not intended for the QML part DeclarativeAppletScript *appletScript() const; QList contextualActions() const; void executeAction(const QString &name); //QML API------------------------------------------------------------------- /** * Set this to true if the plasmoid needs to be configured in order to work. The containment will display reason as a message to ask the user to configure. * @param needsConfiguring If the plasmoid needs configuration * @param reason The user readable (and localized) reason the plasmoid needs */ Q_INVOKABLE void setConfigurationRequired(bool needsConfiguring, const QString &reason = QString()); Q_INVOKABLE void setActionSeparator(const QString &name); /** * Add an action to the Plasmoid contextual menu. * When the action is triggered a function called action_ will be called, if there is no function with that name actionTriggered(name) will be called instead. * @param: action name * @text: user visible displayed text * @icon: user visible optional displayed icon * @shortcut: shortcut to trigger this action */ Q_INVOKABLE void setAction(const QString &name, const QString &text, const QString &icon = QString(), const QString &shortcut = QString()); Q_INVOKABLE void removeAction(const QString &name); Q_INVOKABLE void clearActions(); Q_INVOKABLE QAction *action(QString name) const; /** * Should be called before retrieving any action * to ensure contents are up to date * @see contextualActionsAboutToShow * @since 5.58 */ Q_INVOKABLE void prepareContextualActions(); /** * FIXME: remove? * Retrieve the path of a file from the Plasmoid package * @param fileName the package-recognized name, such as "mainscript" * @returns the full absolute path of the file, if found, an empty string if not */ Q_INVOKABLE QString file(const QString &fileName); /** * FIXME: remove? * Retrieve the path of a file from the Plasmoid package * @param fileType the type supported from the package, such as "ui", "config" or "image" * @param filePath the name of the file, such as "foo.qml" or "bar.png" * @returns the full absolute path of the file, if found, an empty string if not */ Q_INVOKABLE QString file(const QString &fileType, const QString &filePath); /** * @returns A path where it is safe to write on disk downloaded files. * @since 5.23 */ Q_INVOKABLE QString downloadPath() const; /** * @returns A path where it is safe to write on disk downloaded files. * @param file that name of the file to download (unused). * @deprecated Use downloadPath() instead. */ Q_INVOKABLE PLASMA_DEPRECATED QString downloadPath(const QString &file); /** * @returns The list of files that have been downloaded */ Q_INVOKABLE QStringList downloadedFiles() const; QVariantList availableScreenRegion() const; QRect availableScreenRect() const; static AppletInterface *qmlAttachedProperties(QObject *object) { return qobject_cast(AppletQuickItem::qmlAttachedProperties(object)); } //PROPERTY ACCESSORS------------------------------------------------------------------- QString pluginName() const; QString icon() const; void setIcon(const QString &icon); QString title() const; void setTitle(const QString &title); QString toolTipMainText() const; void setToolTipMainText(const QString &text); QString toolTipSubText() const; void setToolTipSubText(const QString &text); int toolTipTextFormat() const; void setToolTipTextFormat(int format); QQuickItem *toolTipItem() const; void setToolTipItem(QQuickItem *toolTipItem); uint id() const; Plasma::Types::FormFactor formFactor() const; Plasma::Types::Location location() const; QString currentActivity() const; QObject *configuration() const; bool isBusy() const; void setBusy(bool busy); Plasma::Types::BackgroundHints backgroundHints() const; void setBackgroundHints(Plasma::Types::BackgroundHints hint); Plasma::Types::BackgroundHints userBackgroundHints() const; void setUserBackgroundHints(Plasma::Types::BackgroundHints hint); Plasma::Types::BackgroundHints effectiveBackgroundHints() const; void setAssociatedApplication(const QString &string); QString associatedApplication() const; void setAssociatedApplicationUrls(const QList &urls); QList associatedApplicationUrls() const; void setStatus(const Plasma::Types::ItemStatus &status); Plasma::Types::ItemStatus status() const; int screen() const; QRect screenGeometry() const; bool immutable() const; Plasma::Types::ImmutabilityType immutability() const; bool userConfiguring() const; int apiVersion() const; bool hideOnWindowDeactivate() const; void setHideOnWindowDeactivate(bool hide); QKeySequence globalShortcut() const; void setGlobalShortcut(const QKeySequence &keySequence); QObject *nativeInterface(); //NOTE: setConfigurationRequiredProperty because ambiguous with the // setConfigurationRequired invokable bool configurationRequired() const; void setConfigurationRequiredProperty(bool required); QString configurationRequiredReason() const; void setConfigurationRequiredReason(const QString &reason); Q_SIGNALS: /** * somebody else, usually the containment sent some data to the applet * @param mimetype the mime type of the data such as text/plain * @param data either the actual data or an URL representing it */ void externalData(const QString &mimetype, const QVariant &data); void configNeedsSaving(); /** * Emitted when the applet's activation action is triggered */ void activated(); /** * Emitted just before the contextual actions are about to show * For instance just before the context menu containing the actions * added with setAction() is shown */ void contextualActionsAboutToShow(); //PROPERTY change notifiers-------------- void iconChanged(); void titleChanged(); void toolTipMainTextChanged(); void toolTipSubTextChanged(); void toolTipTextFormatChanged(); void toolTipItemChanged(); void formFactorChanged(); void locationChanged(); void contextChanged(); void immutabilityChanged(); void statusChanged(); void backgroundHintsChanged(); void userBackgroundHintsChanged(); void effectiveBackgroundHintsChanged(); void busyChanged(); void screenChanged(); void screenGeometryChanged(); void hideOnWindowDeactivateChanged(); void associatedApplicationChanged(); void associatedApplicationUrlsChanged(); void availableScreenRegionChanged(); void availableScreenRectChanged(); void userConfiguringChanged(); void globalShortcutChanged(); void configurationRequiredChanged(); void configurationRequiredReasonChanged(); void isLoadingChanged(); protected Q_SLOTS: void init() override; protected: bool event(QEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; /* * Returns true if this plasmoid or a dependent feature (i.e wallpaper) is loading */ virtual bool isLoading() const; /* * Set UIReadyConstraint if we're not currently loading */ void updateUiReadyConstraint(); private Q_SLOTS: void destroyedChanged(bool destroyed); private: QStringList m_actions; KDeclarative::ConfigPropertyMap *m_configuration; DeclarativeAppletScript *m_appletScriptEngine; //UI-specific members ------------------ QString m_toolTipMainText; QString m_toolTipSubText; int m_toolTipTextFormat; QPointer m_toolTipItem; QVariantList m_args; - Plasma::Types::BackgroundHints m_backgroundHints; - Plasma::Types::BackgroundHints m_userBackgroundHints; - bool m_userBackgroundHintsInitialized = false; bool m_hideOnDeactivate : 1; bool m_loading = false; //this is used to build an emacs style shortcut int m_oldKeyboardShortcut; QObject *m_dummyNativeInterface; friend class ContainmentInterface; //This is used by ContainmentInterface QPointF m_positionBeforeRemoval; }; QML_DECLARE_TYPEINFO(AppletInterface, QML_HAS_ATTACHED_PROPERTIES) #endif