diff --git a/applets/kicker/plugin/appentry.cpp b/applets/kicker/plugin/appentry.cpp index 820a94c12..9b24dd6c2 100644 --- a/applets/kicker/plugin/appentry.cpp +++ b/applets/kicker/plugin/appentry.cpp @@ -1,303 +1,308 @@ /*************************************************************************** * Copyright (C) 201 by Eike Hein * * * * 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 . * ***************************************************************************/ #include #include "appentry.h" #include "actionlist.h" #include "appsmodel.h" #include "containmentinterface.h" #include #include #include #include #if HAVE_X11 #include #endif #include #include #include #include #include #include #include #include #include #include #include AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat) : AbstractEntry(owner) , m_service(service) { if (m_service) { init(nameFormat); } } AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner) { const QUrl url(id); if (url.scheme() == QLatin1String("preferred")) { m_service = defaultAppByName(url.host()); m_id = id; } else { m_service = KService::serviceByStorageId(id); } if (m_service) { init((NameFormat)owner->rootModel()->property("appNameFormat").toInt()); } } void AppEntry::init(NameFormat nameFormat) { m_name = nameFromService(m_service, nameFormat); if (nameFormat == GenericNameOnly) { m_description = nameFromService(m_service, NameOnly); } else { m_description = nameFromService(m_service, GenericNameOnly); } } bool AppEntry::isValid() const { return m_service; } QIcon AppEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme(QStringLiteral("unknown"))); } return m_icon; } QString AppEntry::name() const { return m_name; } QString AppEntry::description() const { return m_description; } KService::Ptr AppEntry::service() const { return m_service; } QString AppEntry::id() const { if (!m_id.isEmpty()) { return m_id; } return m_service->storageId(); } QString AppEntry::menuId() const { return m_service->menuId(); } QUrl AppEntry::url() const { return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(m_service)); } bool AppEntry::hasActions() const { return true; } QVariantList AppEntry::actions() const { QVariantList actionList; actionList << Kicker::jumpListActions(m_service); if (!actionList.isEmpty()) { actionList << Kicker::createSeparatorActionItem(); } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); - const bool systemImmutable = appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable; + bool systemImmutable = false; + if (appletInterface) { + systemImmutable = (appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable); + } const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service); if (!systemImmutable && !addLauncherActions.isEmpty()) { actionList << addLauncherActions << Kicker::createSeparatorActionItem(); } const QVariantList &recentDocuments = Kicker::recentDocumentActions(m_service); if (!recentDocuments.isEmpty()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } // Don't allow adding launchers, editing, hiding, or uninstalling applications // when system is immutable. if (systemImmutable) { return actionList; } if (m_service->isApplication()) { actionList << Kicker::createSeparatorActionItem(); actionList << Kicker::editApplicationAction(m_service); actionList << Kicker::appstreamActions(m_service); } - QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); + if (appletInterface) { + QQmlPropertyMap *appletConfig = qobject_cast(appletInterface->property("configuration").value()); - if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications")) && qobject_cast(m_owner)) { - const QStringList &hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList(); + if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications")) && qobject_cast(m_owner)) { + const QStringList &hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList(); - if (!hiddenApps.contains(m_service->menuId())) { - QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("hideApplication")); - hideAction[QStringLiteral("icon")] = QStringLiteral("hint"); - actionList << hideAction; + if (!hiddenApps.contains(m_service->menuId())) { + QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("hideApplication")); + hideAction[QStringLiteral("icon")] = QStringLiteral("hint"); + actionList << hideAction; + } } } return actionList; } bool AppEntry::run(const QString& actionId, const QVariant &argument) { if (!m_service->isValid()) { return false; } if (actionId.isEmpty()) { quint32 timeStamp = 0; #if HAVE_X11 if (QX11Info::isPlatformX11()) { timeStamp = QX11Info::appUserTime(); } #endif KRun::runApplication(*m_service, {}, nullptr, KRun::DeleteTemporaryFiles, {}, KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + m_service->storageId()), QStringLiteral("org.kde.plasma.kicker")); return true; } QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value(); if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) { return true; } else if (Kicker::handleEditApplicationAction(actionId, m_service)) { return true; } else if (Kicker::handleAppstreamActions(actionId, argument)) { return true; } else if (actionId == QLatin1String("_kicker_jumpListAction")) { return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon()); } return Kicker::handleRecentDocumentAction(m_service, actionId, argument); } QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat) { const QString &name = service->name(); QString genericName = service->genericName(); if (genericName.isEmpty()) { genericName = service->comment(); } if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) { return name; } else if (nameFormat == GenericNameOnly) { return genericName; } else if (nameFormat == NameAndGenericName) { return i18nc("App name (Generic name)", "%1 (%2)", name, genericName); } else { return i18nc("Generic name (App name)", "%1 (%2)", genericName, name); } } KService::Ptr AppEntry::defaultAppByName(const QString& name) { if (name == QLatin1String("browser")) { KConfigGroup config(KSharedConfig::openConfig(), "General"); QString browser = config.readPathEntry("BrowserApplication", QString()); if (browser.isEmpty()) { return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html")); } else if (browser.startsWith(QLatin1Char('!'))) { browser.remove(0, 1); } return KService::serviceByStorageId(browser); } return KService::Ptr(); } AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group, bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel), m_group(group) { AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat, sorted, separators, parentModel); model->setAppNameFormat(appNameFormat); m_childModel = model; QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater); QObject::connect(model, &AppsModel::countChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); QObject::connect(model, &AppsModel::hiddenEntriesChanged, [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } } ); } QIcon AppGroupEntry::icon() const { if (m_icon.isNull()) { m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme(QStringLiteral("unknown"))); } return m_icon; } QString AppGroupEntry::name() const { return m_group->caption(); } bool AppGroupEntry::hasChildren() const { return m_childModel && m_childModel->count() > 0; } AbstractModel *AppGroupEntry::childModel() const { return m_childModel; } diff --git a/applets/kicker/plugin/runnermatchesmodel.cpp b/applets/kicker/plugin/runnermatchesmodel.cpp index 0756e5f58..b52bd868f 100644 --- a/applets/kicker/plugin/runnermatchesmodel.cpp +++ b/applets/kicker/plugin/runnermatchesmodel.cpp @@ -1,255 +1,258 @@ /*************************************************************************** * Copyright (C) 2012 by Aurélien Gâteau * * Copyright (C) 2014-2015 by Eike Hein * * * * 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 . * ***************************************************************************/ #include "runnermatchesmodel.h" #include "runnermodel.h" #include "actionlist.h" #include #include #include #include #include #include RunnerMatchesModel::RunnerMatchesModel(const QString &runnerId, const QString &name, Plasma::RunnerManager *manager, QObject *parent) : AbstractModel(parent) , m_runnerId(runnerId) , m_name(name) , m_runnerManager(manager) { } QString RunnerMatchesModel::description() const { return m_name; } QVariant RunnerMatchesModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_matches.count()) { return QVariant(); } Plasma::QueryMatch match = m_matches.at(index.row()); if (role == Qt::DisplayRole) { return match.text(); } else if (role == Qt::DecorationRole) { if (!match.iconName().isEmpty()) { return match.iconName(); } return match.icon(); } else if (role == Kicker::DescriptionRole) { return match.subtext(); } else if (role == Kicker::FavoriteIdRole) { if (match.runner()->id() == QLatin1String("services")) { return match.data().toString(); } } else if (role == Kicker::UrlRole) { const QString &runnerId = match.runner()->id(); if (runnerId == QLatin1String("baloosearch") || runnerId == QLatin1String("bookmarks")) { return QUrl(match.data().toString()); } else if (runnerId == QLatin1String("recentdocuments") || runnerId == QLatin1String("services")) { KService::Ptr service = KService::serviceByStorageId(match.data().toString()); if (service) { return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(service)); } } } else if (role == Kicker::HasActionListRole) { // Hack to expose the protected Plasma::AbstractRunner::actions() method. class MyRunner : public Plasma::AbstractRunner { public: using Plasma::AbstractRunner::actions; }; MyRunner *runner = static_cast(match.runner()); Q_ASSERT(runner); return match.runner()->id() == QLatin1String("services") || !runner->actions().isEmpty(); } else if (role == Kicker::ActionListRole) { QVariantList actionList; foreach (QAction *action, m_runnerManager->actionsForMatch(match)) { QVariantMap item = Kicker::createActionItem(action->text(), QStringLiteral("runnerAction"), QVariant::fromValue(action)); item[QStringLiteral("icon")] = action->icon(); actionList << item; } // Only try to get a KService for matches from the services runner. Assuming // that any other runner returns something we want to turn into a KService is // unsafe, e.g. files from the Baloo runner might match a storageId just by // accident, creating a dangerous false positive. if (match.runner()->id() != QLatin1String("services")) { return actionList; } const KService::Ptr service = KService::serviceByStorageId(match.data().toString()); if (service) { if (!actionList.isEmpty()) { actionList << Kicker::createSeparatorActionItem(); } const QVariantList &jumpListActions = Kicker::jumpListActions(service); if (!jumpListActions.isEmpty()) { actionList << jumpListActions << Kicker::createSeparatorActionItem(); } QObject *appletInterface = static_cast(parent())->appletInterface(); - const bool systemImmutable = appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable; + bool systemImmutable = false; + if (appletInterface) { + systemImmutable = (appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable); + } const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, service); if (!systemImmutable && !addLauncherActions.isEmpty()) { actionList << addLauncherActions << Kicker::createSeparatorActionItem(); } const QVariantList &recentDocuments = Kicker::recentDocumentActions(service); if (!recentDocuments.isEmpty()) { actionList << recentDocuments << Kicker::createSeparatorActionItem(); } // Don't allow adding launchers, editing, hiding, or uninstalling applications // when system is immutable. if (systemImmutable) { return actionList; } if (service->isApplication()) { actionList << Kicker::editApplicationAction(service); actionList << Kicker::appstreamActions(service); } } return actionList; } return QVariant(); } int RunnerMatchesModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_matches.count(); } bool RunnerMatchesModel::trigger(int row, const QString &actionId, const QVariant &argument) { if (row < 0 || row >= m_matches.count()) { return false; } Plasma::QueryMatch match = m_matches.at(row); if (!match.isEnabled()) { return false; } QObject *appletInterface = static_cast(parent())->appletInterface(); const KService::Ptr service = KService::serviceByStorageId(match.data().toString()); if (Kicker::handleAddLauncherAction(actionId, appletInterface, service)) { return true; } else if (Kicker::handleEditApplicationAction(actionId, service)) { return true; } else if (Kicker::handleAppstreamActions(actionId, argument)) { return true; } else if (actionId == QLatin1String("_kicker_jumpListAction")) { return KRun::run(argument.toString(), {}, nullptr, service ? service->name() : QString(), service ? service->icon() : QString()); } else if (actionId == QLatin1String("_kicker_recentDocument") || actionId == QLatin1String("_kicker_forgetRecentDocuments")) { return Kicker::handleRecentDocumentAction(service, actionId, argument); } if (!actionId.isEmpty()) { QObject *obj = argument.value(); if (!obj) { return false; } QAction *action = qobject_cast(obj); if (!action) { return false; } match.setSelectedAction(action); } m_runnerManager->run(match); return true; } void RunnerMatchesModel::setMatches(const QList< Plasma::QueryMatch > &matches) { int oldCount = m_matches.count(); int newCount = matches.count(); bool emitCountChange = (oldCount != newCount); int ceiling = qMin(oldCount, newCount); bool emitDataChange = false; for (int row = 0; row < ceiling; ++row) { if (!(m_matches.at(row) == matches.at(row))) { emitDataChange = true; m_matches[row] = matches.at(row); } } if (emitDataChange) { emit dataChanged(index(0, 0), index(ceiling - 1, 0)); } if (newCount > oldCount) { beginInsertRows(QModelIndex(), oldCount, newCount - 1); m_matches = matches; endInsertRows(); } else if (newCount < oldCount) { beginRemoveRows(QModelIndex(), newCount, oldCount - 1); m_matches = matches; endRemoveRows(); } if (emitCountChange) { emit countChanged(); } } AbstractModel *RunnerMatchesModel::favoritesModel() { return static_cast(parent())->favoritesModel(); } diff --git a/lookandfeel/contents/lockscreen/LockScreenUi.qml b/lookandfeel/contents/lockscreen/LockScreenUi.qml index 32e234a31..2a5d94f39 100644 --- a/lookandfeel/contents/lockscreen/LockScreenUi.qml +++ b/lookandfeel/contents/lockscreen/LockScreenUi.qml @@ -1,516 +1,524 @@ /******************************************************************** This file is part of the KDE project. Copyright (C) 2014 Aleix Pol Gonzalez 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, see . *********************************************************************/ import QtQuick 2.8 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.private.sessions 2.0 import "../components" PlasmaCore.ColorScope { // If we're using software rendering, draw outlines instead of shadows // See https://bugs.kde.org/show_bug.cgi?id=398317 readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software colorGroup: PlasmaCore.Theme.ComplementaryColorGroup Connections { target: authenticator onFailed: { root.notification = i18nd("plasma_lookandfeel_org.kde.lookandfeel","Unlocking failed"); } onGraceLockedChanged: { if (!authenticator.graceLocked) { root.notification = ""; root.clearPassword(); } } onMessage: { root.notification = msg; } onError: { root.notification = err; } } SessionsModel { id: sessionsModel showNewSessionEntry: false } PlasmaCore.DataSource { id: keystateSource engine: "keystate" connectedSources: "Caps Lock" } Loader { id: changeSessionComponent active: false source: "ChangeSession.qml" visible: false } MouseArea { id: lockScreenRoot property bool uiVisible: false property bool blockUI: mainStack.depth > 1 || mainBlock.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive x: parent.x y: parent.y width: parent.width height: parent.height hoverEnabled: true drag.filterChildren: true onPressed: uiVisible = true; onPositionChanged: uiVisible = true; onUiVisibleChanged: { if (blockUI) { fadeoutTimer.running = false; } else if (uiVisible) { fadeoutTimer.restart(); } } onBlockUIChanged: { if (blockUI) { fadeoutTimer.running = false; uiVisible = true; } else { fadeoutTimer.restart(); } } Keys.onEscapePressed: { uiVisible = !uiVisible; if (inputPanel.keyboardActive) { inputPanel.showHide(); } if (!uiVisible) { mainBlock.mainPasswordBox.text = ""; } } Keys.onPressed: { uiVisible = true; event.accepted = false; } Timer { id: fadeoutTimer interval: 10000 onTriggered: { if (!lockScreenRoot.blockUI) { lockScreenRoot.uiVisible = false; } } } Component.onCompleted: PropertyAnimation { id: launchAnimation; target: lockScreenRoot; property: "opacity"; from: 0; to: 1; duration: 1000 } states: [ State { name: "onOtherSession" // for slide out animation PropertyChanges { target: lockScreenRoot; y: lockScreenRoot.height } // we also change the opacity just to be sure it's not visible even on unexpected screen dimension changes with possible race conditions PropertyChanges { target: lockScreenRoot; opacity: 0 } } ] transitions: Transition { // we only animate switchting to another session, because kscreenlocker doesn't get notified when // coming from another session back and so we wouldn't know when to trigger the animation exactly from: "" to: "onOtherSession" PropertyAnimation { id: stateChangeAnimation; properties: "y"; duration: 300; easing.type: Easing.InQuad} PropertyAnimation { properties: "opacity"; duration: 300} onRunningChanged: { // after the animation has finished switch session: since we only animate the transition TO state "onOtherSession" // and not the other way around, we don't have to check the state we transitioned into if (/* lockScreenRoot.state == "onOtherSession" && */ !running) { mainStack.currentItem.switchSession() } } } WallpaperFader { anchors.fill: parent state: lockScreenRoot.uiVisible ? "on" : "off" source: wallpaper mainStack: mainStack footer: footer clock: clock } DropShadow { id: clockShadow anchors.fill: clock source: clock visible: !softwareRendering horizontalOffset: 1 verticalOffset: 1 radius: 6 samples: 14 spread: 0.3 color: "black" // matches Breeze window decoration and desktopcontainment Behavior on opacity { OpacityAnimator { duration: 1000 easing.type: Easing.InOutQuad } } } Clock { id: clock property Item shadow: clockShadow anchors.horizontalCenter: parent.horizontalCenter y: (mainBlock.userList.y + mainStack.y)/2 - height/2 visible: y > 0 Layout.alignment: Qt.AlignBaseline } ListModel { id: users Component.onCompleted: { users.append({name: kscreenlocker_userName, realName: kscreenlocker_userName, icon: kscreenlocker_userImage, }) } } StackView { id: mainStack anchors { left: parent.left right: parent.right } height: lockScreenRoot.height + units.gridUnit * 3 focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it initialItem: MainBlock { id: mainBlock lockScreenUiVisible: lockScreenRoot.uiVisible showUserList: userList.y + mainStack.y > 0 Stack.onStatusChanged: { // prepare for presenting again to the user if (Stack.status == Stack.Activating) { mainPasswordBox.remove(0, mainPasswordBox.length) mainPasswordBox.focus = true } } userListModel: users notificationMessage: { var text = "" if (keystateSource.data["Caps Lock"]["Locked"]) { text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") if (root.notification) { text += " • " } } text += root.notification return text } onLoginRequest: { root.notification = "" authenticator.tryUnlock(password) } actionItems: [ ActionButton { text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User") iconSource: "system-switch-user" onClicked: { // If there are no existing sessions to switch to, create a new one instead if (((sessionsModel.showNewSessionEntry && sessionsModel.count === 1) || (!sessionsModel.showNewSessionEntry && sessionsModel.count === 0)) && sessionsModel.canSwitchUser) { mainStack.pop({immediate:true}) sessionsModel.startNewSession(true /* lock the screen too */) lockScreenRoot.state = '' } else { mainStack.push(switchSessionPage) } } visible: sessionsModel.canStartNewSession && sessionsModel.canSwitchUser + //Button gets cut off on smaller displays without this. + anchors{ + verticalCenter: parent.top + } } ] Loader { Layout.fillWidth: true Layout.preferredHeight: item ? item.implicitHeight : 0 active: config.showMediaControls source: "MediaControls.qml" } } Component.onCompleted: { if (defaultToSwitchUser) { //context property // If we are in the only session, then going to the session switcher is // a pointless extra step; instead create a new session immediately if (((sessionsModel.showNewSessionEntry && sessionsModel.count === 1) || (!sessionsModel.showNewSessionEntry && sessionsModel.count === 0)) && sessionsModel.canStartNewSession) { sessionsModel.startNewSession(true /* lock the screen too */) } else { mainStack.push({ item: switchSessionPage, immediate: true}); } } } } Loader { id: inputPanel state: "hidden" readonly property bool keyboardActive: item ? item.active : false anchors { left: parent.left right: parent.right } function showHide() { state = state == "hidden" ? "visible" : "hidden"; } Component.onCompleted: inputPanel.source = "../components/VirtualKeyboard.qml" onKeyboardActiveChanged: { if (keyboardActive) { state = "visible"; } else { state = "hidden"; } } states: [ State { name: "visible" PropertyChanges { target: mainStack y: Math.min(0, lockScreenRoot.height - inputPanel.height - mainBlock.visibleBoundary) } PropertyChanges { target: inputPanel y: lockScreenRoot.height - inputPanel.height opacity: 1 } }, State { name: "hidden" PropertyChanges { target: mainStack y: 0 } PropertyChanges { target: inputPanel y: lockScreenRoot.height - lockScreenRoot.height/4 opacity: 0 } } ] transitions: [ Transition { from: "hidden" to: "visible" SequentialAnimation { ScriptAction { script: { inputPanel.item.activated = true; Qt.inputMethod.show(); } } ParallelAnimation { NumberAnimation { target: mainStack property: "y" duration: units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: units.longDuration easing.type: Easing.OutQuad } OpacityAnimator { target: inputPanel duration: units.longDuration easing.type: Easing.OutQuad } } } }, Transition { from: "visible" to: "hidden" SequentialAnimation { ParallelAnimation { NumberAnimation { target: mainStack property: "y" duration: units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: units.longDuration easing.type: Easing.InQuad } OpacityAnimator { target: inputPanel duration: units.longDuration easing.type: Easing.InQuad } } ScriptAction { script: { Qt.inputMethod.hide(); } } } } ] } Component { id: switchSessionPage SessionManagementScreen { property var switchSession: finalSwitchSession Stack.onStatusChanged: { if (Stack.status == Stack.Activating) { focus = true } } userListModel: sessionsModel // initiating animation of lockscreen for session switch function initSwitchSession() { lockScreenRoot.state = 'onOtherSession' } // initiating session switch and preparing lockscreen for possible return of user function finalSwitchSession() { mainStack.pop({immediate:true}) sessionsModel.switchUser(userListCurrentModelData.vtNumber) lockScreenRoot.state = '' } Keys.onLeftPressed: userList.decrementCurrentIndex() Keys.onRightPressed: userList.incrementCurrentIndex() Keys.onEnterPressed: initSwitchSession() Keys.onReturnPressed: initSwitchSession() Keys.onEscapePressed: mainStack.pop() ColumnLayout { Layout.fillWidth: true spacing: units.largeSpacing PlasmaComponents.Button { Layout.fillWidth: true font.pointSize: theme.defaultFont.pointSize + 1 text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch to This Session") onClicked: initSwitchSession() visible: sessionsModel.count > 0 } PlasmaComponents.Button { Layout.fillWidth: true font.pointSize: theme.defaultFont.pointSize + 1 text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Start New Session") onClicked: { mainStack.pop({immediate:true}) sessionsModel.startNewSession(true /* lock the screen too */) lockScreenRoot.state = '' } } } actionItems: [ ActionButton { iconSource: "go-previous" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") onClicked: mainStack.pop() + //Button gets cut off on smaller displays without this. + anchors{ + verticalCenter: parent.top + } } ] } } Loader { active: root.viewVisible source: "LockOsd.qml" anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom bottomMargin: units.largeSpacing } } RowLayout { id: footer anchors { bottom: parent.bottom left: parent.left right: parent.right margins: units.smallSpacing } PlasmaComponents.ToolButton { text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard") iconName: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off" onClicked: inputPanel.showHide() visible: inputPanel.status == Loader.Ready } KeyboardLayoutButton { } Item { Layout.fillWidth: true } Battery {} } } Component.onCompleted: { // version support checks if (root.interfaceVersion < 1) { // ksmserver of 5.4, with greeter of 5.5 root.viewVisible = true; } } }