mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-04 10:44:46 +00:00
panel: Refactor panel and implement blue ocean mockups
This commit is contained in:
parent
7f9a9ca60c
commit
6653ad6e54
101 changed files with 4004 additions and 2339 deletions
|
|
@ -1,3 +1,3 @@
|
||||||
|
|
||||||
add_subdirectory(mobilehomescreencomponents)
|
add_subdirectory(mobilehomescreencomponents)
|
||||||
install(DIRECTORY mobileshell/ DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/mobileshell)
|
add_subdirectory(mobileshell)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ set(mobilehomescreencomponentsplugin_SRCS
|
||||||
applicationlistmodel.cpp
|
applicationlistmodel.cpp
|
||||||
favoritesmodel.cpp
|
favoritesmodel.cpp
|
||||||
homescreenutils.cpp
|
homescreenutils.cpp
|
||||||
quicksettingsmodel.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(mobilehomescreencomponentsplugin ${mobilehomescreencomponentsplugin_SRCS})
|
add_library(mobilehomescreencomponentsplugin ${mobilehomescreencomponentsplugin_SRCS})
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,11 @@
|
||||||
#include "applicationlistmodel.h"
|
#include "applicationlistmodel.h"
|
||||||
#include "favoritesmodel.h"
|
#include "favoritesmodel.h"
|
||||||
#include "homescreenutils.h"
|
#include "homescreenutils.h"
|
||||||
#include "quicksettingsmodel.h"
|
|
||||||
|
|
||||||
void MobileHomeScreenComponentsPlugin::registerTypes(const char *uri)
|
void MobileHomeScreenComponentsPlugin::registerTypes(const char *uri)
|
||||||
{
|
{
|
||||||
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.private.mobilehomescreencomponents"));
|
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.private.mobilehomescreencomponents"));
|
||||||
|
|
||||||
qmlRegisterType<QuickSetting>(uri, 0, 1, "QuickSetting");
|
|
||||||
qmlRegisterType<QuickSettingsModel>(uri, 0, 1, "QuickSettingsModel");
|
|
||||||
|
|
||||||
qmlRegisterSingletonType<HomeScreenUtils>(uri, 0, 1, "HomeScreenUtils",
|
qmlRegisterSingletonType<HomeScreenUtils>(uri, 0, 1, "HomeScreenUtils",
|
||||||
[](QQmlEngine *, QJSEngine *) {
|
[](QQmlEngine *, QJSEngine *) {
|
||||||
return new HomeScreenUtils{};
|
return new HomeScreenUtils{};
|
||||||
|
|
|
||||||
40
components/mobileshell/CMakeLists.txt
Normal file
40
components/mobileshell/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KWin.ScreenShot2.xml
|
||||||
|
dbus/org.kde.KScreen.xml)
|
||||||
|
|
||||||
|
set(mobileshellplugin_SRCS
|
||||||
|
mobileshellplugin.cpp
|
||||||
|
shellutil.cpp
|
||||||
|
quicksettingsmodel.cpp
|
||||||
|
notifications/notificationthumbnailer.cpp
|
||||||
|
notifications/notificationfilemenu.cpp
|
||||||
|
${DBUS_SRCS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(mobileshellplugin ${mobileshellplugin_SRCS})
|
||||||
|
|
||||||
|
target_link_libraries(mobileshellplugin
|
||||||
|
PUBLIC
|
||||||
|
Qt::Core
|
||||||
|
PRIVATE
|
||||||
|
Qt::DBus
|
||||||
|
Qt::Qml
|
||||||
|
Qt::Gui
|
||||||
|
Qt::Quick
|
||||||
|
KF5::ConfigWidgets # for KStandardAction
|
||||||
|
KF5::KIOGui
|
||||||
|
KF5::Plasma
|
||||||
|
KF5::I18n
|
||||||
|
KF5::Notifications
|
||||||
|
KF5::PlasmaQuick
|
||||||
|
KF5::KIOGui
|
||||||
|
KF5::KIOWidgets # for PreviewJob
|
||||||
|
KF5::WaylandClient
|
||||||
|
KF5::Service
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS mobileshellplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/mobileshell)
|
||||||
|
|
||||||
|
install(DIRECTORY qml/ DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/mobileshell)
|
||||||
30
components/mobileshell/mobileshellplugin.cpp
Normal file
30
components/mobileshell/mobileshellplugin.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QQmlContext>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include "mobileshellplugin.h"
|
||||||
|
#include "notifications/notificationfilemenu.h"
|
||||||
|
#include "notifications/notificationthumbnailer.h"
|
||||||
|
#include "quicksettingsmodel.h"
|
||||||
|
#include "shellutil.h"
|
||||||
|
|
||||||
|
void MobileShellPlugin::registerTypes(const char *uri)
|
||||||
|
{
|
||||||
|
Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.plasma.private.mobileshell"));
|
||||||
|
|
||||||
|
qmlRegisterSingletonType<ShellUtil>(uri, 1, 0, "ShellUtil", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
||||||
|
return ShellUtil::instance();
|
||||||
|
});
|
||||||
|
|
||||||
|
qmlRegisterType<QuickSetting>(uri, 1, 0, "QuickSetting");
|
||||||
|
qmlRegisterType<QuickSettingsModel>(uri, 1, 0, "QuickSettingsModel");
|
||||||
|
|
||||||
|
// notifications
|
||||||
|
qmlRegisterType<NotificationThumbnailer>(uri, 1, 0, "NotificationThumbnailer");
|
||||||
|
qmlRegisterType<NotificationFileMenu>(uri, 1, 0, "NotificationFileMenu");
|
||||||
|
}
|
||||||
21
components/mobileshell/mobileshellplugin.h
Normal file
21
components/mobileshell/mobileshellplugin.h
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQmlExtensionPlugin>
|
||||||
|
|
||||||
|
class MobileShellPlugin : public QQmlExtensionPlugin
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
|
||||||
|
|
||||||
|
public:
|
||||||
|
void registerTypes(const char *uri) override;
|
||||||
|
};
|
||||||
231
components/mobileshell/notifications/notificationfilemenu.cpp
Normal file
231
components/mobileshell/notifications/notificationfilemenu.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2016, 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "notificationfilemenu.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <KConfigGroup>
|
||||||
|
#include <KFileItemActions>
|
||||||
|
#include <KFileItemListProperties>
|
||||||
|
#include <KLocalizedString>
|
||||||
|
#include <KPropertiesDialog>
|
||||||
|
#include <KProtocolManager>
|
||||||
|
#include <KSharedConfig>
|
||||||
|
#include <KStandardAction>
|
||||||
|
#include <KUrlMimeData>
|
||||||
|
|
||||||
|
#include <KIO/CopyJob> // for KIO::trash
|
||||||
|
#include <KIO/DeleteJob>
|
||||||
|
#include <KIO/FileUndoManager>
|
||||||
|
#include <KIO/JobUiDelegate>
|
||||||
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
|
||||||
|
NotificationFileMenu::NotificationFileMenu(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationFileMenu::~NotificationFileMenu() = default;
|
||||||
|
|
||||||
|
QUrl NotificationFileMenu::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationFileMenu::setUrl(const QUrl &url)
|
||||||
|
{
|
||||||
|
if (m_url != url) {
|
||||||
|
m_url = url;
|
||||||
|
Q_EMIT urlChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *NotificationFileMenu::visualParent() const
|
||||||
|
{
|
||||||
|
return m_visualParent.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationFileMenu::setVisualParent(QQuickItem *visualParent)
|
||||||
|
{
|
||||||
|
if (m_visualParent.data() == visualParent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_visualParent) {
|
||||||
|
disconnect(m_visualParent.data(), nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
m_visualParent = visualParent;
|
||||||
|
if (m_visualParent) {
|
||||||
|
connect(m_visualParent.data(), &QObject::destroyed, this, &NotificationFileMenu::visualParentChanged);
|
||||||
|
}
|
||||||
|
Q_EMIT visualParentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationFileMenu::visible() const
|
||||||
|
{
|
||||||
|
return m_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationFileMenu::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
if (m_visible == visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
open(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationFileMenu::open(int x, int y)
|
||||||
|
{
|
||||||
|
if (!m_visualParent || !m_visualParent->window()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_url.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KFileItem fileItem(m_url);
|
||||||
|
|
||||||
|
auto menu = new QMenu();
|
||||||
|
menu->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
connect(menu, &QMenu::triggered, this, &NotificationFileMenu::actionTriggered);
|
||||||
|
|
||||||
|
connect(menu, &QMenu::aboutToHide, this, [this] {
|
||||||
|
m_visible = false;
|
||||||
|
Q_EMIT visibleChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (KProtocolManager::supportsListing(m_url)) {
|
||||||
|
QAction *openContainingFolderAction = menu->addAction(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Open Containing Folder"));
|
||||||
|
connect(openContainingFolderAction, &QAction::triggered, [this] {
|
||||||
|
KIO::highlightInFileManager({m_url});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto actions = new KFileItemActions(menu);
|
||||||
|
KFileItemListProperties itemProperties(KFileItemList({fileItem}));
|
||||||
|
actions->setItemListProperties(itemProperties);
|
||||||
|
actions->setParentWidget(menu);
|
||||||
|
|
||||||
|
actions->insertOpenWithActionsTo(nullptr, menu, QStringList());
|
||||||
|
|
||||||
|
// KStandardAction? But then the Ctrl+C shortcut makes no sense in this context
|
||||||
|
QAction *copyAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy"));
|
||||||
|
connect(copyAction, &QAction::triggered, this, [fileItem] {
|
||||||
|
// inspired by KDirModel::mimeData()
|
||||||
|
auto data = new QMimeData(); // who cleans it up?
|
||||||
|
KUrlMimeData::setUrls({fileItem.url()}, {fileItem.mostLocalUrl()}, data);
|
||||||
|
QApplication::clipboard()->setMimeData(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
QAction *copyPathAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy-path")), i18nc("@action:incontextmenu", "Copy Location"));
|
||||||
|
connect(copyPathAction, &QAction::triggered, this, [fileItem] {
|
||||||
|
QString path = fileItem.localPath();
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
path = fileItem.url().toDisplayString();
|
||||||
|
}
|
||||||
|
QApplication::clipboard()->setText(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
const bool canTrash = itemProperties.isLocal() && itemProperties.supportsMoving();
|
||||||
|
if (canTrash) {
|
||||||
|
auto moveToTrashLambda = [this] {
|
||||||
|
const QList<QUrl> urls{m_url};
|
||||||
|
|
||||||
|
KIO::JobUiDelegate uiDelegate;
|
||||||
|
if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
|
||||||
|
auto *job = KIO::trash(urls);
|
||||||
|
job->uiDelegate()->setAutoErrorHandlingEnabled(true);
|
||||||
|
KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl(QStringLiteral("trash:/")), job);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto moveToTrashAction = KStandardAction::moveToTrash(this, moveToTrashLambda, menu);
|
||||||
|
moveToTrashAction->setShortcut({}); // Can't focus notification to press Delete
|
||||||
|
menu->addAction(moveToTrashAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
KConfigGroup cg(KSharedConfig::openConfig(), "KDE");
|
||||||
|
const bool showDeleteCommand = cg.readEntry("ShowDeleteCommand", false);
|
||||||
|
|
||||||
|
if (itemProperties.supportsDeleting() && (!canTrash || showDeleteCommand)) {
|
||||||
|
auto deleteLambda = [this] {
|
||||||
|
const QList<QUrl> urls{m_url};
|
||||||
|
|
||||||
|
KIO::JobUiDelegate uiDelegate;
|
||||||
|
if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
|
||||||
|
auto *job = KIO::del(urls);
|
||||||
|
job->uiDelegate()->setAutoErrorHandlingEnabled(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto deleteAction = KStandardAction::deleteFile(this, deleteLambda, menu);
|
||||||
|
deleteAction->setShortcut({});
|
||||||
|
menu->addAction(deleteAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
actions->addActionsTo(menu);
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
|
||||||
|
QAction *propertiesAction = menu->addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18n("Properties"));
|
||||||
|
connect(propertiesAction, &QAction::triggered, [fileItem] {
|
||||||
|
KPropertiesDialog *dialog = new KPropertiesDialog(fileItem.url());
|
||||||
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
dialog->show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// this is a workaround where Qt will fail to realize a mouse has been released
|
||||||
|
// this happens if a window which does not accept focus spawns a new window that takes focus and X grab
|
||||||
|
// whilst the mouse is depressed
|
||||||
|
// https://bugreports.qt.io/browse/QTBUG-59044
|
||||||
|
// this causes the next click to go missing
|
||||||
|
|
||||||
|
// by releasing manually we avoid that situation
|
||||||
|
auto ungrabMouseHack = [this]() {
|
||||||
|
if (m_visualParent && m_visualParent->window() && m_visualParent->window()->mouseGrabberItem()) {
|
||||||
|
m_visualParent->window()->mouseGrabberItem()->ungrabMouse();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTimer::singleShot(0, m_visualParent, ungrabMouseHack);
|
||||||
|
// end workaround
|
||||||
|
|
||||||
|
QPoint pos;
|
||||||
|
if (x == -1 && y == -1) { // align "bottom left of visualParent"
|
||||||
|
menu->adjustSize();
|
||||||
|
|
||||||
|
pos = m_visualParent->mapToGlobal(QPointF(0, m_visualParent->height())).toPoint();
|
||||||
|
|
||||||
|
if (!qApp->isRightToLeft()) {
|
||||||
|
pos.rx() += m_visualParent->width();
|
||||||
|
pos.rx() -= menu->width();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pos = m_visualParent->mapToGlobal(QPointF(x, y)).toPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
menu->winId();
|
||||||
|
menu->windowHandle()->setTransientParent(m_visualParent->window());
|
||||||
|
menu->popup(pos);
|
||||||
|
|
||||||
|
m_visible = true;
|
||||||
|
Q_EMIT visibleChanged();
|
||||||
|
}
|
||||||
49
components/mobileshell/notifications/notificationfilemenu.h
Normal file
49
components/mobileshell/notifications/notificationfilemenu.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2016, 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
|
||||||
|
class NotificationFileMenu : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||||
|
Q_PROPERTY(QQuickItem *visualParent READ visualParent WRITE setVisualParent NOTIFY visualParentChanged)
|
||||||
|
Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NotificationFileMenu(QObject *parent = nullptr);
|
||||||
|
~NotificationFileMenu() override;
|
||||||
|
|
||||||
|
QUrl url() const;
|
||||||
|
void setUrl(const QUrl &url);
|
||||||
|
|
||||||
|
QQuickItem *visualParent() const;
|
||||||
|
void setVisualParent(QQuickItem *visualParent);
|
||||||
|
|
||||||
|
bool visible() const;
|
||||||
|
void setVisible(bool visible);
|
||||||
|
|
||||||
|
Q_INVOKABLE void open(int x, int y);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void actionTriggered(QAction *action);
|
||||||
|
|
||||||
|
void urlChanged();
|
||||||
|
void visualParentChanged();
|
||||||
|
void visibleChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl m_url;
|
||||||
|
QPointer<QQuickItem> m_visualParent;
|
||||||
|
bool m_visible = false;
|
||||||
|
};
|
||||||
157
components/mobileshell/notifications/notificationthumbnailer.cpp
Normal file
157
components/mobileshell/notifications/notificationthumbnailer.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "notificationthumbnailer.h"
|
||||||
|
|
||||||
|
#include <KIO/PreviewJob>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QQuickItem>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <KConfigGroup>
|
||||||
|
#include <KFileItemListProperties>
|
||||||
|
#include <KLocalizedString>
|
||||||
|
#include <KPropertiesDialog>
|
||||||
|
#include <KProtocolManager>
|
||||||
|
#include <KSharedConfig>
|
||||||
|
#include <KUrlMimeData>
|
||||||
|
|
||||||
|
#include <KIO/OpenFileManagerWindowJob>
|
||||||
|
|
||||||
|
NotificationThumbnailer::NotificationThumbnailer(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationThumbnailer::~NotificationThumbnailer() = default;
|
||||||
|
|
||||||
|
void NotificationThumbnailer::classBegin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationThumbnailer::componentComplete()
|
||||||
|
{
|
||||||
|
m_inited = true;
|
||||||
|
generatePreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl NotificationThumbnailer::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationThumbnailer::setUrl(const QUrl &url)
|
||||||
|
{
|
||||||
|
if (m_url != url) {
|
||||||
|
m_url = url;
|
||||||
|
Q_EMIT urlChanged();
|
||||||
|
|
||||||
|
generatePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize NotificationThumbnailer::size() const
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationThumbnailer::setSize(const QSize &size)
|
||||||
|
{
|
||||||
|
if (m_size != size) {
|
||||||
|
m_size = size;
|
||||||
|
Q_EMIT sizeChanged();
|
||||||
|
|
||||||
|
generatePreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationThumbnailer::busy() const
|
||||||
|
{
|
||||||
|
return m_busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationThumbnailer::hasPreview() const
|
||||||
|
{
|
||||||
|
return !m_pixmap.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap NotificationThumbnailer::pixmap() const
|
||||||
|
{
|
||||||
|
return m_pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize NotificationThumbnailer::pixmapSize() const
|
||||||
|
{
|
||||||
|
return m_pixmap.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NotificationThumbnailer::iconName() const
|
||||||
|
{
|
||||||
|
return m_iconName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NotificationThumbnailer::menuVisible() const
|
||||||
|
{
|
||||||
|
return m_menuVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotificationThumbnailer::generatePreview()
|
||||||
|
{
|
||||||
|
if (!m_inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_url.isValid() || !m_url.isLocalFile() || !m_size.isValid() || m_size.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto maxSize = qMax(m_size.width(), m_size.height());
|
||||||
|
|
||||||
|
KConfigGroup previewSettings(KSharedConfig::openConfig(QStringLiteral("dolphinrc")), "PreviewSettings");
|
||||||
|
const QStringList enabledPlugins = previewSettings.readEntry("Plugins", KIO::PreviewJob::defaultPlugins());
|
||||||
|
|
||||||
|
KIO::PreviewJob *job = KIO::filePreview(KFileItemList({KFileItem(m_url)}), QSize(maxSize, maxSize), &enabledPlugins);
|
||||||
|
job->setScaleType(KIO::PreviewJob::Scaled);
|
||||||
|
job->setIgnoreMaximumSize(true);
|
||||||
|
|
||||||
|
connect(job, &KIO::PreviewJob::gotPreview, this, [this](const KFileItem &item, const QPixmap &preview) {
|
||||||
|
Q_UNUSED(item);
|
||||||
|
m_pixmap = preview;
|
||||||
|
Q_EMIT pixmapChanged();
|
||||||
|
|
||||||
|
if (!m_iconName.isEmpty()) {
|
||||||
|
m_iconName.clear();
|
||||||
|
Q_EMIT iconNameChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) {
|
||||||
|
m_pixmap = QPixmap();
|
||||||
|
Q_EMIT pixmapChanged();
|
||||||
|
|
||||||
|
const QString &iconName = item.determineMimeType().iconName();
|
||||||
|
if (m_iconName != iconName) {
|
||||||
|
m_iconName = iconName;
|
||||||
|
Q_EMIT iconNameChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(job, &KJob::result, this, [this] {
|
||||||
|
m_busy = false;
|
||||||
|
Q_EMIT busyChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
m_busy = true;
|
||||||
|
Q_EMIT busyChanged();
|
||||||
|
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlParserStatus>
|
||||||
|
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class NotificationThumbnailer : public QObject, public QQmlParserStatus
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(QQmlParserStatus)
|
||||||
|
|
||||||
|
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||||
|
Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||||
|
Q_PROPERTY(bool hasPreview READ hasPreview NOTIFY pixmapChanged)
|
||||||
|
Q_PROPERTY(QPixmap pixmap READ pixmap NOTIFY pixmapChanged)
|
||||||
|
Q_PROPERTY(QSize pixmapSize READ pixmapSize NOTIFY pixmapChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString iconName READ iconName NOTIFY iconNameChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool menuVisible READ menuVisible NOTIFY menuVisibleChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit NotificationThumbnailer(QObject *parent = nullptr);
|
||||||
|
~NotificationThumbnailer() override;
|
||||||
|
|
||||||
|
QUrl url() const;
|
||||||
|
void setUrl(const QUrl &url);
|
||||||
|
|
||||||
|
QSize size() const;
|
||||||
|
void setSize(const QSize &size);
|
||||||
|
|
||||||
|
bool busy() const;
|
||||||
|
bool hasPreview() const;
|
||||||
|
QPixmap pixmap() const;
|
||||||
|
QSize pixmapSize() const;
|
||||||
|
|
||||||
|
QString iconName() const;
|
||||||
|
|
||||||
|
bool menuVisible() const;
|
||||||
|
|
||||||
|
void classBegin() override;
|
||||||
|
void componentComplete() override;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void menuVisibleChanged();
|
||||||
|
|
||||||
|
void urlChanged();
|
||||||
|
void sizeChanged();
|
||||||
|
void busyChanged();
|
||||||
|
void pixmapChanged();
|
||||||
|
void iconNameChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void generatePreview();
|
||||||
|
|
||||||
|
bool m_inited = false;
|
||||||
|
|
||||||
|
bool m_menuVisible = false;
|
||||||
|
|
||||||
|
QUrl m_url;
|
||||||
|
QSize m_size;
|
||||||
|
|
||||||
|
bool m_busy = false;
|
||||||
|
|
||||||
|
QPixmap m_pixmap;
|
||||||
|
|
||||||
|
QString m_iconName;
|
||||||
|
};
|
||||||
256
components/mobileshell/qml/actiondrawer/ActionDrawer.qml
Normal file
256
components/mobileshell/qml/actiondrawer/ActionDrawer.qml
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||||
|
|
||||||
|
import "../components" as Components
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swipe top left - minimized quick settings, fully shown notifications list
|
||||||
|
* Swipe top right - full quick settings, minimized notifications list
|
||||||
|
* Swiping up and down on notifications list toggle minimized/maximized
|
||||||
|
* Swiping up and down on panel hides and shows the panel
|
||||||
|
*/
|
||||||
|
|
||||||
|
NanoShell.FullScreenOverlay {
|
||||||
|
id: window
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of pixels moved by touch/mouse in the process of opening/closing the panel.
|
||||||
|
*/
|
||||||
|
property real offset: 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the panel is being dragged.
|
||||||
|
*/
|
||||||
|
property bool dragging: false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the panel is open after touch/mouse release from the first opening swipe.
|
||||||
|
*/
|
||||||
|
property bool opened: false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Direction the panel is currently moving in.
|
||||||
|
*/
|
||||||
|
property int direction: Components.Direction.None
|
||||||
|
|
||||||
|
property int mode: (height > width && width <= largePortraitThreshold) ? ActionDrawer.Portrait : ActionDrawer.Landscape
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At some point, even if the screen is technically portrait, if we have a ton of width it'd be best to just show the landscape mode.
|
||||||
|
*/
|
||||||
|
readonly property real largePortraitThreshold: PlasmaCore.Units.gridUnit * 35
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
Portrait = 0,
|
||||||
|
Landscape
|
||||||
|
}
|
||||||
|
|
||||||
|
width: Screen.width
|
||||||
|
height: Screen.height
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
onOpenedChanged: {
|
||||||
|
if (opened) flickable.focus = true;
|
||||||
|
}
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real oldOffset
|
||||||
|
onOffsetChanged: {
|
||||||
|
if (offset < 0) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
window.direction = (oldOffset === offset)
|
||||||
|
? Components.Direction.None
|
||||||
|
: (offset > oldOffset ? Components.Direction.Down : Components.Direction.Up);
|
||||||
|
|
||||||
|
oldOffset = offset;
|
||||||
|
|
||||||
|
// close panel immediately after panel is not shown, and the flickable is not being dragged
|
||||||
|
if (opened && window.offset <= 0 && !flickable.dragging && !closeAnim.running && !openAnim.running) {
|
||||||
|
window.updateState();
|
||||||
|
focus = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelAnimations() {
|
||||||
|
closeAnim.stop();
|
||||||
|
openAnim.stop();
|
||||||
|
}
|
||||||
|
function open() {
|
||||||
|
cancelAnimations();
|
||||||
|
openAnim.restart();
|
||||||
|
}
|
||||||
|
function closeImmediately() {
|
||||||
|
cancelAnimations();
|
||||||
|
offset = 0;
|
||||||
|
closeAnim.finished();
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
cancelAnimations();
|
||||||
|
closeAnim.restart();
|
||||||
|
}
|
||||||
|
function expand() {
|
||||||
|
cancelAnimations();
|
||||||
|
expandAnim.restart();
|
||||||
|
}
|
||||||
|
function updateState() {
|
||||||
|
cancelAnimations();
|
||||||
|
let openThreshold = PlasmaCore.Units.gridUnit;
|
||||||
|
|
||||||
|
if (window.offset <= 0) {
|
||||||
|
// close immediately, so that we don't have to wait PlasmaCore.Units.longDuration
|
||||||
|
window.visible = false;
|
||||||
|
close();
|
||||||
|
} else if (window.direction === Components.Direction.None || !window.opened) {
|
||||||
|
if (window.offset < openThreshold) {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
} else if (window.offset > contentContainerLoader.maximizedQuickSettingsOffset) {
|
||||||
|
expand();
|
||||||
|
} else if (window.offset > contentContainerLoader.minimizedQuickSettingsOffset) {
|
||||||
|
if (window.direction === Components.Direction.Down) {
|
||||||
|
expand();
|
||||||
|
} else {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
} else if (window.direction === Components.Direction.Down) {
|
||||||
|
open();
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: updateStateTimer
|
||||||
|
interval: 0
|
||||||
|
onTriggered: updateState()
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyAnimation on offset {
|
||||||
|
id: closeAnim
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
to: 0
|
||||||
|
onFinished: {
|
||||||
|
window.visible = false;
|
||||||
|
window.opened = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PropertyAnimation on offset {
|
||||||
|
id: openAnim
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
to: contentContainerLoader.minimizedQuickSettingsOffset
|
||||||
|
onFinished: window.opened = true
|
||||||
|
}
|
||||||
|
PropertyAnimation on offset {
|
||||||
|
id: expandAnim
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
to: contentContainerLoader.maximizedQuickSettingsOffset
|
||||||
|
onFinished: window.opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: flickable
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
contentWidth: window.width
|
||||||
|
contentHeight: window.height + 999999
|
||||||
|
contentY: contentHeight / 2
|
||||||
|
|
||||||
|
// if the recent window.offset change was due to this flickable
|
||||||
|
property bool offsetChangedDueToContentY: false
|
||||||
|
Connections {
|
||||||
|
target: window
|
||||||
|
function onOffsetChanged() {
|
||||||
|
if (!flickable.offsetChangedDueToContentY) {
|
||||||
|
// ensure the flickable's contentY is not moving when other sources change window.offset
|
||||||
|
flickable.cancelFlick();
|
||||||
|
}
|
||||||
|
flickable.offsetChangedDueToContentY = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real oldContentY
|
||||||
|
onContentYChanged: {
|
||||||
|
offsetChangedDueToContentY = true;
|
||||||
|
window.offset += oldContentY - contentY;
|
||||||
|
oldContentY = contentY;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMovementStarted: {
|
||||||
|
window.cancelAnimations();
|
||||||
|
window.dragging = true;
|
||||||
|
}
|
||||||
|
onFlickStarted: window.dragging = true;
|
||||||
|
onMovementEnded: {
|
||||||
|
window.dragging = false;
|
||||||
|
window.updateState();
|
||||||
|
}
|
||||||
|
onFlickEnded: {
|
||||||
|
window.dragging = true;
|
||||||
|
window.updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDraggingChanged: {
|
||||||
|
if (!dragging) {
|
||||||
|
window.dragging = false;
|
||||||
|
flickable.cancelFlick();
|
||||||
|
window.updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the flickable is only used to measure drag changes, we implement our own UI component movements
|
||||||
|
// the window element is not affected by contentY changes (it's effectively anchored to the flickable)
|
||||||
|
Loader {
|
||||||
|
id: contentContainerLoader
|
||||||
|
|
||||||
|
property real minimizedQuickSettingsOffset: item ? item.minimizedQuickSettingsOffset : 0
|
||||||
|
property real maximizedQuickSettingsOffset: item ? item.maximizedQuickSettingsOffset : 0
|
||||||
|
|
||||||
|
y: flickable.contentY
|
||||||
|
width: window.width
|
||||||
|
height: window.height
|
||||||
|
|
||||||
|
sourceComponent: window.mode == ActionDrawer.Portrait ? portraitContentContainer : landscapeContentContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: portraitContentContainer
|
||||||
|
PortraitContentContainer {
|
||||||
|
actionDrawer: window
|
||||||
|
width: window.width
|
||||||
|
height: window.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: landscapeContentContainer
|
||||||
|
LandscapeContentContainer {
|
||||||
|
actionDrawer: window
|
||||||
|
width: window.width
|
||||||
|
height: window.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that triggers the opening and closing of an ActionDrawer when dragged on with touch or mouse.
|
||||||
|
*/
|
||||||
|
MouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property ActionDrawer actionDrawer
|
||||||
|
|
||||||
|
property int oldMouseY: 0
|
||||||
|
|
||||||
|
function startSwipe() {
|
||||||
|
actionDrawer.cancelAnimations();
|
||||||
|
actionDrawer.dragging = true;
|
||||||
|
actionDrawer.opened = false;
|
||||||
|
|
||||||
|
// must be after properties other are set, we cannot have actionDrawer.updateState() be called
|
||||||
|
actionDrawer.offset = 0;
|
||||||
|
actionDrawer.oldOffset = 0;
|
||||||
|
actionDrawer.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function endSwipe() {
|
||||||
|
actionDrawer.dragging = false;
|
||||||
|
actionDrawer.updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateOffset(offsetY) {
|
||||||
|
actionDrawer.offset += offsetY;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: {
|
||||||
|
oldMouseY = mouse.y;
|
||||||
|
startSwipe();
|
||||||
|
}
|
||||||
|
onReleased: endSwipe()
|
||||||
|
onCanceled: endSwipe()
|
||||||
|
onPositionChanged: {
|
||||||
|
updateOffset(mouse.y - oldMouseY);
|
||||||
|
oldMouseY = mouse.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import "../components" as Components
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import "quicksettings"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root element that contains all of the ActionDrawer's contents, and is anchored to the screen.
|
||||||
|
*/
|
||||||
|
PlasmaCore.ColorScope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
|
readonly property real minimizedQuickSettingsOffset: height
|
||||||
|
readonly property real maximizedQuickSettingsOffset: height
|
||||||
|
|
||||||
|
colorGroup: PlasmaCore.Theme.ViewColorGroup
|
||||||
|
|
||||||
|
function applyMinMax(val) {
|
||||||
|
return Math.max(0, Math.min(1, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullscreen background
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(PlasmaCore.Theme.backgroundColor.r, PlasmaCore.Theme.backgroundColor.g, PlasmaCore.Theme.backgroundColor.b, 0.95)
|
||||||
|
opacity: Math.max(0, Math.min(1, actionDrawer.offset / root.minimizedQuickSettingsOffset))
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaCore.DataSource {
|
||||||
|
id: timeSource
|
||||||
|
engine: "time"
|
||||||
|
connectedSources: ["Local"]
|
||||||
|
interval: 60 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
// left side
|
||||||
|
ColumnLayout {
|
||||||
|
opacity: applyMinMax(root.actionDrawer.offset / root.maximizedQuickSettingsOffset)
|
||||||
|
spacing: 0
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
topMargin: Math.min(root.width, root.height) * 0.06
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: Math.min(root.width, root.height) * 0.06
|
||||||
|
right: quickSettings.left
|
||||||
|
rightMargin: Math.min(root.width, root.height) * 0.06
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: Math.min(root.width, root.height) * 0.06
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: clock
|
||||||
|
text: Qt.formatTime(timeSource.data.Local.DateTime, MobileShell.ShellUtil.isSystem24HourFormat ? "h:mm" : "h:mm ap")
|
||||||
|
verticalAlignment: Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
font.pixelSize: Math.min(40, Math.min(root.width, root.height) * 0.1)
|
||||||
|
font.weight: Font.ExtraLight
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: date
|
||||||
|
text: Qt.formatDate(timeSource.data.Local.DateTime, "ddd MMMM d")
|
||||||
|
verticalAlignment: Qt.AlignTop
|
||||||
|
color: PlasmaCore.ColorScope.disabledTextColor
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
font.pixelSize: Math.min(20, Math.min(root.width, root.height) * 0.05)
|
||||||
|
font.weight: Font.Light
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.NotificationsWidget {
|
||||||
|
// don't allow notifications widget to get too wide
|
||||||
|
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Math.min(root.width, root.height) * 0.02
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// right sidebar
|
||||||
|
QuickSettingsPanel {
|
||||||
|
id: quickSettings
|
||||||
|
height: Math.min(root.height, Math.max(quickSettings.minimizedHeight, actionDrawer.offset))
|
||||||
|
width: intendedWidth
|
||||||
|
|
||||||
|
readonly property real intendedWidth: 360
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
actionDrawer: root.actionDrawer
|
||||||
|
fullHeight: root.height
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
id: translate
|
||||||
|
y: Math.min(root.actionDrawer.offset - quickSettings.minimizedHeight, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
import "../components" as Components
|
||||||
|
import "../widgets" as Widgets
|
||||||
|
import "quicksettings"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root element that contains all of the ActionDrawer's contents, and is anchored to the screen.
|
||||||
|
*/
|
||||||
|
PlasmaCore.ColorScope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
|
readonly property real minimizedQuickSettingsOffset: quickSettings.minimizedHeight
|
||||||
|
readonly property real maximizedQuickSettingsOffset: minimizedQuickSettingsOffset + quickSettings.maxAddedHeight
|
||||||
|
|
||||||
|
colorGroup: PlasmaCore.Theme.ViewColorGroup
|
||||||
|
|
||||||
|
function applyMinMax(val) {
|
||||||
|
return Math.max(0, Math.min(1, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullscreen background
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(PlasmaCore.Theme.backgroundColor.r, PlasmaCore.Theme.backgroundColor.g, PlasmaCore.Theme.backgroundColor.b, 0.95)
|
||||||
|
opacity: Math.max(0, Math.min(1, actionDrawer.offset / root.minimizedQuickSettingsOffset))
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickSettingsDrawer {
|
||||||
|
id: quickSettings
|
||||||
|
z: 1 // ensure it's above notifications
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
actionDrawer: root.actionDrawer
|
||||||
|
|
||||||
|
// opacity and move animation
|
||||||
|
property real offsetDist: actionDrawer.offset - minimizedQuickSettingsOffset
|
||||||
|
property real totalOffsetDist: maximizedQuickSettingsOffset - minimizedQuickSettingsOffset
|
||||||
|
minimizedToFullProgress: actionDrawer.opened ? applyMinMax(offsetDist / totalOffsetDist) : 0
|
||||||
|
|
||||||
|
addedHeight: {
|
||||||
|
if (!actionDrawer.opened) {
|
||||||
|
// over-scroll effect for initial opening
|
||||||
|
let progress = (root.actionDrawer.offset - minimizedQuickSettingsOffset) / quickSettings.maxAddedHeight;
|
||||||
|
let effectProgress = Math.atan(Math.max(0, progress));
|
||||||
|
return quickSettings.maxAddedHeight * 0.25 * effectProgress;
|
||||||
|
} else {
|
||||||
|
return Math.max(0, Math.min(quickSettings.maxAddedHeight, root.actionDrawer.offset - minimizedQuickSettingsOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
id: translate
|
||||||
|
y: Math.min(root.actionDrawer.offset - minimizedQuickSettingsOffset, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.NotificationsWidget {
|
||||||
|
anchors {
|
||||||
|
top: quickSettings.top
|
||||||
|
topMargin: quickSettings.height + translate.y
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
}
|
||||||
|
opacity: applyMinMax(root.actionDrawer.offset / root.minimizedQuickSettingsOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2012-2013 Daniel Nicoletti <dantti12@gmail.com>
|
* SPDX-FileCopyrightText: 2012-2013 Daniel Nicoletti <dantti12@gmail.com>
|
||||||
* SPDX-FileCopyrightText: 2013, 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
* SPDX-FileCopyrightText: 2013, 2015 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
|
@ -15,7 +14,7 @@ import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PC3
|
import org.kde.plasma.components 3.0 as PC3
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: brightnessRoot
|
id: root
|
||||||
|
|
||||||
implicitHeight: brightnessRow.implicitHeight
|
implicitHeight: brightnessRow.implicitHeight
|
||||||
|
|
||||||
|
|
@ -23,14 +22,31 @@ Item {
|
||||||
property bool disableBrightnessUpdate: true
|
property bool disableBrightnessUpdate: true
|
||||||
readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0
|
readonly property int maximumScreenBrightness: pmSource.data["PowerDevil"] ? pmSource.data["PowerDevil"]["Maximum Screen Brightness"] || 0 : 0
|
||||||
|
|
||||||
|
property QtObject updateScreenBrightnessJob
|
||||||
|
|
||||||
|
function updateBrightnessUI() {
|
||||||
|
if (updateScreenBrightnessJob)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.disableBrightnessUpdate = true;
|
||||||
|
console.log(pmSource.data["PowerDevil"]["Screen Brightness"]);
|
||||||
|
root.screenBrightness = pmSource.data["PowerDevil"]["Screen Brightness"];
|
||||||
|
root.disableBrightnessUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
onScreenBrightnessChanged: {
|
onScreenBrightnessChanged: {
|
||||||
brightnessSlider.value = brightnessRoot.screenBrightness
|
brightnessSlider.value = root.screenBrightness
|
||||||
|
|
||||||
if (!disableBrightnessUpdate) {
|
if (!disableBrightnessUpdate) {
|
||||||
var service = pmSource.serviceForSource("PowerDevil");
|
var service = pmSource.serviceForSource("PowerDevil");
|
||||||
var operation = service.operationDescription("setBrightness");
|
var operation = service.operationDescription("setBrightness");
|
||||||
operation.brightness = screenBrightness;
|
operation.brightness = screenBrightness;
|
||||||
operation.silent = true
|
operation.silent = true; // don't show OSD
|
||||||
service.startOperationCall(operation);
|
|
||||||
|
updateScreenBrightnessJob = service.startOperationCall(operation);
|
||||||
|
updateScreenBrightnessJob.finished.connect(function (job) {
|
||||||
|
root.updateBrightnessUI();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,18 +60,7 @@ Item {
|
||||||
connectSource(source);
|
connectSource(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onDataChanged: {
|
onDataChanged: root.updateBrightnessUI()
|
||||||
disableBrightnessUpdate = true;
|
|
||||||
brightnessRoot.screenBrightness = pmSource.data["PowerDevil"]["Screen Brightness"];
|
|
||||||
disableBrightnessUpdate = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
brightnessSlider.moved.connect(function() {
|
|
||||||
brightnessRoot.screenBrightness = brightnessSlider.value;
|
|
||||||
});
|
|
||||||
disableBrightnessUpdate = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
@ -69,24 +74,26 @@ Item {
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.leftMargin: PlasmaCore.Units.smallSpacing
|
Layout.leftMargin: PlasmaCore.Units.smallSpacing
|
||||||
Layout.preferredWidth: Math.round(PlasmaCore.Units.gridUnit * 1.75)
|
Layout.preferredWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
Layout.preferredHeight: width
|
Layout.preferredHeight: width
|
||||||
source: "low-brightness"
|
source: "low-brightness"
|
||||||
}
|
}
|
||||||
|
|
||||||
Slider {
|
PC3.Slider {
|
||||||
id: brightnessSlider
|
id: brightnessSlider
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
value: screenBrightness
|
|
||||||
from: 1
|
from: 1
|
||||||
to: maximumScreenBrightness
|
to: root.maximumScreenBrightness
|
||||||
|
value: root.screenBrightness
|
||||||
|
|
||||||
|
onMoved: root.screenBrightness = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
||||||
Layout.preferredWidth: Math.round(PlasmaCore.Units.gridUnit * 1.75)
|
Layout.preferredWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
Layout.preferredHeight: width
|
Layout.preferredHeight: width
|
||||||
source: "high-brightness"
|
source: "high-brightness"
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: handle
|
||||||
|
|
||||||
|
signal tapped()
|
||||||
|
|
||||||
|
implicitWidth: PlasmaCore.Units.gridUnit * 3
|
||||||
|
implicitHeight: 3
|
||||||
|
radius: height
|
||||||
|
color: PlasmaCore.Theme.textColor
|
||||||
|
opacity: 0.5
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.PointingHandCursor
|
||||||
|
onTapped: handle.tapped()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
|
import "../../components" as Components
|
||||||
|
import "../../components/util.js" as Util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick settings elements layout, change the height to clip.
|
||||||
|
*/
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
|
readonly property real columns: Math.round(Util.applyMinMaxRange(3, 6, width / intendedColumnWidth))
|
||||||
|
readonly property real columnWidth: Math.floor(width / columns)
|
||||||
|
readonly property real minimizedColumns: Math.round(Util.applyMinMaxRange(5, 8, width / intendedMinimizedColumnWidth))
|
||||||
|
readonly property real minimizedColumnWidth: Math.floor(width / minimizedColumns)
|
||||||
|
|
||||||
|
readonly property real rowHeight: columnWidth * 0.7
|
||||||
|
readonly property real fullHeight: fullView.implicitHeight
|
||||||
|
|
||||||
|
readonly property real intendedColumnWidth: 120
|
||||||
|
readonly property real intendedMinimizedColumnWidth: PlasmaCore.Units.gridUnit * 3 + PlasmaCore.Units.largeSpacing
|
||||||
|
readonly property real minimizedRowHeight: PlasmaCore.Units.gridUnit * 3 + PlasmaCore.Units.largeSpacing
|
||||||
|
|
||||||
|
property real minimizedViewProgress: 0
|
||||||
|
property real fullViewProgress: 1
|
||||||
|
|
||||||
|
readonly property SettingsModel quickSettingsModel: SettingsModel {
|
||||||
|
actionDrawer: root.actionDrawer
|
||||||
|
}
|
||||||
|
|
||||||
|
// view when fully open
|
||||||
|
ColumnLayout {
|
||||||
|
id: fullView
|
||||||
|
opacity: root.fullViewProgress
|
||||||
|
visible: opacity !== 0
|
||||||
|
transform: Translate { y: (1 - fullView.opacity) * root.rowHeight }
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
// TODO add pages
|
||||||
|
Flow {
|
||||||
|
id: flow
|
||||||
|
spacing: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.quickSettingsModel
|
||||||
|
delegate: Components.BaseItem {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
height: root.rowHeight
|
||||||
|
width: root.columnWidth
|
||||||
|
padding: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
contentItem: QuickSettingsFullDelegate {
|
||||||
|
text: modelData.text
|
||||||
|
status: modelData.status
|
||||||
|
icon: modelData.icon
|
||||||
|
enabled: modelData.enabled
|
||||||
|
settingsCommand: modelData.settingsCommand
|
||||||
|
toggleFunction: modelData.toggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BrightnessItem {
|
||||||
|
id: brightnessItem
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing * 2
|
||||||
|
Layout.bottomMargin: PlasmaCore.Units.smallSpacing * 2
|
||||||
|
Layout.leftMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.rightMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// view when in minimized mode
|
||||||
|
RowLayout {
|
||||||
|
id: minimizedView
|
||||||
|
spacing: 0
|
||||||
|
opacity: root.minimizedViewProgress
|
||||||
|
visible: opacity !== 0
|
||||||
|
transform: Translate { y: (1 - minimizedView.opacity) * -root.rowHeight }
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.quickSettingsModel
|
||||||
|
delegate: Components.BaseItem {
|
||||||
|
required property var modelData
|
||||||
|
required property var index
|
||||||
|
|
||||||
|
implicitHeight: root.minimizedRowHeight
|
||||||
|
implicitWidth: root.minimizedColumnWidth
|
||||||
|
horizontalPadding: (width - PlasmaCore.Units.gridUnit * 3) / 2
|
||||||
|
verticalPadding: (height - PlasmaCore.Units.gridUnit * 3) / 2
|
||||||
|
visible: index <= root.minimizedColumns
|
||||||
|
|
||||||
|
contentItem: QuickSettingsMinimizedDelegate {
|
||||||
|
text: modelData.text
|
||||||
|
status: modelData.status
|
||||||
|
icon: modelData.icon
|
||||||
|
enabled: modelData.enabled
|
||||||
|
settingsCommand: modelData.settingsCommand
|
||||||
|
toggleFunction: modelData.toggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
import "../../components" as Components
|
||||||
|
|
||||||
|
Components.BaseItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Model interface
|
||||||
|
required property string text
|
||||||
|
required property string status
|
||||||
|
required property string icon
|
||||||
|
required property bool enabled
|
||||||
|
required property string settingsCommand
|
||||||
|
required property var toggleFunction
|
||||||
|
|
||||||
|
// set by children
|
||||||
|
property var iconItem
|
||||||
|
|
||||||
|
readonly property color enabledButtonBorderColor: Qt.darker(Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {}), 1.25)
|
||||||
|
readonly property color disabledButtonBorderColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.textColor, {"alpha": 0.2*255})
|
||||||
|
readonly property color enabledButtonColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {alpha: 0.4*255})
|
||||||
|
readonly property color enabledButtonPressedColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {alpha: 0.6*255});
|
||||||
|
readonly property color disabledButtonColor: PlasmaCore.Theme.backgroundColor
|
||||||
|
readonly property color disabledButtonPressedColor: Qt.darker(disabledButtonColor, 1.1)
|
||||||
|
|
||||||
|
function delegateClick() {
|
||||||
|
if (root.toggle) {
|
||||||
|
root.toggle();
|
||||||
|
} else if (root.toggleFunction) {
|
||||||
|
root.toggleFunction();
|
||||||
|
} else if (root.settingsCommand) {
|
||||||
|
NanoShell.StartupFeedback.open(
|
||||||
|
root.icon,
|
||||||
|
root.text,
|
||||||
|
iconItem.Kirigami.ScenePosition.x + iconItem.width/2,
|
||||||
|
iconItem.Kirigami.ScenePosition.y + iconItem.height/2,
|
||||||
|
Math.min(iconItem.width, iconItem.height))
|
||||||
|
MobileShell.ShellUtil.executeCommand(root.settingsCommand);
|
||||||
|
root.closeRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function delegatePressAndHold() {
|
||||||
|
if (root.settingsCommand) {
|
||||||
|
NanoShell.StartupFeedback.open(
|
||||||
|
root.icon,
|
||||||
|
root.text,
|
||||||
|
iconItem.Kirigami.ScenePosition.x + iconItem.width/2,
|
||||||
|
iconItem.Kirigami.ScenePosition.y + iconItem.height/2,
|
||||||
|
Math.min(iconItem.width, iconItem.height))
|
||||||
|
closeRequested();
|
||||||
|
MobileShell.ShellUtil.executeCommand(root.settingsCommand);
|
||||||
|
} else if (root.toggleFunction) {
|
||||||
|
root.toggleFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import "../../statusbar" as StatusBar
|
||||||
|
import "../../components" as Components
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick settings drawer pulled down from the top (for portrait mode).
|
||||||
|
* For the landscape view quicksettings container, see QuickSettingsPanel.
|
||||||
|
*/
|
||||||
|
Components.BaseItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of height to add to the panel (increasing the height of the quick settings area).
|
||||||
|
*/
|
||||||
|
property real addedHeight: 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum amount of added height to snap to the full height of the quick settings panel.
|
||||||
|
*/
|
||||||
|
readonly property real maxAddedHeight: quickSettings.fullHeight - minimizedQuickSettingsHeight // first row is part of minimized height
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of panel when in minimized mode.
|
||||||
|
*/
|
||||||
|
readonly property real minimizedHeight: bottomPadding + topPadding + statusBar.height + minimizedQuickSettingsHeight + mediaWidget.height + handle.fullHeight
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of just the QuickSettings component in minimized mode.
|
||||||
|
*/
|
||||||
|
readonly property real minimizedQuickSettingsHeight: quickSettings.minimizedRowHeight + PlasmaCore.Units.gridUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Progress of showing the full quick settings view from pinned.
|
||||||
|
*/
|
||||||
|
property real minimizedToFullProgress: 1
|
||||||
|
|
||||||
|
// we need extra padding if the background side border is enabled
|
||||||
|
topPadding: PlasmaCore.Units.smallSpacing
|
||||||
|
leftPadding: PlasmaCore.Units.smallSpacing
|
||||||
|
rightPadding: PlasmaCore.Units.smallSpacing
|
||||||
|
bottomPadding: PlasmaCore.Units.smallSpacing * 4
|
||||||
|
|
||||||
|
background: PlasmaCore.FrameSvgItem {
|
||||||
|
enabledBorders: PlasmaCore.FrameSvg.BottomBorder
|
||||||
|
imagePath: "widgets/background"
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: containerItem
|
||||||
|
implicitHeight: column.implicitHeight
|
||||||
|
|
||||||
|
// use container item so that our column doesn't get stretched if base item is anchored
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
StatusBar.StatusBar {
|
||||||
|
id: statusBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: MobileShell.TopPanelControls.panelHeight + PlasmaCore.Units.gridUnit * 0.8
|
||||||
|
|
||||||
|
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
showSecondRow: true
|
||||||
|
showDropShadow: false
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickSettings {
|
||||||
|
id: quickSettings
|
||||||
|
Layout.preferredHeight: root.minimizedQuickSettingsHeight + root.addedHeight
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
actionDrawer: root.actionDrawer
|
||||||
|
minimizedViewProgress: 1 - root.minimizedToFullProgress
|
||||||
|
fullViewProgress: root.minimizedToFullProgress
|
||||||
|
height: root.minimizedQuickSettingsHeight + root.addedHeight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.MediaControlsWidget {
|
||||||
|
id: mediaWidget
|
||||||
|
property real fullHeight: height + Layout.topMargin
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle {
|
||||||
|
id: handle
|
||||||
|
property real fullHeight: height + Layout.topMargin
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing * 2
|
||||||
|
|
||||||
|
onTapped: {
|
||||||
|
if (root.minimizedToFullProgress < 0.5) {
|
||||||
|
root.actionDrawer.expand();
|
||||||
|
} else {
|
||||||
|
root.actionDrawer.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
import "../../components" as Components
|
||||||
|
|
||||||
|
QuickSettingsDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
padding: PlasmaCore.Units.smallSpacing * 2
|
||||||
|
iconItem: icon
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: PlasmaCore.Units.smallSpacing
|
||||||
|
border.color: root.enabled ? root.enabledButtonBorderColor : root.disabledButtonBorderColor
|
||||||
|
color: {
|
||||||
|
if (root.enabled) {
|
||||||
|
return mouseArea.pressed ? root.enabledButtonPressedColor : root.enabledButtonColor
|
||||||
|
} else {
|
||||||
|
return mouseArea.pressed ? root.disabledButtonPressedColor : root.disabledButtonColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
onClicked: root.delegateClick()
|
||||||
|
onPressAndHold: root.delegatePressAndHold()
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
id: icon
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
implicitWidth: PlasmaCore.Units.iconSizes.small
|
||||||
|
implicitHeight: width
|
||||||
|
source: root.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: root.text
|
||||||
|
font.pixelSize: PlasmaCore.Theme.defaultFont.pixelSize * 0.8 // TODO base height off of size of delegate
|
||||||
|
}
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
// if no status is given, just use On/Off
|
||||||
|
text: root.status ? root.status : (root.enabled ? i18n("On") : i18n("Off"))
|
||||||
|
opacity: 0.6
|
||||||
|
font.pixelSize: PlasmaCore.Theme.defaultFont.pixelSize * 0.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
import "../../components" as Components
|
||||||
|
|
||||||
|
QuickSettingsDelegate {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
iconItem: icon
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
radius: PlasmaCore.Units.smallSpacing
|
||||||
|
border.color: root.enabled ? root.enabledButtonBorderColor : root.disabledButtonBorderColor
|
||||||
|
color: {
|
||||||
|
if (root.enabled) {
|
||||||
|
return mouseArea.pressed ? root.enabledButtonPressedColor : root.enabledButtonColor
|
||||||
|
} else {
|
||||||
|
return mouseArea.pressed ? root.disabledButtonPressedColor : root.disabledButtonColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
onClicked: root.delegateClick()
|
||||||
|
onPressAndHold: root.delegatePressAndHold()
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
id: icon
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
|
implicitHeight: width
|
||||||
|
source: root.icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import "../../statusbar" as StatusBar
|
||||||
|
import "../../components" as Components
|
||||||
|
import "../../widgets" as Widgets
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick settings panel for landscape view (right sidebar).
|
||||||
|
* For the portrait view quicksettings container, see QuickSettingsDrawer.
|
||||||
|
*/
|
||||||
|
Components.BaseItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
|
required property real fullHeight
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of panel when first pulled down.
|
||||||
|
*/
|
||||||
|
readonly property real minimizedHeight: bottomPadding + topPadding + statusBar.height + quickSettings.rowHeight
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicit height of the contents of the panel.
|
||||||
|
*/
|
||||||
|
readonly property real contentImplicitHeight: column.implicitHeight
|
||||||
|
|
||||||
|
// we need extra padding since the background side border is enabled
|
||||||
|
topPadding: PlasmaCore.Units.smallSpacing * 4
|
||||||
|
leftPadding: PlasmaCore.Units.smallSpacing * 4
|
||||||
|
rightPadding: PlasmaCore.Units.smallSpacing * 4
|
||||||
|
bottomPadding: PlasmaCore.Units.smallSpacing * 4
|
||||||
|
|
||||||
|
background: PlasmaCore.FrameSvgItem {
|
||||||
|
enabledBorders: PlasmaCore.FrameSvg.AllBorders
|
||||||
|
imagePath: "widgets/background"
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: containerItem
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
// use container item so that our column doesn't get stretched if base item is anchored
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
height: root.fullHeight
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
StatusBar.StatusBar {
|
||||||
|
id: statusBar
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5
|
||||||
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 1.5
|
||||||
|
|
||||||
|
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
showSecondRow: false
|
||||||
|
showDropShadow: false
|
||||||
|
showTime: false
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: root.fullHeight - root.topPadding - root.bottomPadding - statusBar.height - mediaWidget.fullHeight - PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.maximumWidth: column.width
|
||||||
|
|
||||||
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
QuickSettings {
|
||||||
|
id: quickSettings
|
||||||
|
width: column.width
|
||||||
|
implicitHeight: quickSettings.fullHeight
|
||||||
|
|
||||||
|
actionDrawer: root.actionDrawer
|
||||||
|
minimizedViewProgress: 0
|
||||||
|
fullViewProgress: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillHeight: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
Widgets.MediaControlsWidget {
|
||||||
|
id: mediaWidget
|
||||||
|
property real fullHeight: visible ? height + PlasmaCore.Units.smallSpacing * 6 : 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: column.bottom
|
||||||
|
anchors.bottomMargin: root.bottomPadding * 2 + PlasmaCore.Units.smallSpacing * 2 // HACK: can't figure out a cleaner way to position
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle {
|
||||||
|
id: handle
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
* SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
@ -9,35 +9,29 @@
|
||||||
import QtQuick 2.14
|
import QtQuick 2.14
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
|
||||||
import org.kde.bluezqt 1.0 as BluezQt
|
import org.kde.bluezqt 1.0 as BluezQt
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|
||||||
import org.kde.plasma.private.mobilehomescreencomponents 0.1 as HomeScreenComponents
|
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PC3
|
import org.kde.plasma.components 3.0 as PC3
|
||||||
|
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
MobileShell.QuickSettingsModel {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var actionDrawer
|
||||||
|
|
||||||
HomeScreenComponents.QuickSettingsModel
|
|
||||||
{
|
|
||||||
id: modelItem
|
|
||||||
property bool screenshotRequested: false
|
property bool screenshotRequested: false
|
||||||
|
|
||||||
signal panelClosed()
|
MobileShell.QuickSetting {
|
||||||
|
|
||||||
onPanelClosed: {
|
|
||||||
if (screenshotRequested) {
|
|
||||||
plasmoid.nativeInterface.takeScreenshot();
|
|
||||||
screenshotRequested = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HomeScreenComponents.QuickSetting {
|
|
||||||
text: i18n("Settings")
|
text: i18n("Settings")
|
||||||
|
status: i18n("Tap to open")
|
||||||
icon: "configure"
|
icon: "configure"
|
||||||
enabled: false
|
enabled: false
|
||||||
settingsCommand: "plasma-open-settings"
|
settingsCommand: "plasma-open-settings"
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
PlasmaNM.Handler {
|
PlasmaNM.Handler {
|
||||||
id: nmHandler
|
id: nmHandler
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +48,7 @@ HomeScreenComponents.QuickSettingsModel
|
||||||
}
|
}
|
||||||
enabled: enabledConnections.wirelessEnabled
|
enabled: enabledConnections.wirelessEnabled
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Bluetooth")
|
text: i18n("Bluetooth")
|
||||||
icon: "network-bluetooth"
|
icon: "network-bluetooth"
|
||||||
settingsCommand: "plasma-open-settings kcm_bluetooth"
|
settingsCommand: "plasma-open-settings kcm_bluetooth"
|
||||||
|
|
@ -69,7 +63,7 @@ HomeScreenComponents.QuickSettingsModel
|
||||||
}
|
}
|
||||||
enabled: BluezQt.Manager.bluetoothOperational
|
enabled: BluezQt.Manager.bluetoothOperational
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Mobile Data")
|
text: i18n("Mobile Data")
|
||||||
icon: "network-modem"
|
icon: "network-modem"
|
||||||
settingsCommand: "plasma-open-settings kcm_mobile_broadband"
|
settingsCommand: "plasma-open-settings kcm_mobile_broadband"
|
||||||
|
|
@ -78,35 +72,70 @@ HomeScreenComponents.QuickSettingsModel
|
||||||
nmHandler.enableWwan(!enabledConnections.wwanEnabled)
|
nmHandler.enableWwan(!enabledConnections.wwanEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Flashlight")
|
text: i18n("Flashlight")
|
||||||
icon: "flashlight-on"
|
icon: "flashlight-on"
|
||||||
enabled: plasmoid.nativeInterface.torchEnabled
|
enabled: MobileShell.ShellUtil.torchEnabled
|
||||||
function toggle() {
|
function toggle() {
|
||||||
plasmoid.nativeInterface.toggleTorch()
|
MobileShell.ShellUtil.toggleTorch()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Location")
|
text: i18n("Location")
|
||||||
icon: "gps"
|
icon: "gps"
|
||||||
enabled: false
|
enabled: false
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Screenshot")
|
text: i18n("Screenshot")
|
||||||
|
status: i18n("Tap to screenshot")
|
||||||
icon: "spectacle"
|
icon: "spectacle"
|
||||||
enabled: false
|
enabled: false
|
||||||
function toggle() {
|
function toggle() {
|
||||||
modelItem.screenshotRequested = true;
|
root.screenshotRequested = true;
|
||||||
root.closeRequested();
|
root.actionDrawer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.actionDrawer
|
||||||
|
function onVisibleChanged(visible) {
|
||||||
|
if (!visible && screenshotRequested) {
|
||||||
|
timer.restart();
|
||||||
|
root.screenshotRequested = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: KWin's fade effect may have the window ending up being in the screenshot if taken too fast
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 500
|
||||||
|
onTriggered: MobileShell.ShellUtil.takeScreenshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Auto-rotate")
|
text: i18n("Auto-rotate")
|
||||||
icon: "rotation-allowed"
|
icon: "rotation-allowed"
|
||||||
settingsCommand: "plasma-open-settings kcm_kscreen"
|
settingsCommand: "plasma-open-settings kcm_kscreen"
|
||||||
enabled: plasmoid.nativeInterface.autoRotateEnabled
|
enabled: MobileShell.ShellUtil.autoRotateEnabled
|
||||||
function toggle() {
|
function toggle() {
|
||||||
plasmoid.nativeInterface.autoRotateEnabled = !enabled
|
MobileShell.ShellUtil.autoRotateEnabled = !enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MobileShell.QuickSetting {
|
||||||
|
text: i18n("Battery")
|
||||||
|
status: i18n("%1%", MobileShell.BatteryProvider.percent)
|
||||||
|
icon: "battery-full" + (MobileShell.BatteryProvider.pluggedIn ? "-charging" : "")
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: "plasma-open-settings kcm_mobile_power"
|
||||||
|
}
|
||||||
|
MobileShell.QuickSetting {
|
||||||
|
text: i18n("Sound")
|
||||||
|
icon: "audio-speakers-symbolic"
|
||||||
|
status: i18n("%1%", MobileShell.VolumeProvider.volumeValue)
|
||||||
|
enabled: false
|
||||||
|
settingsCommand: "plasma-open-settings kcm_pulseaudio"
|
||||||
|
function toggle() {
|
||||||
|
volumeProvider.showVolumeOverlay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
66
components/mobileshell/qml/components/BaseItem.qml
Normal file
66
components/mobileshell/qml/components/BaseItem.qml
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serves a similar function as a QQC2.Control, but does not
|
||||||
|
* take input events, preventing conflicts with Flickable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real topInset: 0
|
||||||
|
property real bottomInset: 0
|
||||||
|
property real leftInset: 0
|
||||||
|
property real rightInset: 0
|
||||||
|
|
||||||
|
property real padding: 0
|
||||||
|
property real verticalPadding: padding
|
||||||
|
property real horizontalPadding: padding
|
||||||
|
property real topPadding: verticalPadding
|
||||||
|
property real bottomPadding: verticalPadding
|
||||||
|
property real leftPadding: horizontalPadding
|
||||||
|
property real rightPadding: horizontalPadding
|
||||||
|
|
||||||
|
property Item contentItem: Item {}
|
||||||
|
property Item background: Item {}
|
||||||
|
|
||||||
|
implicitHeight: topPadding + bottomPadding + contentItem.implicitHeight
|
||||||
|
implicitWidth: leftPadding + rightPadding + contentItem.implicitWidth
|
||||||
|
|
||||||
|
onContentItemChanged: {
|
||||||
|
contentItem.parent = contentItemLoader;
|
||||||
|
contentItem.anchors.fill = contentItemLoader;
|
||||||
|
contentItemLoader.children.push(contentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBackgroundChanged: {
|
||||||
|
background.parent = backgroundLoader;
|
||||||
|
background.anchors.fill = backgroundLoader;
|
||||||
|
backgroundLoader.children.push(background);
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: backgroundLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: root.leftInset
|
||||||
|
anchors.rightMargin: root.rightInset
|
||||||
|
anchors.topMargin: root.topInset
|
||||||
|
anchors.bottomMargin: root.bottomInset
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentItemLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: root.leftPadding
|
||||||
|
anchors.rightMargin: root.rightPadding
|
||||||
|
anchors.topMargin: root.topPadding
|
||||||
|
anchors.bottomMargin: root.bottomPadding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
components/mobileshell/qml/components/Direction.qml
Normal file
18
components/mobileshell/qml/components/Direction.qml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
enum Type {
|
||||||
|
None = 0,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
9
components/mobileshell/qml/components/util.js
Normal file
9
components/mobileshell/qml/components/util.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
function applyMinMaxRange(min, max, num) {
|
||||||
|
return Math.min(max, Math.max(min, num));
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
@ -12,6 +12,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
import org.kde.plasma.workspace.components 2.0 as PW
|
import org.kde.plasma.workspace.components 2.0 as PW
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property bool isVisible: pmSource.data["Battery"]["Has Cumulative"]
|
property bool isVisible: pmSource.data["Battery"]["Has Cumulative"]
|
||||||
property int percent: pmSource.data["Battery"]["Percent"]
|
property int percent: pmSource.data["Battery"]["Percent"]
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
||||||
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
||||||
|
|
||||||
|
|
@ -11,6 +11,8 @@ import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.bluezqt 1.0 as BluezQt
|
import org.kde.bluezqt 1.0 as BluezQt
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
property bool isVisible: BluezQt.Manager.bluetoothOperational
|
property bool isVisible: BluezQt.Manager.bluetoothOperational
|
||||||
property string icon: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth"
|
property string icon: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth"
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
|
||||||
|
property var notificationSettings: NotificationManager.Settings {}
|
||||||
|
|
||||||
|
property var historyModel: NotificationManager.Notifications {
|
||||||
|
showExpired: true
|
||||||
|
showDismissed: true
|
||||||
|
showJobs: notificationSettings.jobsInNotifications
|
||||||
|
sortMode: NotificationManager.Notifications.SortByTypeAndUrgency
|
||||||
|
groupMode: NotificationManager.Notifications.GroupApplicationsFlat
|
||||||
|
groupLimit: 2
|
||||||
|
expandUnread: true
|
||||||
|
blacklistedDesktopEntries: notificationSettings.historyBlacklistedApplications
|
||||||
|
blacklistedNotifyRcNames: notificationSettings.historyBlacklistedServices
|
||||||
|
urgencies: {
|
||||||
|
var urgencies = NotificationManager.Notifications.CriticalUrgency
|
||||||
|
| NotificationManager.Notifications.NormalUrgency;
|
||||||
|
if (notificationSettings.lowPriorityHistory) {
|
||||||
|
urgencies |= NotificationManager.Notifications.LowUrgency;
|
||||||
|
}
|
||||||
|
return urgencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
* SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||||
*
|
*
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
import QtQuick 2.1
|
import QtQuick 2.1
|
||||||
import org.kde.plasma.mm 1.0
|
import org.kde.plasma.mm 1.0
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
|
||||||
property string icon: "network-mobile-" + Math.floor(SignalIndicator.strength / 20) * 20
|
property string icon: "network-mobile-" + Math.floor(SignalIndicator.strength / 20) * 20
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
SPDX-FileCopyrightText: 2019 Aditya Mehra <Aix.m@outlook.com>
|
SPDX-FileCopyrightText: 2019 Aditya Mehra <Aix.m@outlook.com>
|
||||||
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
|
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
|
||||||
|
|
||||||
|
|
@ -11,10 +11,21 @@ import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.private.volume 0.1
|
import org.kde.plasma.private.volume 0.1
|
||||||
|
|
||||||
import "../../volumeosd"
|
import "../osd/volume"
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
|
/**
|
||||||
|
* Whether or not to bind the volume global key shortcuts.
|
||||||
|
* We should never bind the shortcut multiple times in the shell, or else they may not work at all.
|
||||||
|
*
|
||||||
|
* We only set this to true when loaded for the panel containment (and NOT in the lockscreen).
|
||||||
|
*/
|
||||||
|
property bool bindShortcuts: false
|
||||||
|
|
||||||
property bool isVisible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted
|
property bool isVisible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted
|
||||||
|
|
||||||
property string icon: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)
|
property string icon: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)
|
||||||
? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted)
|
? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted)
|
||||||
: iconName(0, true)
|
: iconName(0, true)
|
||||||
|
|
@ -22,6 +33,8 @@ QtObject {
|
||||||
property bool volumeFeedback: true
|
property bool volumeFeedback: true
|
||||||
property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0)
|
property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0)
|
||||||
property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
|
property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
|
||||||
|
property int volumeValue
|
||||||
|
|
||||||
readonly property string dummyOutputName: "auto_null"
|
readonly property string dummyOutputName: "auto_null"
|
||||||
|
|
||||||
function showVolumeOverlay() {
|
function showVolumeOverlay() {
|
||||||
|
|
@ -80,7 +93,7 @@ QtObject {
|
||||||
var percent = volumePercent(volume, maxVolumeValue);
|
var percent = volumePercent(volume, maxVolumeValue);
|
||||||
paSinkModel.preferredSink.muted = percent == 0;
|
paSinkModel.preferredSink.muted = percent == 0;
|
||||||
paSinkModel.preferredSink.volume = volume;
|
paSinkModel.preferredSink.volume = volume;
|
||||||
osd.volume = percent;
|
volumeValue = percent;
|
||||||
osd.showOverlay();
|
osd.showOverlay();
|
||||||
playFeedback();
|
playFeedback();
|
||||||
|
|
||||||
|
|
@ -95,7 +108,7 @@ QtObject {
|
||||||
var percent = volumePercent(volume, maxVolumeValue);
|
var percent = volumePercent(volume, maxVolumeValue);
|
||||||
paSinkModel.preferredSink.muted = percent == 0;
|
paSinkModel.preferredSink.muted = percent == 0;
|
||||||
paSinkModel.preferredSink.volume = volume;
|
paSinkModel.preferredSink.volume = volume;
|
||||||
osd.volume = percent;
|
volumeValue = percent;
|
||||||
osd.showOverlay();
|
osd.showOverlay();
|
||||||
playFeedback();
|
playFeedback();
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +123,7 @@ QtObject {
|
||||||
var toMute = !paSinkModel.preferredSink.muted;
|
var toMute = !paSinkModel.preferredSink.muted;
|
||||||
paSinkModel.preferredSink.muted = toMute;
|
paSinkModel.preferredSink.muted = toMute;
|
||||||
|
|
||||||
osd.volume = toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
volumeValue = toMute ? 0 : volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||||
osd.showOverlay();
|
osd.showOverlay();
|
||||||
|
|
||||||
if (!toMute) {
|
if (!toMute) {
|
||||||
|
|
@ -123,7 +136,7 @@ QtObject {
|
||||||
|
|
||||||
function onVolumeChanged() {
|
function onVolumeChanged() {
|
||||||
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||||
osd.volume = percent;
|
volumeValue = percent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property var updateVolumeOnSinkChange: Connections {
|
property var updateVolumeOnSinkChange: Connections {
|
||||||
|
|
@ -132,49 +145,56 @@ QtObject {
|
||||||
function onPreferredSinkChanged() {
|
function onPreferredSinkChanged() {
|
||||||
if (paSinkModel.preferredSink) {
|
if (paSinkModel.preferredSink) {
|
||||||
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
var percent = volumePercent(paSinkModel.preferredSink.volume, maxVolumeValue);
|
||||||
osd.volume = percent;
|
volumeValue = percent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
property SinkModel paSinkModel: SinkModel {}
|
property SinkModel paSinkModel: SinkModel {}
|
||||||
|
|
||||||
property VolumeOsd osd: VolumeOsd {}
|
property VolumeOSD osd: VolumeOSD {
|
||||||
|
volume: volumeValue
|
||||||
|
}
|
||||||
|
|
||||||
property VolumeFeedback feedback: VolumeFeedback {}
|
property VolumeFeedback feedback: VolumeFeedback {}
|
||||||
|
|
||||||
property GlobalActionCollection actionCollection: GlobalActionCollection {
|
// only bind the global shortcuts when told to
|
||||||
// KGlobalAccel cannot transition from kmix to something else, so if
|
property var actionCollection: Loader {
|
||||||
// the user had a custom shortcut set for kmix those would get lost.
|
active: bindShortcuts
|
||||||
// To avoid this we hijack kmix name and actions. Entirely mental but
|
|
||||||
// best we can do to not cause annoyance for the user.
|
|
||||||
// The display name actually is updated to whatever registered last
|
|
||||||
// though, so as far as user visible strings go we should be fine.
|
|
||||||
// As of 2015-07-21:
|
|
||||||
// componentName: kmix
|
|
||||||
// actions: increase_volume, decrease_volume, mute
|
|
||||||
name: "kmix"
|
|
||||||
displayName: i18n("Audio")
|
|
||||||
|
|
||||||
GlobalAction {
|
sourceComponent: GlobalActionCollection {
|
||||||
objectName: "increase_volume"
|
// KGlobalAccel cannot transition from kmix to something else, so if
|
||||||
text: i18n("Increase Volume")
|
// the user had a custom shortcut set for kmix those would get lost.
|
||||||
shortcut: Qt.Key_VolumeUp
|
// To avoid this we hijack kmix name and actions. Entirely mental but
|
||||||
onTriggered: increaseVolume()
|
// best we can do to not cause annoyance for the user.
|
||||||
}
|
// The display name actually is updated to whatever registered last
|
||||||
|
// though, so as far as user visible strings go we should be fine.
|
||||||
|
// As of 2015-07-21:
|
||||||
|
// componentName: kmix
|
||||||
|
// actions: increase_volume, decrease_volume, mute
|
||||||
|
name: "kmix"
|
||||||
|
displayName: i18n("Audio")
|
||||||
|
|
||||||
GlobalAction {
|
GlobalAction {
|
||||||
objectName: "decrease_volume"
|
objectName: "increase_volume"
|
||||||
text: i18n("Decrease Volume")
|
text: i18n("Increase Volume")
|
||||||
shortcut: Qt.Key_VolumeDown
|
shortcut: Qt.Key_VolumeUp
|
||||||
onTriggered: decreaseVolume()
|
onTriggered: increaseVolume()
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalAction {
|
GlobalAction {
|
||||||
objectName: "mute"
|
objectName: "decrease_volume"
|
||||||
text: i18n("Mute")
|
text: i18n("Decrease Volume")
|
||||||
shortcut: Qt.Key_VolumeMute
|
shortcut: Qt.Key_VolumeDown
|
||||||
onTriggered: muteVolume()
|
onTriggered: decreaseVolume()
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalAction {
|
||||||
|
objectName: "mute"
|
||||||
|
text: i18n("Mute")
|
||||||
|
shortcut: Qt.Key_VolumeMute
|
||||||
|
onTriggered: muteVolume()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
||||||
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
||||||
|
|
||||||
|
|
@ -11,24 +11,25 @@ import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
||||||
|
|
||||||
Item {
|
pragma Singleton
|
||||||
visible: false
|
|
||||||
|
QtObject {
|
||||||
property string icon: connectionIconProvider.connectionIcon
|
property string icon: connectionIconProvider.connectionIcon
|
||||||
property bool indicatorRunning: connectionIconProvider.connecting
|
property bool indicatorRunning: connectionIconProvider.connecting
|
||||||
|
|
||||||
PlasmaNM.NetworkStatus {
|
property var networkStatus: PlasmaNM.NetworkStatus {
|
||||||
id: networkStatus
|
id: networkStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaNM.NetworkModel {
|
property var networkModel: PlasmaNM.NetworkModel {
|
||||||
id: connectionModel
|
id: connectionModel
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaNM.Handler {
|
property var handler: PlasmaNM.Handler {
|
||||||
id: handler
|
id: handler
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaNM.ConnectionIcon {
|
property var connectionIcon: PlasmaNM.ConnectionIcon {
|
||||||
id: connectionIconProvider
|
id: connectionIconProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,6 @@ import QtGraphicalEffects 1.12
|
||||||
import org.kde.plasma.core 2.1 as PlasmaCore
|
import org.kde.plasma.core 2.1 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
import org.kde.plasma.extras 2.0 as PlasmaExtra
|
import org.kde.plasma.extras 2.0 as PlasmaExtra
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
|
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
|
||||||
|
|
||||||
import org.kde.plasma.private.volume 0.1
|
import org.kde.plasma.private.volume 0.1
|
||||||
34
components/mobileshell/qml/qmldir
Normal file
34
components/mobileshell/qml/qmldir
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
module org.kde.plasma.private.mobileshell
|
||||||
|
|
||||||
|
plugin mobileshellplugin
|
||||||
|
|
||||||
|
# /actiondrawer
|
||||||
|
ActionDrawer 1.0 actiondrawer/ActionDrawer.qml
|
||||||
|
ActionDrawerOpenSurface 1.0 actiondrawer/ActionDrawerOpenSurface.qml
|
||||||
|
|
||||||
|
# /components
|
||||||
|
BaseItem 1.0 components/BaseItem.qml
|
||||||
|
Direction 1.0 components/Direction.qml
|
||||||
|
PanelBackground 1.0 components/PanelBackground.qml
|
||||||
|
|
||||||
|
# /dataproviders
|
||||||
|
singleton BatteryProvider 1.0 dataproviders/BatteryProvider.qml
|
||||||
|
singleton BluetoothProvider 1.0 dataproviders/BluetoothProvider.qml
|
||||||
|
singleton NotificationProvider 1.0 dataproviders/NotificationProvider.qml
|
||||||
|
singleton SignalStrengthProvider 1.0 dataproviders/SignalStrengthProvider.qml
|
||||||
|
singleton VolumeProvider 1.0 dataproviders/VolumeProvider.qml
|
||||||
|
singleton WifiProvider 1.0 dataproviders/WifiProvider.qml
|
||||||
|
|
||||||
|
# /statusbar
|
||||||
|
StatusBar 1.0 statusbar/StatusBar.qml
|
||||||
|
|
||||||
|
# /widgets
|
||||||
|
MediaControlsWidget 1.0 widgets/MediaControlsWidget.qml
|
||||||
|
NotificationsWidget 1.0 widgets/NotificationsWidget.qml
|
||||||
|
|
||||||
|
# /
|
||||||
|
singleton HomeScreenControls 1.0 HomeScreenControls.qml
|
||||||
|
singleton TopPanelControls 1.0 TopPanelControls.qml
|
||||||
33
components/mobileshell/qml/statusbar/ClockText.qml
Normal file
33
components/mobileshell/qml/statusbar/ClockText.qml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import "indicators" as Indicators
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: clock
|
||||||
|
|
||||||
|
required property PlasmaCore.DataSource source
|
||||||
|
|
||||||
|
property bool is24HourTime: MobileShell.ShellUtil.isSystem24HourFormat
|
||||||
|
|
||||||
|
text: Qt.formatTime(source.data.Local.DateTime, is24HourTime ? "h:mm" : "h:mm ap")
|
||||||
|
color: PlasmaCore.ColorScope.textColor
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onTapped: {
|
||||||
|
MobileShell.ShellUtil.launchApp("org.kde.kclock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,39 +12,75 @@ import QtQuick.Layouts 1.3
|
||||||
import QtQml.Models 2.12
|
import QtQml.Models 2.12
|
||||||
import QtGraphicalEffects 1.12
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
import org.kde.plasma.workspace.components 2.0 as PlasmaWorkspace
|
|
||||||
import org.kde.taskmanager 0.1 as TaskManager
|
|
||||||
|
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|
||||||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
import "LayoutManager.js" as LayoutManager
|
|
||||||
|
|
||||||
import "indicators" as Indicators
|
import "indicators" as Indicators
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: indicatorsRow
|
id: root
|
||||||
required property var colorGroup
|
|
||||||
required property bool showDropShadow
|
|
||||||
required property color backgroundColor
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color group used for status bar elements.
|
||||||
|
*/
|
||||||
|
required property var colorGroup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show a drop shadow under the status bar.
|
||||||
|
*/
|
||||||
|
property bool showDropShadow: false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The background color of the status bar.
|
||||||
|
*/
|
||||||
|
property color backgroundColor: "transparent"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show a second row of the status bar, with more information.
|
||||||
|
*/
|
||||||
property bool showSecondRow: false // show extra row with date and mobile provider
|
property bool showSecondRow: false // show extra row with date and mobile provider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show time. If set to false, the signal strength indicator is moved in its place.
|
||||||
|
*/
|
||||||
|
property bool showTime: true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables showing system tray indicators, preventing SIGABRT when used on the lockscreen.
|
||||||
|
*/
|
||||||
|
property bool disableSystemTray: false
|
||||||
|
|
||||||
property alias colorScopeColor: icons.backgroundColor
|
property alias colorScopeColor: icons.backgroundColor
|
||||||
property alias applets: appletIconsRow
|
property alias applets: appletIconsRow
|
||||||
|
|
||||||
property real textPixelSize: PlasmaCore.Units.gridUnit * 0.6
|
readonly property real textPixelSize: PlasmaCore.Units.gridUnit * 0.6
|
||||||
property real elementSpacing: PlasmaCore.Units.smallSpacing * 1.5
|
readonly property real elementSpacing: PlasmaCore.Units.smallSpacing * 1.5
|
||||||
|
|
||||||
PlasmaCore.DataSource {
|
PlasmaCore.DataSource {
|
||||||
id: timeSource
|
id: timeSource
|
||||||
engine: "time"
|
engine: "time"
|
||||||
connectedSources: ["Local"]
|
connectedSources: ["Local"]
|
||||||
interval: 60 * 1000
|
interval: 60 * 1000
|
||||||
|
intervalAlignment: PlasmaCore.Types.AlignToMinute
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias statusNotifierSource: statusNotifierSourceLoader.item
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: statusNotifierSourceLoader
|
||||||
|
active: !disableSystemTray
|
||||||
|
sourceComponent: PlasmaCore.DataSource {
|
||||||
|
id: statusNotifierSource
|
||||||
|
engine: "statusnotifieritem"
|
||||||
|
interval: 0
|
||||||
|
onSourceAdded: {
|
||||||
|
connectSource(source)
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
connectedSources = sources
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
DropShadow {
|
||||||
|
|
@ -63,7 +99,7 @@ Item {
|
||||||
PlasmaCore.ColorScope {
|
PlasmaCore.ColorScope {
|
||||||
id: icons
|
id: icons
|
||||||
z: 1
|
z: 1
|
||||||
colorGroup: indicatorsRow.colorGroup
|
colorGroup: root.colorGroup
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Controls.Control {
|
Controls.Control {
|
||||||
|
|
@ -88,21 +124,18 @@ Item {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// clock
|
// clock
|
||||||
PlasmaComponents.Label {
|
ClockText {
|
||||||
id: clock
|
visible: root.showTime
|
||||||
property bool is24HourTime: plasmoid.nativeInterface.isSystem24HourFormat
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
text: Qt.formatTime(timeSource.data.Local.DateTime, is24HourTime ? "h:mm" : "h:mm ap")
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
|
||||||
verticalAlignment: Qt.AlignVCenter
|
|
||||||
font.pixelSize: textPixelSize
|
font.pixelSize: textPixelSize
|
||||||
|
source: timeSource
|
||||||
|
}
|
||||||
|
|
||||||
TapHandler {
|
Indicators.SignalStrengthIndicator {
|
||||||
onTapped: {
|
Layout.fillHeight: true
|
||||||
plasmoid.nativeInterface.launchApp("org.kde.kclock");
|
showLabel: true
|
||||||
}
|
visible: !root.showTime
|
||||||
}
|
textPixelSize: root.textPixelSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// spacing in the middle
|
// spacing in the middle
|
||||||
|
|
@ -117,21 +150,21 @@ Item {
|
||||||
id: filteredStatusNotifiers
|
id: filteredStatusNotifiers
|
||||||
filterRole: "Title"
|
filterRole: "Title"
|
||||||
sourceModel: PlasmaCore.DataModel {
|
sourceModel: PlasmaCore.DataModel {
|
||||||
dataSource: statusNotifierSource
|
dataSource: statusNotifierSource ? statusNotifierSource : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: TaskWidget {
|
delegate: TaskWidget {
|
||||||
Layout.leftMargin: indicatorsRow.elementSpacing
|
Layout.leftMargin: root.elementSpacing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// applet indicators
|
// applet indicators
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: appletIconsRow
|
id: appletIconsRow
|
||||||
Layout.leftMargin: indicatorsRow.elementSpacing
|
Layout.leftMargin: root.elementSpacing
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: indicatorsRow.elementSpacing
|
spacing: root.elementSpacing
|
||||||
visible: children.length > 0
|
visible: children.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,28 +173,25 @@ Item {
|
||||||
id: indicators
|
id: indicators
|
||||||
Layout.leftMargin: PlasmaCore.Units.smallSpacing // applets have different spacing needs
|
Layout.leftMargin: PlasmaCore.Units.smallSpacing // applets have different spacing needs
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
spacing: indicatorsRow.elementSpacing
|
spacing: root.elementSpacing
|
||||||
|
|
||||||
Indicators.SignalStrength {
|
Indicators.SignalStrengthIndicator {
|
||||||
provider: signalStrengthProvider
|
Layout.fillHeight: true
|
||||||
|
showLabel: false
|
||||||
|
visible: root.showTime
|
||||||
|
}
|
||||||
|
Indicators.BluetoothIndicator {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
Indicators.Bluetooth {
|
Indicators.WifiIndicator {
|
||||||
provider: bluetoothProvider
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
Indicators.Wifi {
|
Indicators.VolumeIndicator {
|
||||||
provider: wifiProvider
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
Indicators.Volume {
|
Indicators.BatteryIndicator {
|
||||||
provider: volumeProvider
|
spacing: root.elementSpacing
|
||||||
Layout.fillHeight: true
|
textPixelSize: root.textPixelSize
|
||||||
}
|
|
||||||
Indicators.Battery {
|
|
||||||
provider: batteryProvider
|
|
||||||
spacing: indicatorsRow.elementSpacing
|
|
||||||
labelHeight: textPixelSize
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,19 +200,20 @@ Item {
|
||||||
// extra row with date and mobile provider (for quicksettings panel)
|
// extra row with date and mobile provider (for quicksettings panel)
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
visible: indicatorsRow.showSecondRow
|
visible: root.showSecondRow
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
text: Qt.formatDate(timeSource.data.Local.DateTime, "ddd. MMMM d")
|
text: Qt.formatDate(timeSource.data.Local.DateTime, "ddd. MMMM d")
|
||||||
color: PlasmaCore.ColorScope.disabledTextColor
|
color: PlasmaCore.ColorScope.disabledTextColor
|
||||||
font.pixelSize: indicatorsRow.textPixelSize * 0.8
|
font.pixelSize: root.textPixelSize * 0.8
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
text: signalStrengthProvider.label
|
visible: root.showTime
|
||||||
|
text: MobileShell.SignalStrengthProvider.label
|
||||||
color: PlasmaCore.ColorScope.disabledTextColor
|
color: PlasmaCore.ColorScope.disabledTextColor
|
||||||
font.pixelSize: indicatorsRow.textPixelSize * 0.8
|
font.pixelSize: root.textPixelSize * 0.8
|
||||||
horizontalAlignment: Qt.AlignRight
|
horizontalAlignment: Qt.AlignRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,12 +11,11 @@ import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
import org.kde.plasma.workspace.components 2.0 as PW
|
import org.kde.plasma.workspace.components 2.0 as PW
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
import "providers"
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
required property BatteryProvider provider
|
property MobileShell.BatteryProvider provider: MobileShell.BatteryProvider
|
||||||
property real labelHeight
|
property real textPixelSize: PlasmaCore.Units.gridUnit * 0.6
|
||||||
visible: provider.isVisible
|
visible: provider.isVisible
|
||||||
|
|
||||||
PW.BatteryIcon {
|
PW.BatteryIcon {
|
||||||
|
|
@ -37,6 +36,6 @@ RowLayout {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
color: PlasmaCore.ColorScope.textColor
|
||||||
font.pixelSize: labelHeight
|
font.pixelSize: textPixelSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,16 +8,17 @@
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.2
|
||||||
import QtQuick.Layouts 1.4
|
import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.bluezqt 1.0 as BluezQt
|
|
||||||
|
|
||||||
import "providers"
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import org.kde.bluezqt 1.0 as BluezQt
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
id: connectionIcon
|
id: connectionIcon
|
||||||
required property BluetoothProvider provider
|
property MobileShell.BluetoothProvider provider: MobileShell.BluetoothProvider
|
||||||
|
|
||||||
source: provider.icon;
|
source: provider.icon
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||||
|
|
||||||
visible: provider.isVisible
|
visible: provider.isVisible
|
||||||
|
|
@ -10,14 +10,16 @@ import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
required property QtObject provider
|
property MobileShell.SignalStrengthProvider provider: MobileShell.SignalStrengthProvider
|
||||||
|
|
||||||
property real labelPixelSize
|
property bool showLabel: true
|
||||||
|
property real textPixelSize: PlasmaCore.Units.gridUnit * 0.6
|
||||||
|
|
||||||
width: strengthIcon.height + label.width
|
width: strengthIcon.width + label.width
|
||||||
Layout.minimumWidth: strengthIcon.height + label.width
|
Layout.minimumWidth: strengthIcon.width + label.width
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
id: strengthIcon
|
id: strengthIcon
|
||||||
|
|
@ -32,12 +34,14 @@ Item {
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
id: label
|
id: label
|
||||||
|
visible: showLabel
|
||||||
|
width: visible ? implicitWidth : 0
|
||||||
anchors.leftMargin: PlasmaCore.Units.smallSpacing
|
anchors.leftMargin: PlasmaCore.Units.smallSpacing
|
||||||
anchors.left: strengthIcon.right
|
anchors.left: strengthIcon.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
text: provider.label
|
text: provider.label
|
||||||
color: PlasmaCore.ColorScope.textColor
|
color: PlasmaCore.ColorScope.textColor
|
||||||
font.pixelSize: labelPixelSize
|
font.pixelSize: textPixelSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,12 +10,11 @@ import QtQuick 2.2
|
||||||
import QtQuick.Layouts 1.4
|
import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.private.volume 0.1
|
import org.kde.plasma.private.volume 0.1
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
import "providers"
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
id: paIcon
|
id: paIcon
|
||||||
required property VolumeProvider provider
|
property MobileShell.VolumeProvider provider: MobileShell.VolumeProvider
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: height
|
||||||
|
|
@ -11,12 +11,11 @@ import QtQuick.Layouts 1.4
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
import "providers"
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
id: connectionIcon
|
id: connectionIcon
|
||||||
required property WifiProvider provider
|
property MobileShell.WifiProvider provider: MobileShell.WifiProvider
|
||||||
|
|
||||||
source: provider.icon
|
source: provider.icon
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
colorGroup: PlasmaCore.ColorScope.colorGroup
|
||||||
127
components/mobileshell/qml/widgets/MediaControlsWidget.qml
Normal file
127
components/mobileshell/qml/widgets/MediaControlsWidget.qml
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import "../components" as Components
|
||||||
|
import "mediacontrols"
|
||||||
|
|
||||||
|
Components.BaseItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
visible: mpris2Source.hasPlayer
|
||||||
|
padding: visible ? Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0
|
||||||
|
implicitHeight: visible ? bottomPadding + topPadding + PlasmaCore.Units.gridUnit * 2 + PlasmaCore.Units.smallSpacing : 0
|
||||||
|
|
||||||
|
background: BlurredBackground {
|
||||||
|
imageSource: mpris2Source.albumArt
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: PlasmaCore.ColorScope {
|
||||||
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
|
width: root.width - root.leftPadding - root.rightPadding
|
||||||
|
|
||||||
|
MediaControlsSource {
|
||||||
|
id: mpris2Source
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: controlsRow
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
enabled: mpris2Source.canControl
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: albumArt
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
Layout.fillHeight: true
|
||||||
|
asynchronous: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: mpris2Source.albumArt
|
||||||
|
sourceSize.height: height
|
||||||
|
visible: status === Image.Loading || status === Image.Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.leftMargin: albumArt.visible ? Kirigami.Units.largeSpacing : 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: mpris2Source.track || i18n("No media playing")
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize
|
||||||
|
maximumLineCount: 1
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
// if no artist is given, show player name instead
|
||||||
|
text: mpris2Source.artist || mpris2Source.identity || ""
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
font.pointSize: PlasmaCore.Theme.smallestFont.pointSize
|
||||||
|
maximumLineCount: 1
|
||||||
|
opacity: 0.9
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
|
||||||
|
enabled: mpris2Source.canGoBack
|
||||||
|
icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
|
||||||
|
icon.width: PlasmaCore.Units.iconSizes.small
|
||||||
|
icon.height: PlasmaCore.Units.iconSizes.small
|
||||||
|
onClicked: mpris2Source.goPrevious()
|
||||||
|
visible: mpris2Source.canGoBack || mpris2Source.canGoNext
|
||||||
|
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Previous track")
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
|
||||||
|
icon.name: mpris2Source.playing ? "media-playback-pause" : "media-playback-start"
|
||||||
|
icon.width: PlasmaCore.Units.iconSizes.small
|
||||||
|
icon.height: PlasmaCore.Units.iconSizes.small
|
||||||
|
onClicked: mpris2Source.playPause()
|
||||||
|
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Play or Pause media")
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: height
|
||||||
|
|
||||||
|
enabled: mpris2Source.canGoNext
|
||||||
|
icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
|
||||||
|
icon.width: PlasmaCore.Units.iconSizes.small
|
||||||
|
icon.height: PlasmaCore.Units.iconSizes.small
|
||||||
|
onClicked: mpris2Source.goNext()
|
||||||
|
visible: mpris2Source.canGoBack || mpris2Source.canGoNext
|
||||||
|
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Next track")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
178
components/mobileshell/qml/widgets/NotificationsWidget.qml
Normal file
178
components/mobileshell/qml/widgets/NotificationsWidget.qml
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
import "notifications"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Embeddable notification list widget optimized for mobile and touch.
|
||||||
|
* Used on the lockscreen and action drawer.
|
||||||
|
*/
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var historyModel: MobileShell.NotificationProvider.historyModel
|
||||||
|
property var notificationSettings: MobileShell.NotificationProvider.notificationSettings
|
||||||
|
|
||||||
|
function clearHistory() {
|
||||||
|
historyModel.clear(NotificationManager.Notifications.ClearExpired);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNotificationSettings() {
|
||||||
|
MobileShell.ShellUtil.executeCommand("plasma-open-settings kcm_notifications");
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaCore.DataSource {
|
||||||
|
id: timeDataSource
|
||||||
|
engine: "time"
|
||||||
|
connectedSources: ["Local"]
|
||||||
|
interval: 60000 // 1 min
|
||||||
|
intervalAlignment: PlasmaCore.Types.AlignToMinute
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: list
|
||||||
|
model: historyModel
|
||||||
|
currentIndex: -1
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// TODO keyboard focus
|
||||||
|
highlightMoveDuration: 0
|
||||||
|
highlightResizeDuration: 0
|
||||||
|
highlight: Item {}
|
||||||
|
|
||||||
|
section {
|
||||||
|
property: "isInGroup"
|
||||||
|
criteria: ViewSection.FullString
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.PlaceholderMessage {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - (PlasmaCore.Units.largeSpacing * 4)
|
||||||
|
|
||||||
|
text: i18n("Notification service not available")
|
||||||
|
visible: list.count === 0 && !NotificationManager.Server.valid
|
||||||
|
|
||||||
|
PlasmaComponents3.Label {
|
||||||
|
// Checking valid to avoid creating ServerInfo object if everything is alright
|
||||||
|
readonly property NotificationManager.ServerInfo currentOwner: !NotificationManager.Server.valid ? NotificationManager.Server.currentOwner : null
|
||||||
|
// PlasmaExtras.PlaceholderMessage is internally a ColumnLayout, so we can use Layout.whatever properties here
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: currentOwner ? i18nc("Vendor and product name", "Notifications are currently provided by '%1 %2'", currentOwner.vendor, currentOwner.name) : ""
|
||||||
|
visible: currentOwner && currentOwner.vendor && currentOwner.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add: Transition {
|
||||||
|
SequentialAnimation {
|
||||||
|
PropertyAction { property: "opacity"; value: 0 }
|
||||||
|
PauseAnimation { duration: PlasmaCore.Units.longDuration }
|
||||||
|
ParallelAnimation {
|
||||||
|
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: PlasmaCore.Units.longDuration }
|
||||||
|
NumberAnimation { property: "height"; from: 0; duration: PlasmaCore.Units.longDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addDisplaced: Transition {
|
||||||
|
NumberAnimation { properties: "y"; duration: PlasmaCore.Units.longDuration }
|
||||||
|
}
|
||||||
|
removeDisplaced: Transition {
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation { duration: PlasmaCore.Units.longDuration }
|
||||||
|
NumberAnimation { properties: "y"; duration: PlasmaCore.Units.longDuration }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRowExpanded(row) {
|
||||||
|
var idx = historyModel.index(row, 0);
|
||||||
|
return historyModel.data(idx, NotificationManager.Notifications.IsGroupExpandedRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGroupExpanded(row, expanded) {
|
||||||
|
var rowIdx = historyModel.index(row, 0);
|
||||||
|
var persistentRowIdx = historyModel.makePersistentModelIndex(rowIdx);
|
||||||
|
var persistentGroupIdx = historyModel.makePersistentModelIndex(historyModel.groupIndex(rowIdx));
|
||||||
|
|
||||||
|
historyModel.setData(rowIdx, expanded, NotificationManager.Notifications.IsGroupExpandedRole);
|
||||||
|
|
||||||
|
// If the current item went away when the group collapsed, scroll to the group heading
|
||||||
|
if (!persistentRowIdx || !persistentRowIdx.valid) {
|
||||||
|
if (persistentGroupIdx && persistentGroupIdx.valid) {
|
||||||
|
list.positionViewAtIndex(persistentGroupIdx.row, ListView.Contain);
|
||||||
|
// When closed via keyboard, also set a sane current index
|
||||||
|
if (list.currentIndex > -1) {
|
||||||
|
list.currentIndex = persistentGroupIdx.row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Loader {
|
||||||
|
id: delegateLoader
|
||||||
|
width: list.width
|
||||||
|
sourceComponent: model.isGroup ? groupDelegate : notificationDelegate
|
||||||
|
|
||||||
|
required property var model
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: groupDelegate
|
||||||
|
NotificationGroupHeader {
|
||||||
|
applicationName: model.applicationName
|
||||||
|
applicationIconSource: model.applicationIconName
|
||||||
|
originName: model.originName || ""
|
||||||
|
timeSource: timeDataSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: notificationDelegate
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
NotificationItem {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
model: delegateLoader.model
|
||||||
|
modelIndex: delegateLoader.index
|
||||||
|
notificationsModel: historyModel
|
||||||
|
timeSource: timeDataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
icon.name: model.isGroupExpanded ? "arrow-up" : "arrow-down"
|
||||||
|
text: model.isGroupExpanded ? i18n("Show Fewer")
|
||||||
|
: i18nc("Expand to show n more notifications",
|
||||||
|
"Show %1 More", (model.groupChildrenCount - model.expandedGroupChildrenCount))
|
||||||
|
visible: (model.groupChildrenCount > model.expandedGroupChildrenCount || model.isGroupExpanded)
|
||||||
|
&& delegateLoader.ListView.nextSection !== delegateLoader.ListView.section
|
||||||
|
onClicked: list.setGroupExpanded(model.index, !model.isGroupExpanded)
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property string imageSource
|
||||||
|
|
||||||
|
// clip corners so that the image has rounded corners
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Item {
|
||||||
|
width: img.width
|
||||||
|
height: img.height
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: img.width
|
||||||
|
height: img.height
|
||||||
|
radius: PlasmaCore.Units.smallSpacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img
|
||||||
|
source: root.imageSource
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
// ensure text is readable
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply lighten, saturate and blur effect
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: HueSaturation {
|
||||||
|
cached: true
|
||||||
|
|
||||||
|
lightness: 0.2
|
||||||
|
saturation: 1.5
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: FastBlur {
|
||||||
|
cached: true
|
||||||
|
radius: 64
|
||||||
|
transparentBorder: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
|
PlasmaCore.DataSource {
|
||||||
|
id: mpris2Source
|
||||||
|
|
||||||
|
readonly property string source: "@multiplex"
|
||||||
|
readonly property var playerData: data[source]
|
||||||
|
|
||||||
|
readonly property bool hasPlayer: sources.length > 1 && !!playerData
|
||||||
|
readonly property string identity: hasPlayer ? playerData.Identity : ""
|
||||||
|
readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing"
|
||||||
|
readonly property bool canControl: hasPlayer && playerData.CanControl
|
||||||
|
readonly property bool canGoBack: hasPlayer && playerData.CanGoPrevious
|
||||||
|
readonly property bool canGoNext: hasPlayer && playerData.CanGoNext
|
||||||
|
|
||||||
|
readonly property var currentMetadata: hasPlayer ? playerData.Metadata : ({})
|
||||||
|
|
||||||
|
readonly property string track: {
|
||||||
|
const xesamTitle = currentMetadata["xesam:title"]
|
||||||
|
if (xesamTitle) {
|
||||||
|
return xesamTitle
|
||||||
|
}
|
||||||
|
// if no track title is given, print out the file name
|
||||||
|
const xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : ""
|
||||||
|
if (!xesamUrl) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
const lastSlashPos = xesamUrl.lastIndexOf('/')
|
||||||
|
if (lastSlashPos < 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
const lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
|
||||||
|
return decodeURIComponent(lastUrlPart)
|
||||||
|
}
|
||||||
|
readonly property string artist: currentMetadata["xesam:artist"] || ""
|
||||||
|
readonly property string albumArt: currentMetadata["mpris:artUrl"] || ""
|
||||||
|
|
||||||
|
engine: "mpris2"
|
||||||
|
connectedSources: [source]
|
||||||
|
|
||||||
|
function startOperation(op) {
|
||||||
|
var service = serviceForSource(source)
|
||||||
|
var operation = service.operationDescription(op)
|
||||||
|
return service.startOperationCall(operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
function goPrevious() {
|
||||||
|
startOperation("Previous");
|
||||||
|
}
|
||||||
|
function goNext() {
|
||||||
|
startOperation("Next");
|
||||||
|
}
|
||||||
|
function playPause(source) {
|
||||||
|
startOperation("PlayPause");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
import org.kde.kcoreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: notificationItem
|
||||||
|
required property NotificationManager.Notifications notificationsModel
|
||||||
|
|
||||||
|
property var model
|
||||||
|
property int modelIndex
|
||||||
|
|
||||||
|
property PlasmaCore.DataSource timeSource
|
||||||
|
|
||||||
|
readonly property int notificationType: model.type
|
||||||
|
|
||||||
|
readonly property bool inGroup: model.isInGroup
|
||||||
|
readonly property bool inHistory: true
|
||||||
|
|
||||||
|
readonly property string applicationIconSource: model.applicationIconName
|
||||||
|
readonly property string applicationName: model.applicationName
|
||||||
|
readonly property string originName: model.originName || ""
|
||||||
|
|
||||||
|
readonly property string summary: model.summary
|
||||||
|
readonly property var time: model.updated || model.created
|
||||||
|
|
||||||
|
readonly property bool hasReplyAction: model.hasReplyAction || false
|
||||||
|
readonly property string replyActionLabel: model.replyActionLabel || ""
|
||||||
|
readonly property string replyPlaceholderText: model.replyPlaceholderText || ""
|
||||||
|
readonly property string replySubmitButtonText: model.replySubmitButtonText || ""
|
||||||
|
readonly property string replySubmitButtonIconName: model.replySubmitButtonIconName || ""
|
||||||
|
|
||||||
|
// configure button on every single notifications is bit overwhelming
|
||||||
|
readonly property bool configurable: !inGroup && model.configurable
|
||||||
|
|
||||||
|
readonly property bool dismissable: model.type === NotificationManager.Notifications.JobType
|
||||||
|
&& model.jobState !== NotificationManager.Notifications.JobStateStopped
|
||||||
|
&& model.dismissed
|
||||||
|
&& notificationSettings.permanentJobPopups
|
||||||
|
readonly property bool dismissed: model.dismissed || false
|
||||||
|
readonly property bool closable: model.closable
|
||||||
|
|
||||||
|
readonly property string body: model.body || ""
|
||||||
|
readonly property var icon: model.image || model.iconName
|
||||||
|
|
||||||
|
readonly property var urls: model.urls || []
|
||||||
|
|
||||||
|
readonly property int jobState: model.jobState || 0
|
||||||
|
readonly property int percentage: model.percentage || 0
|
||||||
|
readonly property int jobError: model.jobError || 0
|
||||||
|
readonly property bool suspendable: !!model.suspendable
|
||||||
|
readonly property bool killable: !!model.killable
|
||||||
|
|
||||||
|
readonly property QtObject jobDetails: model.jobDetails || null
|
||||||
|
|
||||||
|
readonly property string configureActionLabel: model.configureActionLabel || ""
|
||||||
|
readonly property bool hasDefaultAction: model.hasDefaultAction
|
||||||
|
readonly property bool addDefaultAction: (model.hasDefaultAction
|
||||||
|
&& model.defaultActionLabel
|
||||||
|
&& (model.actionLabels || []).indexOf(model.defaultActionLabel) === -1) ? true : false
|
||||||
|
readonly property var actionNames: {
|
||||||
|
var actions = (model.actionNames || []);
|
||||||
|
if (addDefaultAction) {
|
||||||
|
actions.unshift("default"); // prepend
|
||||||
|
}
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
readonly property var actionLabels: {
|
||||||
|
var labels = (model.actionLabels || []);
|
||||||
|
if (addDefaultAction) {
|
||||||
|
labels.unshift(model.defaultActionLabel);
|
||||||
|
}
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal actionInvoked(string actionName)
|
||||||
|
signal replied(string text)
|
||||||
|
signal openUrl(string url)
|
||||||
|
signal fileActionInvoked(QtObject action)
|
||||||
|
|
||||||
|
signal suspendJobClicked
|
||||||
|
signal resumeJobClicked
|
||||||
|
signal killJobClicked
|
||||||
|
|
||||||
|
onActionInvoked: {
|
||||||
|
if (actionName === "default") {
|
||||||
|
notificationsModel.invokeDefaultAction(notificationsModel.index(modelIndex, 0));
|
||||||
|
} else {
|
||||||
|
notificationsModel.invokeAction(notificationsModel.index(modelIndex, 0), actionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
expire();
|
||||||
|
}
|
||||||
|
onOpenUrl: {
|
||||||
|
Qt.openUrlExternally(url);
|
||||||
|
expire();
|
||||||
|
}
|
||||||
|
onFileActionInvoked: {
|
||||||
|
if (action.objectName === "movetotrash" || action.objectName === "deletefile") {
|
||||||
|
close();
|
||||||
|
} else {
|
||||||
|
expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSuspendJobClicked: notificationsModel.suspendJob(notificationsModel.index(modelIndex, 0))
|
||||||
|
onResumeJobClicked: notificationsModel.resumeJob(notificationsModel.index(modelIndex, 0))
|
||||||
|
onKillJobClicked: notificationsModel.killJob(notificationsModel.index(modelIndex, 0))
|
||||||
|
|
||||||
|
function expire() {
|
||||||
|
if (model.resident) {
|
||||||
|
model.expired = true;
|
||||||
|
} else {
|
||||||
|
notificationsModel.expire(notificationsModel.index(modelIndex, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
notificationsModel.close(notificationsModel.index(modelIndex, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO call
|
||||||
|
function configure() {
|
||||||
|
notificationsModel.configure(notificationsModel.index(modelIndex, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2011 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2014, 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: bodyText
|
||||||
|
|
||||||
|
background: Item {}
|
||||||
|
// Work around Qt bug where NativeRendering breaks for non-integer scale factors
|
||||||
|
// https://bugreports.qt.io/browse/QTBUG-67007
|
||||||
|
renderType: Screen.devicePixelRatio % 1 !== 0 ? Text.QtRendering : Text.NativeRendering
|
||||||
|
|
||||||
|
opacity: 0.6
|
||||||
|
maximumLineCount: 3
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
default property Item contentItem
|
||||||
|
|
||||||
|
property bool tapEnabled: false
|
||||||
|
|
||||||
|
property bool swipeGestureEnabled: false
|
||||||
|
|
||||||
|
property real dragOffset: 0
|
||||||
|
|
||||||
|
signal tapped()
|
||||||
|
signal dismissRequested()
|
||||||
|
signal configureClicked() // TODO implement settings button
|
||||||
|
|
||||||
|
onContentItemChanged: {
|
||||||
|
contentItem.parent = contentParent;
|
||||||
|
contentItem.anchors.fill = contentParent;
|
||||||
|
contentItem.anchors.margins = Kirigami.Units.largeSpacing;
|
||||||
|
contentParent.children.push(contentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: contentParent.implicitHeight
|
||||||
|
|
||||||
|
NumberAnimation on dragOffset {
|
||||||
|
id: dragAnim
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
onFinished: {
|
||||||
|
if (to !== 0) {
|
||||||
|
root.dismissRequested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// glow
|
||||||
|
RectangularGlow {
|
||||||
|
visible: Math.abs(dragOffset) !== root.width
|
||||||
|
anchors.topMargin: 1
|
||||||
|
anchors.leftMargin: 1
|
||||||
|
anchors.fill: mainCard
|
||||||
|
cornerRadius: mainCard.radius * 2
|
||||||
|
glowRadius: 2
|
||||||
|
spread: 0.2
|
||||||
|
color: Qt.lighter(PlasmaCore.Theme.backgroundColor, 0.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// shadow
|
||||||
|
Rectangle {
|
||||||
|
visible: Math.abs(dragOffset) !== root.width
|
||||||
|
anchors.fill: mainCard
|
||||||
|
anchors.leftMargin: -1
|
||||||
|
anchors.rightMargin: -1
|
||||||
|
anchors.bottomMargin: -1
|
||||||
|
|
||||||
|
color: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.3)
|
||||||
|
radius: PlasmaCore.Units.smallSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// card
|
||||||
|
Rectangle {
|
||||||
|
id: mainCard
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: root.dragOffset > 0 ? root.dragOffset : 0
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: root.dragOffset < 0 ? -root.dragOffset : 0
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
color: (root.tapEnabled && mouseArea.pressed) ? Qt.darker(PlasmaCore.Theme.backgroundColor, 1.1) : PlasmaCore.Theme.backgroundColor
|
||||||
|
radius: PlasmaCore.Units.smallSpacing
|
||||||
|
implicitHeight: contentParent.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
// ensure this is behind the content to not interfere
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
if (root.tapEnabled) {
|
||||||
|
root.tapped()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// content parent
|
||||||
|
Item {
|
||||||
|
id: contentParent
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: root.dragOffset > 0 ? parent.left : undefined
|
||||||
|
anchors.right: root.dragOffset < 0 ? parent.right : undefined
|
||||||
|
|
||||||
|
width: root.width
|
||||||
|
implicitHeight: contentItem.implicitHeight + contentItem.anchors.topMargin + contentItem.anchors.bottomMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
enabled: root.swipeGestureEnabled
|
||||||
|
yAxis.enabled: false
|
||||||
|
|
||||||
|
property real startDragOffset: 0
|
||||||
|
property real startPosition: 0
|
||||||
|
property bool startActive: false
|
||||||
|
|
||||||
|
onTranslationChanged: {
|
||||||
|
if (startActive) {
|
||||||
|
startDragOffset = root.dragOffset;
|
||||||
|
startPosition = translation.x;
|
||||||
|
startActive = false;
|
||||||
|
}
|
||||||
|
root.dragOffset = startDragOffset + (translation.x - startPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
dragAnim.stop();
|
||||||
|
startActive = active;
|
||||||
|
|
||||||
|
if (!active) { // release event
|
||||||
|
let threshold = PlasmaCore.Units.gridUnit * 5; // drag threshold
|
||||||
|
if (root.dragOffset > threshold) {
|
||||||
|
dragAnim.to = root.width;
|
||||||
|
} else if (root.dragOffset < -threshold) {
|
||||||
|
dragAnim.to = -root.width;
|
||||||
|
} else {
|
||||||
|
dragAnim.to = 0;
|
||||||
|
}
|
||||||
|
dragAnim.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: actionContainer
|
||||||
|
|
||||||
|
required property BaseNotificationItem notification
|
||||||
|
|
||||||
|
implicitHeight: Math.max(actionFlow.implicitHeight, replyLoader.height)
|
||||||
|
visible: actionRepeater.count > 0
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: actionFlow
|
||||||
|
width: parent.width
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
layoutDirection: Qt.RightToLeft
|
||||||
|
enabled: !replyLoader.active
|
||||||
|
opacity: replyLoader.active ? 0 : 1
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// action buttons
|
||||||
|
Repeater {
|
||||||
|
id: actionRepeater
|
||||||
|
|
||||||
|
model: {
|
||||||
|
const buttons = [];
|
||||||
|
var actionNames = (notificationItem.actionNames || []);
|
||||||
|
var actionLabels = (notificationItem.actionLabels || []);
|
||||||
|
for (var i = actionNames.length - 1; i >= 0; --i) {
|
||||||
|
buttons.push({
|
||||||
|
actionName: actionNames[i],
|
||||||
|
label: actionLabels[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notificationItem.hasReplyAction) {
|
||||||
|
buttons.unshift({
|
||||||
|
actionName: "inline-reply",
|
||||||
|
label: notificationItem.replyActionLabel || i18nc("Reply to message", "Reply")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.ToolButton {
|
||||||
|
flat: false
|
||||||
|
text: modelData.label || ""
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (modelData.actionName === "inline-reply") {
|
||||||
|
replyLoader.beginReply();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
notificationItem.actionInvoked(modelData.actionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline reply field
|
||||||
|
Loader {
|
||||||
|
id: replyLoader
|
||||||
|
width: parent.width
|
||||||
|
height: active ? item.implicitHeight : 0
|
||||||
|
|
||||||
|
// When there is only one action and it is a reply action, show text field right away
|
||||||
|
active: false
|
||||||
|
visible: active
|
||||||
|
opacity: active ? 1 : 0
|
||||||
|
x: active ? 0 : parent.width
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginReply() {
|
||||||
|
active = true
|
||||||
|
replyLoader.item.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceComponent: NotificationReplyField {
|
||||||
|
placeholderText: notificationItem.replyPlaceholderText
|
||||||
|
buttonIconName: notificationItem.replySubmitButtonIconName
|
||||||
|
buttonText: notificationItem.replySubmitButtonText
|
||||||
|
onReplied: notificationItem.replied(text)
|
||||||
|
|
||||||
|
replying: replyLoader.active
|
||||||
|
onBeginReplyRequested: replyLoader.beginReply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
import org.kde.kcoreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
|
import "util.js" as Util
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: notificationHeading
|
||||||
|
property int notificationType
|
||||||
|
|
||||||
|
property var applicationIconSource
|
||||||
|
property string applicationName
|
||||||
|
property string originName
|
||||||
|
|
||||||
|
property var time
|
||||||
|
property PlasmaCore.DataSource timeSource
|
||||||
|
|
||||||
|
property int jobState
|
||||||
|
property QtObject jobDetails
|
||||||
|
|
||||||
|
property real timeout: 5000
|
||||||
|
property real remainingTime: 0
|
||||||
|
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.preferredHeight: Math.max(applicationNameLabel.implicitHeight, PlasmaCore.Units.iconSizes.small)
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
id: applicationIconItem
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.preferredWidth: PlasmaCore.Units.iconSizes.small
|
||||||
|
Layout.preferredHeight: PlasmaCore.Units.iconSizes.small
|
||||||
|
source: notificationHeading.applicationIconSource
|
||||||
|
usesPlasmaTheme: false
|
||||||
|
visible: valid
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: applicationNameLabel
|
||||||
|
Layout.leftMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
opacity: 0.8
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideLeft
|
||||||
|
text: notificationHeading.applicationName + (notificationHeading.originName ? " · " + notificationHeading.originName : "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: spacer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.kcoreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
|
import "util.js" as Util
|
||||||
|
|
||||||
|
// notification properties are in BaseNotificationItem
|
||||||
|
BaseNotificationItem {
|
||||||
|
id: notificationItem
|
||||||
|
implicitHeight: mainCard.implicitHeight
|
||||||
|
|
||||||
|
// notification heading for groups with one element
|
||||||
|
NotificationGroupHeader {
|
||||||
|
id: notificationHeading
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
PlasmaCore.ColorScope.colorGroup: PlasmaCore.Theme.HeaderColorGroup
|
||||||
|
PlasmaCore.ColorScope.inherit: false
|
||||||
|
|
||||||
|
visible: !notificationItem.inGroup
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
|
||||||
|
applicationName: notificationItem.applicationName
|
||||||
|
applicationIconSource: notificationItem.applicationIconSource
|
||||||
|
originName: notificationItem.originName
|
||||||
|
|
||||||
|
notificationType: notificationItem.notificationType
|
||||||
|
jobState: notificationItem.jobState
|
||||||
|
jobDetails: notificationItem.jobDetails
|
||||||
|
|
||||||
|
time: notificationItem.time
|
||||||
|
timeSource: notificationItem.timeSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification
|
||||||
|
NotificationCard {
|
||||||
|
id: mainCard
|
||||||
|
anchors.topMargin: notificationHeading.visible ? Kirigami.Units.largeSpacing : 0
|
||||||
|
anchors.top: notificationHeading.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
tapEnabled: notificationItem.hasDefaultAction
|
||||||
|
onTapped: notificationItem.actionInvoked("default");
|
||||||
|
swipeGestureEnabled: notificationItem.notificationType != NotificationManager.Notifications.JobType
|
||||||
|
onDismissRequested: notificationItem.close()
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
// notification summary row
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.bottomMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
// notification summary
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: summaryLabel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
maximumLineCount: 3
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: Util.determineNotificationHeadingText(notificationItem)
|
||||||
|
visible: text !== ""
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification timestamp
|
||||||
|
NotificationTimeText {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
|
notificationType: notificationItem.notificationType
|
||||||
|
jobState: notificationItem.jobState
|
||||||
|
jobDetails: notificationItem.jobDetails
|
||||||
|
|
||||||
|
time: notificationItem.time
|
||||||
|
timeSource: notificationItem.timeSource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification contents
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
// notification text
|
||||||
|
NotificationBodyLabel {
|
||||||
|
id: bodyLabel
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
// HACK RichText does not allow to specify link color and since LineEdit
|
||||||
|
// does not support StyledText, we have to inject some CSS to force the color,
|
||||||
|
// cf. QTBUG-81463 and to some extent QTBUG-80354
|
||||||
|
text: "<style>a { color: " + PlasmaCore.Theme.linkColor + "; }</style>" + notificationItem.body
|
||||||
|
|
||||||
|
// Cannot do text !== "" because RichText adds some HTML tags even when empty
|
||||||
|
visible: notificationItem.body !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification icon
|
||||||
|
Item {
|
||||||
|
id: iconContainer
|
||||||
|
Layout.preferredWidth: PlasmaCore.Units.iconSizes.large
|
||||||
|
Layout.preferredHeight: PlasmaCore.Units.iconSizes.large
|
||||||
|
Layout.topMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
visible: iconItem.active
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
id: iconItem
|
||||||
|
// don't show two identical icons
|
||||||
|
readonly property bool active: valid && source != notificationItem.applicationIconSource
|
||||||
|
anchors.fill: parent
|
||||||
|
usesPlasmaTheme: false
|
||||||
|
smooth: true
|
||||||
|
// don't show a generic "info" icon since this is a notification already
|
||||||
|
source: notificationItem.icon !== "dialog-information" ? notificationItem.icon : ""
|
||||||
|
visible: active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification actions
|
||||||
|
NotificationFooterActions {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
notification: notificationItem
|
||||||
|
}
|
||||||
|
|
||||||
|
// thumbnails
|
||||||
|
Loader {
|
||||||
|
id: thumbnailStripLoader
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
active: notificationItem.urls.length > 0
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
|
sourceComponent: ThumbnailStrip {
|
||||||
|
leftPadding: -thumbnailStripLoader.Layout.leftMargin
|
||||||
|
rightPadding: -thumbnailStripLoader.Layout.rightMargin
|
||||||
|
topPadding: -notificationItem.thumbnailTopPadding
|
||||||
|
bottomPadding: -thumbnailStripLoader.Layout.bottomMargin
|
||||||
|
urls: notificationItem.urls
|
||||||
|
onOpenUrl: notificationItem.openUrl(url)
|
||||||
|
onFileActionInvoked: notificationItem.fileActionInvoked(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: replyRow
|
||||||
|
|
||||||
|
signal beginReplyRequested
|
||||||
|
signal replied(string text)
|
||||||
|
|
||||||
|
property bool replying: false
|
||||||
|
|
||||||
|
property alias text: replyTextField.text
|
||||||
|
property string placeholderText
|
||||||
|
property string buttonIconName
|
||||||
|
property string buttonText
|
||||||
|
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
function activate() {
|
||||||
|
replyTextField.forceActiveFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.TextField {
|
||||||
|
id: replyTextField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: replyRow.placeholderText || i18nc("Text field placeholder", "Type a reply…")
|
||||||
|
onAccepted: {
|
||||||
|
if (replyButton.enabled) {
|
||||||
|
replyRow.replied(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catches mouse click when reply field is already shown to start a reply
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.IBeamCursor
|
||||||
|
visible: !replyRow.replying
|
||||||
|
onPressed: replyRow.beginReplyRequested()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: replyButton
|
||||||
|
icon.name: replyRow.buttonIconName || "document-send"
|
||||||
|
text: replyRow.buttonText || i18nc("@action:button", "Send")
|
||||||
|
enabled: replyTextField.length > 0
|
||||||
|
onClicked: replyRow.replied(replyTextField.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
|
import org.kde.kcoreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
|
import "util.js" as Util
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
id: ageLabel
|
||||||
|
|
||||||
|
property int notificationType: model.type
|
||||||
|
property int jobState
|
||||||
|
property QtObject jobDetails
|
||||||
|
|
||||||
|
property var time
|
||||||
|
property PlasmaCore.DataSource timeSource
|
||||||
|
|
||||||
|
// notification created/updated time changed
|
||||||
|
onTimeChanged: updateAgoText()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: timeSource
|
||||||
|
// clock time changed
|
||||||
|
function onDataChanged() {
|
||||||
|
ageLabel.updateAgoText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: updateAgoText()
|
||||||
|
|
||||||
|
function updateAgoText() {
|
||||||
|
ageLabel.agoText = Util.generateNotificationHeaderAgoText(time, jobState);
|
||||||
|
}
|
||||||
|
|
||||||
|
font.pixelSize: PlasmaCore.Theme.defaultFont.pixelSize * 0.8
|
||||||
|
|
||||||
|
// the "n minutes ago" text, for jobs we show remaining time instead
|
||||||
|
// updated periodically by a Timer hence this property with generate() function
|
||||||
|
property string agoText: ""
|
||||||
|
visible: text !== ""
|
||||||
|
opacity: 0.6
|
||||||
|
text: Util.generateNotificationHeaderRemainingText(notificationType, jobState, jobDetails) || agoText
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
|
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: thumbnailArea
|
||||||
|
|
||||||
|
// The protocol supports multiple URLs but so far it's only used to show
|
||||||
|
// a single preview image, so this code is simplified a lot to accommodate
|
||||||
|
// this usecase and drops everything else (fallback to app icon or ListView
|
||||||
|
// for multiple files)
|
||||||
|
property var urls
|
||||||
|
|
||||||
|
readonly property alias menuOpen: fileMenu.visible
|
||||||
|
|
||||||
|
property int _pressX: -1
|
||||||
|
property int _pressY: -1
|
||||||
|
|
||||||
|
property int leftPadding: 0
|
||||||
|
property int rightPadding: 0
|
||||||
|
property int topPadding: 0
|
||||||
|
property int bottomPadding: 0
|
||||||
|
|
||||||
|
signal openUrl(string url)
|
||||||
|
signal fileActionInvoked(QtObject action)
|
||||||
|
|
||||||
|
implicitHeight: Math.max(menuButton.height + 2 * menuButton.anchors.topMargin,
|
||||||
|
Math.round(Math.min(width / 3, width / thumbnailer.ratio)))
|
||||||
|
+ topPadding + bottomPadding
|
||||||
|
|
||||||
|
MobileShell.NotificationFileMenu {
|
||||||
|
id: fileMenu
|
||||||
|
url: thumbnailer.url
|
||||||
|
visualParent: menuButton
|
||||||
|
onActionTriggered: thumbnailArea.fileActionInvoked(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
MobileShell.NotificationThumbnailer {
|
||||||
|
id: thumbnailer
|
||||||
|
|
||||||
|
readonly property real ratio: pixmapSize.height ? pixmapSize.width / pixmapSize.height : 1
|
||||||
|
|
||||||
|
url: urls[0]
|
||||||
|
// height is dynamic, so request a "square" size and then show it fitting to aspect ratio
|
||||||
|
size: Qt.size(thumbnailArea.width, thumbnailArea.width)
|
||||||
|
}
|
||||||
|
|
||||||
|
KQCAddons.QPixmapItem {
|
||||||
|
id: previewBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
layer.enabled: true
|
||||||
|
opacity: 0.25
|
||||||
|
pixmap: thumbnailer.pixmap
|
||||||
|
layer.effect: FastBlur {
|
||||||
|
source: previewBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: thumbnailArea.leftPadding
|
||||||
|
rightMargin: thumbnailArea.rightPadding
|
||||||
|
topMargin: thumbnailArea.topPadding
|
||||||
|
bottomMargin: thumbnailArea.bottomPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
KQCAddons.QPixmapItem {
|
||||||
|
id: previewPixmap
|
||||||
|
anchors.fill: parent
|
||||||
|
pixmap: thumbnailer.pixmap
|
||||||
|
smooth: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaCore.IconItem {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: height
|
||||||
|
height: PlasmaCore.Units.roundToIconSize(parent.height)
|
||||||
|
usesPlasmaTheme: false
|
||||||
|
source: !thumbnailer.busy && !thumbnailer.hasPreview ? thumbnailer.iconName : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.BusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
running: thumbnailer.busy
|
||||||
|
visible: thumbnailer.busy
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: menuButton
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
margins: PlasmaCore.Units.smallSpacing
|
||||||
|
}
|
||||||
|
Accessible.name: tooltip.text
|
||||||
|
icon.name: "application-menu"
|
||||||
|
checkable: true
|
||||||
|
|
||||||
|
onPressedChanged: {
|
||||||
|
if (pressed) {
|
||||||
|
// fake "pressed" while menu is open
|
||||||
|
checked = Qt.binding(function() {
|
||||||
|
return fileMenu.visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
fileMenu.visualParent = this;
|
||||||
|
// -1 tells it to "align bottom left of visualParent (this)"
|
||||||
|
fileMenu.open(-1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolTip {
|
||||||
|
id: tooltip
|
||||||
|
text: i18n("More Options…")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
components/mobileshell/qml/widgets/notifications/util.js
Normal file
85
components/mobileshell/qml/widgets/notifications/util.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
.import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
.import QtQml 2.15 as QtQml
|
||||||
|
|
||||||
|
function determineNotificationHeadingText(notificationItem) {
|
||||||
|
if (notificationItem.notificationType === NotificationManager.Notifications.JobType) {
|
||||||
|
if (notificationItem.jobState === NotificationManager.Notifications.JobStateSuspended) {
|
||||||
|
if (notificationItem.summary) {
|
||||||
|
return i18nc("Job name, e.g. Copying is paused", "%1 (Paused)", notificationItem.summary);
|
||||||
|
}
|
||||||
|
} else if (notificationItem.jobState === NotificationManager.Notifications.JobStateStopped) {
|
||||||
|
if (notificationItem.jobError) {
|
||||||
|
if (notificationItem.summary) {
|
||||||
|
return i18nc("Job name, e.g. Copying has failed", "%1 (Failed)", notificationItem.summary);
|
||||||
|
} else {
|
||||||
|
return i18n("Job Failed");
|
||||||
|
}
|
||||||
|
} else if (notificationItem.summary) {
|
||||||
|
return i18ndc("plasma_applet_org.kde.plasma.notifications", "Job name, e.g. Copying has finished", "%1 (Finished)", notificationItem.summary);
|
||||||
|
}
|
||||||
|
return i18nd("plasma_applet_org.kde.plasma.notifications", "Job Finished");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// some apps use their app name as summary, avoid showing the same text twice
|
||||||
|
// try very hard to match the two
|
||||||
|
if (notificationItem.summary && notificationItem.summary.toLocaleLowerCase().trim() !== notificationItem.applicationName.toLocaleLowerCase().trim()) {
|
||||||
|
return notificationItem.summary;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateNotificationHeaderAgoText(time, jobState) {
|
||||||
|
if (!time || isNaN(time.getTime()) || jobState === NotificationManager.Notifications.JobStateRunning) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const deltaMinutes = Math.floor((Date.now() - time.getTime()) / 1000 / 60);
|
||||||
|
if (deltaMinutes < 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Received less than an hour ago, show relative minutes
|
||||||
|
if (deltaMinutes < 60) {
|
||||||
|
return i18nc("Notification was added minutes ago, keep short", "%1m ago", deltaMinutes);
|
||||||
|
}
|
||||||
|
// Received less than a day ago, show time, 22 hours so the time isn't as ambiguous between today and yesterday
|
||||||
|
if (deltaMinutes < 60 * 22) {
|
||||||
|
return Qt.formatTime(time, Qt.locale().timeFormat(QtQml.Locale.ShortFormat).replace(/.ss?/i, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise show relative date (Yesterday, "Last Sunday", or just date if too far in the past)
|
||||||
|
return KCoreAddons.Format.formatRelativeDate(time, QtQml.Locale.ShortFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateNotificationHeaderRemainingText(notificationType, jobState, jobDetails) {
|
||||||
|
if (notificationType !== NotificationManager.Notifications.JobType || jobState !== NotificationManager.Notifications.JobStateRunning) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const details = jobDetails;
|
||||||
|
if (!details || !details.speed) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var remaining = details.totalBytes - details.processedBytes;
|
||||||
|
if (remaining <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var eta = remaining / details.speed;
|
||||||
|
if (eta < 0.5) { // Avoid showing "0 seconds remaining"
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eta < 60) { // 1 minute
|
||||||
|
return i18nc("seconds remaining, keep short", "%1 s remaining", Math.round(eta));
|
||||||
|
}
|
||||||
|
if (eta < 60 * 60) {// 1 hour
|
||||||
|
return i18nc("minutes remaining, keep short", "%1m remaining", Math.round(eta / 60));
|
||||||
|
}
|
||||||
|
if (eta < 60 * 60 * 5) { // 5 hours max, if it takes even longer there's no real point in showing that
|
||||||
|
return i18nc("hours remaining, keep short", "%1h remaining", Math.round(eta / 60 / 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
module org.kde.plasma.private.mobileshell
|
|
||||||
|
|
||||||
singleton HomeScreenControls 1.0 HomeScreenControls.qml
|
|
||||||
singleton TopPanelControls 1.0 TopPanelControls.qml
|
|
||||||
|
|
@ -123,6 +123,15 @@ void QuickSetting::setText(const QString &text)
|
||||||
Q_EMIT textChanged(text);
|
Q_EMIT textChanged(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickSetting::setStatus(const QString &status)
|
||||||
|
{
|
||||||
|
if (m_status == status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_status = status;
|
||||||
|
Q_EMIT statusChanged(status);
|
||||||
|
}
|
||||||
|
|
||||||
QQmlListProperty<QObject> QuickSetting::children()
|
QQmlListProperty<QObject> QuickSetting::children()
|
||||||
{
|
{
|
||||||
return QQmlListProperty<QObject>(this, &m_children);
|
return QQmlListProperty<QObject>(this, &m_children);
|
||||||
|
|
@ -15,6 +15,7 @@ class QuickSetting : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString text READ text WRITE setText REQUIRED NOTIFY textChanged)
|
Q_PROPERTY(QString text READ text WRITE setText REQUIRED NOTIFY textChanged)
|
||||||
|
Q_PROPERTY(QString status READ status WRITE setStatus REQUIRED NOTIFY statusChanged) // if no status is explicitly set, On/Off is used by default
|
||||||
Q_PROPERTY(QString icon READ iconName WRITE setIconName REQUIRED NOTIFY iconNameChanged)
|
Q_PROPERTY(QString icon READ iconName WRITE setIconName REQUIRED NOTIFY iconNameChanged)
|
||||||
Q_PROPERTY(QString settingsCommand READ settingsCommand WRITE setSettingsCommand NOTIFY settingsCommandChanged)
|
Q_PROPERTY(QString settingsCommand READ settingsCommand WRITE setSettingsCommand NOTIFY settingsCommandChanged)
|
||||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
||||||
|
|
@ -28,6 +29,10 @@ public:
|
||||||
{
|
{
|
||||||
return m_text;
|
return m_text;
|
||||||
}
|
}
|
||||||
|
QString status() const
|
||||||
|
{
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
QString iconName() const
|
QString iconName() const
|
||||||
{
|
{
|
||||||
return m_iconName;
|
return m_iconName;
|
||||||
|
|
@ -42,6 +47,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setText(const QString &text);
|
void setText(const QString &text);
|
||||||
|
void setStatus(const QString &status);
|
||||||
void setIconName(const QString &iconName);
|
void setIconName(const QString &iconName);
|
||||||
void setSettingsCommand(const QString &settingsCommand);
|
void setSettingsCommand(const QString &settingsCommand);
|
||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
|
|
@ -50,12 +56,14 @@ public:
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void enabledChanged(bool enabled);
|
void enabledChanged(bool enabled);
|
||||||
void textChanged(const QString &text);
|
void textChanged(const QString &text);
|
||||||
|
void statusChanged(const QString &text);
|
||||||
void iconNameChanged(const QString &icon);
|
void iconNameChanged(const QString &icon);
|
||||||
void settingsCommandChanged(const QString &settingsCommand);
|
void settingsCommandChanged(const QString &settingsCommand);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_enabled = true;
|
bool m_enabled = true;
|
||||||
QString m_text;
|
QString m_text;
|
||||||
|
QString m_status;
|
||||||
QString m_iconName;
|
QString m_iconName;
|
||||||
QString m_settingsCommand;
|
QString m_settingsCommand;
|
||||||
QList<QObject *> m_children;
|
QList<QObject *> m_children;
|
||||||
245
components/mobileshell/shellutil.cpp
Normal file
245
components/mobileshell/shellutil.cpp
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2018 Bhushan Shah <bshah@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shellutil.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <qplatformdefs.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <KConfigGroup>
|
||||||
|
#include <KIO/ApplicationLauncherJob>
|
||||||
|
#include <KLocalizedString>
|
||||||
|
#include <KNotification>
|
||||||
|
|
||||||
|
#include <QDBusPendingReply>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
|
||||||
|
#define FORMAT24H "HH:mm:ss"
|
||||||
|
|
||||||
|
constexpr int SCREENSHOT_DELAY = 200;
|
||||||
|
|
||||||
|
/* -- Static Helpers --------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static QImage allocateImage(const QVariantMap &metadata)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
const uint width = metadata.value(QStringLiteral("width")).toUInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint height = metadata.value(QStringLiteral("height")).toUInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint format = metadata.value(QStringLiteral("format")).toUInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QImage(width, height, QImage::Format(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QImage readImage(int fileDescriptor, const QVariantMap &metadata)
|
||||||
|
{
|
||||||
|
QFile file;
|
||||||
|
if (!file.open(fileDescriptor, QFileDevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
|
||||||
|
close(fileDescriptor);
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage result = allocateImage(metadata);
|
||||||
|
if (result.isNull()) {
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream stream(&file);
|
||||||
|
stream.readRawData(reinterpret_cast<char *>(result.bits()), result.sizeInBytes());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellUtil::ShellUtil(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
// setHasConfigurationInterface(true);
|
||||||
|
m_kscreenInterface = new org::kde::KScreen(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/kscreen"), QDBusConnection::sessionBus(), this);
|
||||||
|
m_screenshotInterface = new OrgKdeKWinScreenShot2Interface(QStringLiteral("org.kde.KWin.ScreenShot2"),
|
||||||
|
QStringLiteral("/org/kde/KWin/ScreenShot2"),
|
||||||
|
QDBusConnection::sessionBus(),
|
||||||
|
this);
|
||||||
|
|
||||||
|
m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
|
||||||
|
m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig);
|
||||||
|
|
||||||
|
// watch for changes to locale config, to update 12/24 hour time
|
||||||
|
connect(m_localeConfigWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
|
||||||
|
if (group.name() == "Locale") {
|
||||||
|
// we have to reparse for new changes (from system settings)
|
||||||
|
m_localeConfig->reparseConfiguration();
|
||||||
|
Q_EMIT isSystem24HourFormatChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellUtil::~ShellUtil() = default;
|
||||||
|
|
||||||
|
ShellUtil *ShellUtil::instance()
|
||||||
|
{
|
||||||
|
static ShellUtil *inst = new ShellUtil(nullptr);
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::executeCommand(const QString &command)
|
||||||
|
{
|
||||||
|
qWarning() << "Executing" << command;
|
||||||
|
const QStringList commandAndArguments = QProcess::splitCommand(command);
|
||||||
|
QProcess::startDetached(commandAndArguments.front(), commandAndArguments.mid(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::toggleTorch()
|
||||||
|
{
|
||||||
|
// FIXME this is hardcoded to the PinePhone for now
|
||||||
|
static auto FLASH_SYSFS_PATH = "/sys/devices/platform/led-controller/leds/white:flash/brightness";
|
||||||
|
int fd = open(FLASH_SYSFS_PATH, O_WRONLY);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
qWarning() << "Unable to open file %s" << FLASH_SYSFS_PATH;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(fd, m_running ? "0" : "1", 1);
|
||||||
|
close(fd);
|
||||||
|
m_running = !m_running;
|
||||||
|
Q_EMIT torchChanged(m_running);
|
||||||
|
}
|
||||||
|
bool ShellUtil::torchEnabled() const
|
||||||
|
{
|
||||||
|
return m_running;
|
||||||
|
}
|
||||||
|
bool ShellUtil::autoRotate()
|
||||||
|
{
|
||||||
|
QDBusPendingReply<bool> reply = m_kscreenInterface->getAutoRotate();
|
||||||
|
reply.waitForFinished();
|
||||||
|
if (reply.isError()) {
|
||||||
|
qWarning() << "Getting auto rotate failed:" << reply.error().name() << reply.error().message();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return reply.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::setAutoRotate(bool value)
|
||||||
|
{
|
||||||
|
QDBusPendingReply<> reply = m_kscreenInterface->setAutoRotate(value);
|
||||||
|
reply.waitForFinished();
|
||||||
|
if (reply.isError()) {
|
||||||
|
qWarning() << "Setting auto rotate failed:" << reply.error().name() << reply.error().message();
|
||||||
|
} else {
|
||||||
|
emit autoRotateChanged(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::handleMetaDataReceived(const QVariantMap &metadata, int fd)
|
||||||
|
{
|
||||||
|
const QString type = metadata.value(QStringLiteral("type")).toString();
|
||||||
|
if (type != QLatin1String("raw")) {
|
||||||
|
qWarning() << "Unsupported metadata type:" << type;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto watcher = new QFutureWatcher<QImage>(this);
|
||||||
|
connect(watcher, &QFutureWatcher<QImage>::finished, this, [watcher]() {
|
||||||
|
watcher->deleteLater();
|
||||||
|
|
||||||
|
QString filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
qWarning() << "Couldn't find a writable location for the screenshot!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDir picturesDir(filePath);
|
||||||
|
if (!picturesDir.mkpath(QStringLiteral("Screenshots"))) {
|
||||||
|
qWarning() << "Couldn't create folder at" << picturesDir.path() + QStringLiteral("/Screenshots") << "to take screenshot.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filePath += QStringLiteral("/Screenshots/Screenshot_%1.png").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss")));
|
||||||
|
const auto m_result = watcher->result();
|
||||||
|
if (m_result.isNull() || !m_result.save(filePath)) {
|
||||||
|
qWarning() << "Screenshot failed";
|
||||||
|
} else {
|
||||||
|
KNotification *notif = new KNotification("captured");
|
||||||
|
notif->setComponentName(QStringLiteral("plasma_phone_components"));
|
||||||
|
notif->setTitle(i18n("New Screenshot"));
|
||||||
|
notif->setUrls({QUrl::fromLocalFile(filePath)});
|
||||||
|
notif->setText(i18n("New screenshot saved to %1", filePath));
|
||||||
|
notif->sendEvent();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
watcher->setFuture(QtConcurrent::run(readImage, fd, metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::takeScreenshot()
|
||||||
|
{
|
||||||
|
// wait ~200 ms to wait for rest of animations
|
||||||
|
QTimer::singleShot(SCREENSHOT_DELAY, [=]() {
|
||||||
|
int lPipeFds[2];
|
||||||
|
if (pipe2(lPipeFds, O_CLOEXEC) != 0) {
|
||||||
|
qWarning() << "Could not take screenshot";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't have access to the ScreenPool so we'll just take the first screen
|
||||||
|
QVariantMap options;
|
||||||
|
options.insert(QStringLiteral("native-resolution"), true);
|
||||||
|
|
||||||
|
auto pendingCall = m_screenshotInterface->CaptureScreen(qGuiApp->screens().constFirst()->name(), options, QDBusUnixFileDescriptor(lPipeFds[1]));
|
||||||
|
close(lPipeFds[1]);
|
||||||
|
auto pipeFileDescriptor = lPipeFds[0];
|
||||||
|
|
||||||
|
auto watcher = new QDBusPendingCallWatcher(pendingCall, this);
|
||||||
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, pipeFileDescriptor]() {
|
||||||
|
watcher->deleteLater();
|
||||||
|
const QDBusPendingReply<QVariantMap> reply = *watcher;
|
||||||
|
|
||||||
|
if (reply.isError()) {
|
||||||
|
qWarning() << "Screenshot request failed:" << reply.error().message();
|
||||||
|
} else {
|
||||||
|
handleMetaDataReceived(reply, pipeFileDescriptor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShellUtil::isSystem24HourFormat()
|
||||||
|
{
|
||||||
|
KConfigGroup localeSettings = KConfigGroup(m_localeConfig, "Locale");
|
||||||
|
|
||||||
|
QString timeFormat = localeSettings.readEntry("TimeFormat", QStringLiteral(FORMAT24H));
|
||||||
|
return timeFormat == QStringLiteral(FORMAT24H);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShellUtil::launchApp(const QString &app)
|
||||||
|
{
|
||||||
|
const KService::Ptr appService = KService::serviceByDesktopName(app);
|
||||||
|
if (!appService) {
|
||||||
|
qWarning() << "Could not find" << app;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto job = new KIO::ApplicationLauncherJob(appService, this);
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
57
components/mobileshell/shellutil.h
Normal file
57
components/mobileshell/shellutil.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <KConfigWatcher>
|
||||||
|
#include <KSharedConfig>
|
||||||
|
|
||||||
|
#include "kscreeninterface.h"
|
||||||
|
#include "screenshot2interface.h"
|
||||||
|
|
||||||
|
class ShellUtil : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool autoRotateEnabled READ autoRotate WRITE setAutoRotate NOTIFY autoRotateChanged);
|
||||||
|
Q_PROPERTY(bool torchEnabled READ torchEnabled NOTIFY torchChanged);
|
||||||
|
Q_PROPERTY(bool isSystem24HourFormat READ isSystem24HourFormat NOTIFY isSystem24HourFormatChanged);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShellUtil(QObject *parent = nullptr);
|
||||||
|
~ShellUtil() override;
|
||||||
|
static ShellUtil *instance();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void executeCommand(const QString &command);
|
||||||
|
void launchApp(const QString &app);
|
||||||
|
void toggleTorch();
|
||||||
|
void takeScreenshot();
|
||||||
|
|
||||||
|
bool autoRotate();
|
||||||
|
void setAutoRotate(bool value);
|
||||||
|
|
||||||
|
bool torchEnabled() const;
|
||||||
|
|
||||||
|
bool isSystem24HourFormat();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void autoRotateChanged(bool value);
|
||||||
|
void torchChanged(bool value);
|
||||||
|
void isSystem24HourFormatChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleMetaDataReceived(const QVariantMap &metadata, int fd);
|
||||||
|
bool m_running = false;
|
||||||
|
|
||||||
|
KConfigWatcher::Ptr m_localeConfigWatcher;
|
||||||
|
KSharedConfig::Ptr m_localeConfig;
|
||||||
|
|
||||||
|
org::kde::KScreen *m_kscreenInterface;
|
||||||
|
OrgKdeKWinScreenShot2Interface *m_screenshotInterface;
|
||||||
|
};
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
qt_add_dbus_interfaces(DBUS_SRCS dbus/org.kde.KWin.ScreenShot2.xml
|
|
||||||
dbus/org.kde.KScreen.xml)
|
|
||||||
|
|
||||||
set(phonepanel_SRCS
|
set(phonepanel_SRCS
|
||||||
phonepanel.cpp
|
phonepanel.cpp
|
||||||
${DBUS_SRCS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(plasma_applet_phonepanel MODULE ${phonepanel_SRCS})
|
add_library(plasma_applet_phonepanel MODULE ${phonepanel_SRCS})
|
||||||
|
|
@ -15,8 +11,6 @@ target_link_libraries(plasma_applet_phonepanel
|
||||||
Qt::DBus
|
Qt::DBus
|
||||||
KF5::Plasma
|
KF5::Plasma
|
||||||
KF5::I18n
|
KF5::I18n
|
||||||
KF5::Notifications
|
|
||||||
KF5::KIOGui
|
|
||||||
KF5::Service
|
KF5::Service
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.1
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
|
|
||||||
|
|
||||||
PlasmaCore.ToolTipArea {
|
|
||||||
id: appletRoot
|
|
||||||
objectName: "org.kde.desktop-CompactApplet"
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
icon: plasmoid.icon
|
|
||||||
mainText: plasmoid.toolTipMainText
|
|
||||||
subText: plasmoid.toolTipSubText
|
|
||||||
location: if (plasmoid.parent && plasmoid.parent.parent.objectName === "hiddenTasksColumn" && plasmoid.location !== PlasmaCore.Types.LeftEdge) {
|
|
||||||
return PlasmaCore.Types.RightEdge;
|
|
||||||
} else {
|
|
||||||
return plasmoid.location;
|
|
||||||
}
|
|
||||||
active: !plasmoid.expanded
|
|
||||||
textFormat: plasmoid.toolTipTextFormat
|
|
||||||
mainItem: plasmoid.toolTipItem ? plasmoid.toolTipItem : null
|
|
||||||
|
|
||||||
property Item fullRepresentation
|
|
||||||
property Item compactRepresentation
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: plasmoid
|
|
||||||
onContextualActionsAboutToShow: appletRoot.hideToolTip()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Layout.minimumWidth: {
|
|
||||||
switch (plasmoid.formFactor) {
|
|
||||||
case PlasmaCore.Types.Vertical:
|
|
||||||
return 0;
|
|
||||||
case PlasmaCore.Types.Horizontal:
|
|
||||||
return height;
|
|
||||||
default:
|
|
||||||
return PlasmaCore.Units.gridUnit * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Layout.minimumHeight: {
|
|
||||||
switch (plasmoid.formFactor) {
|
|
||||||
case PlasmaCore.Types.Vertical:
|
|
||||||
return width;
|
|
||||||
case PlasmaCore.Types.Horizontal:
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return PlasmaCore.Units.gridUnit * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onCompactRepresentationChanged: {
|
|
||||||
if (compactRepresentation) {
|
|
||||||
compactRepresentation.parent = appletRoot;
|
|
||||||
compactRepresentation.anchors.fill = appletRoot;
|
|
||||||
compactRepresentation.visible = true;
|
|
||||||
}
|
|
||||||
appletRoot.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
|
||||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
|
||||||
<kcfgfile name=""/>
|
|
||||||
|
|
||||||
<group name="General">
|
|
||||||
<entry name="AppletOrder" type="String">
|
|
||||||
<label>encoded order of items</label>
|
|
||||||
</entry>
|
|
||||||
</group>
|
|
||||||
|
|
||||||
</kcfg>
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
import QtQuick.Controls 2.4 as QQC2
|
|
||||||
import QtGraphicalEffects 1.12
|
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
|
||||||
|
|
||||||
QQC2.Control {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
leftPadding: frameSvg.margins.left
|
|
||||||
topPadding: frameSvg.margins.top
|
|
||||||
rightPadding: frameSvg.margins.right
|
|
||||||
bottomPadding: frameSvg.margins.bottom
|
|
||||||
|
|
||||||
background: PlasmaCore.FrameSvgItem {
|
|
||||||
id: frameSvg
|
|
||||||
imagePath: "widgets/background"
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.12
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtQml.Models 2.12
|
|
||||||
|
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
|
|
||||||
DrawerBackground {
|
|
||||||
id: fullContainer
|
|
||||||
property Item applet
|
|
||||||
property ObjectModel fullRepresentationModel
|
|
||||||
property ListView fullRepresentationView
|
|
||||||
|
|
||||||
visible: shouldBeVisible
|
|
||||||
|
|
||||||
property bool shouldBeVisible: applet && (applet.status != PlasmaCore.Types.HiddenStatus && applet.status != PlasmaCore.Types.PassiveStatus)
|
|
||||||
|
|
||||||
height: parent.height
|
|
||||||
width: visible ? quickSettings.width : 0
|
|
||||||
Layout.minimumHeight: applet && applet.switchHeight
|
|
||||||
onShouldBeVisibleChanged: fullContainer.visible = fullContainer.shouldBeVisible
|
|
||||||
|
|
||||||
Component.onCompleted: visibleChanged();
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (visible) {
|
|
||||||
for (var i = 0; i < fullRepresentationModel.count; ++i) {
|
|
||||||
if (fullRepresentationModel.get(i) === this) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (applet && applet.pluginName == "org.kde.plasma.notifications") {
|
|
||||||
fullRepresentationModel.insert(0, this);
|
|
||||||
} else {
|
|
||||||
fullRepresentationModel.append(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
fullRepresentationView.forceLayout();
|
|
||||||
|
|
||||||
fullRepresentationView.currentIndex = ObjectModel.index;
|
|
||||||
fullRepresentationView.positionViewAtIndex(ObjectModel.index, ListView.Contain)
|
|
||||||
} else if (ObjectModel.index >= 0) {
|
|
||||||
fullRepresentationModel.remove(ObjectModel.index);
|
|
||||||
fullRepresentationView.forceLayout();
|
|
||||||
}
|
|
||||||
if (!shouldBeVisible) {
|
|
||||||
visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: fullContainer.applet
|
|
||||||
function onActivated() {
|
|
||||||
if (!visible) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fullRepresentationView.currentIndex = ObjectModel.index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: fullContainer.applet.fullRepresentationItem
|
|
||||||
function onParentChanged() {
|
|
||||||
fullContainer.applet.fullRepresentationItem.parent = fullContainer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.12
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.notificationmanager 1.0 as NotificationManager
|
|
||||||
|
|
||||||
FullContainer {
|
|
||||||
id: fullContainer
|
|
||||||
|
|
||||||
shouldBeVisible: applet && historyModel.count > 0
|
|
||||||
visible: shouldBeVisible
|
|
||||||
|
|
||||||
NotificationManager.Notifications {
|
|
||||||
id: historyModel
|
|
||||||
showExpired: true
|
|
||||||
showDismissed: true
|
|
||||||
expandUnread: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
var layout;
|
|
||||||
var root;
|
|
||||||
var plasmoid;
|
|
||||||
var lastSpacer;
|
|
||||||
|
|
||||||
|
|
||||||
function restore() {
|
|
||||||
var configString = String(plasmoid.configuration.AppletOrder)
|
|
||||||
|
|
||||||
//array, a cell for encoded item order
|
|
||||||
var itemsArray = configString.split(";");
|
|
||||||
|
|
||||||
//map applet id->order in panel
|
|
||||||
var idsOrder = new Object();
|
|
||||||
//map order in panel -> applet pointer
|
|
||||||
var appletsOrder = new Object();
|
|
||||||
|
|
||||||
for (var i = 0; i < itemsArray.length; i++) {
|
|
||||||
//property name: applet id
|
|
||||||
//property value: order
|
|
||||||
idsOrder[itemsArray[i]] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < plasmoid.applets.length; ++i) {
|
|
||||||
if (idsOrder[plasmoid.applets[i].id] !== undefined) {
|
|
||||||
appletsOrder[idsOrder[plasmoid.applets[i].id]] = plasmoid.applets[i];
|
|
||||||
//ones that weren't saved in AppletOrder go to the end
|
|
||||||
} else {
|
|
||||||
appletsOrder["unordered"+i] = plasmoid.applets[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//finally, restore the applets in the correct order
|
|
||||||
for (var i in appletsOrder) {
|
|
||||||
root.addApplet(appletsOrder[i], -1, -1)
|
|
||||||
}
|
|
||||||
//rewrite, so if in the orders there were now invalid ids or if some were missing creates a correct list instead
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function save() {
|
|
||||||
var ids = new Array();
|
|
||||||
for (var i = 0; i < layout.children.length; ++i) {
|
|
||||||
var child = layout.children[i];
|
|
||||||
|
|
||||||
if (child.applet) {
|
|
||||||
ids.push(child.applet.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plasmoid.configuration.AppletOrder = ids.join(';');
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeApplet (applet) {
|
|
||||||
for (var i = layout.children.length - 1; i >= 0; --i) {
|
|
||||||
var child = layout.children[i];
|
|
||||||
if (child.applet === applet) {
|
|
||||||
child.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//insert item2 before item1
|
|
||||||
function insertBefore(item1, item2) {
|
|
||||||
if (item1 === item2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var removed = new Array();
|
|
||||||
|
|
||||||
var child;
|
|
||||||
|
|
||||||
var i;
|
|
||||||
for (i = layout.children.length - 1; i >= 0; --i) {
|
|
||||||
child = layout.children[i];
|
|
||||||
removed.push(child);
|
|
||||||
child.parent = root;
|
|
||||||
|
|
||||||
if (child === item1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item2.parent = layout;
|
|
||||||
|
|
||||||
for (var j = removed.length - 1; j >= 0; --j) {
|
|
||||||
removed[j].parent = layout;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
//insert item2 after item1
|
|
||||||
function insertAfter(item1, item2) {
|
|
||||||
if (item1 === item2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var removed = new Array();
|
|
||||||
|
|
||||||
var child;
|
|
||||||
|
|
||||||
var i;
|
|
||||||
for (i = layout.children.length - 1; i >= 0; --i) {
|
|
||||||
child = layout.children[i];
|
|
||||||
//never ever insert after lastSpacer
|
|
||||||
if (child === lastSpacer && item1 === lastSpacer) {
|
|
||||||
removed.push(child);
|
|
||||||
child.parent = root;
|
|
||||||
break;
|
|
||||||
} else if (child === item1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
removed.push(child);
|
|
||||||
child.parent = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
item2.parent = layout;
|
|
||||||
|
|
||||||
for (var j = removed.length - 1; j >= 0; --j) {
|
|
||||||
removed[j].parent = layout;
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertAtIndex(item, position) {
|
|
||||||
if (position < 0 || position >= layout.children.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//never ever insert after lastSpacer
|
|
||||||
if (layout.children[position] === lastSpacer) {
|
|
||||||
--position;
|
|
||||||
}
|
|
||||||
|
|
||||||
var removedItems = new Array();
|
|
||||||
|
|
||||||
for (var i = position; i < layout.children.length; ++i) {
|
|
||||||
var child = layout.children[position];
|
|
||||||
child.parent = root;
|
|
||||||
removedItems.push(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.parent = layout;
|
|
||||||
for (var i in removedItems) {
|
|
||||||
removedItems[i].parent = layout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertAtCoordinates(item, x, y) {
|
|
||||||
if (root.isHorizontal) {
|
|
||||||
y = layout.height / 2;
|
|
||||||
} else {
|
|
||||||
x = layout.width / 2;
|
|
||||||
}
|
|
||||||
var child = layout.childAt(x, y);
|
|
||||||
|
|
||||||
if (!child || child === item) {
|
|
||||||
child = layout.children[0];
|
|
||||||
}
|
|
||||||
item.parent = root;
|
|
||||||
|
|
||||||
//PlasmaCore.Types.Vertical = 3
|
|
||||||
if ((plasmoid.formFactor === 3 && y < child.y + child.height/2) ||
|
|
||||||
(plasmoid.formFactor !== 3 && x < child.x + child.width/2)) {
|
|
||||||
return insertBefore(child, item);
|
|
||||||
} else {
|
|
||||||
return insertAfter(child, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,232 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2014 Marco Martin <notmart@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.15
|
|
||||||
import QtQuick.Controls 2.15
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|
||||||
|
|
||||||
NanoShell.FullScreenOverlay {
|
|
||||||
id: window
|
|
||||||
|
|
||||||
property int offset: 0 // slide progress
|
|
||||||
property int openThreshold: PlasmaCore.Units.gridUnit * 2
|
|
||||||
property bool userInteracting: false
|
|
||||||
property bool initiallyOpened: false // whether the panel is already open after a touch release (then don't restrict to collapsed height)
|
|
||||||
|
|
||||||
// height when quicksettings is fully open
|
|
||||||
required property int fullyOpenHeight
|
|
||||||
|
|
||||||
// flickable contentY
|
|
||||||
readonly property int openedContentY: wideScreen || offset > (collapsedHeight + openThreshold) ? -topEmptyAreaHeight : offsetToContentY(collapsedHeight)
|
|
||||||
readonly property int closedContentY: mainFlickable.contentHeight
|
|
||||||
|
|
||||||
readonly property bool wideScreen: width > height || width > PlasmaCore.Units.gridUnit * 45
|
|
||||||
readonly property int drawerWidth: wideScreen ? contentItem.implicitWidth : width
|
|
||||||
|
|
||||||
property int drawerX: 0
|
|
||||||
property alias fixedArea: mainScope
|
|
||||||
property alias flickable: mainFlickable
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
property alias contentItem: contentArea.contentItem
|
|
||||||
property int topPanelHeight
|
|
||||||
property int collapsedHeight
|
|
||||||
property real topEmptyAreaHeight
|
|
||||||
|
|
||||||
property bool appletsShown: false // whether notifications or media player applets are shown
|
|
||||||
|
|
||||||
signal closed
|
|
||||||
|
|
||||||
width: Screen.width
|
|
||||||
height: Screen.height
|
|
||||||
|
|
||||||
Component.onCompleted: plasmoid.nativeInterface.panel = window;
|
|
||||||
|
|
||||||
onVisibleChanged: if (!visible) {
|
|
||||||
closed()
|
|
||||||
}
|
|
||||||
onInitiallyOpenedChanged: {
|
|
||||||
if (initiallyOpened) mainFlickable.focus = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function offsetToContentY(num) { return -num + window.fullyOpenHeight; }
|
|
||||||
function contentYToOffset(num) { return offsetToContentY(num); }
|
|
||||||
|
|
||||||
// avoids binding loops
|
|
||||||
function updateOffset(delta) {
|
|
||||||
// only go to collapsed height for mousearea when not widescreen
|
|
||||||
let maximum = window.wideScreen ? window.fullyOpenHeight : collapsedHeight + openThreshold / 2;
|
|
||||||
offset = Math.max(0, Math.min(maximum, offset + delta));
|
|
||||||
if (!mainFlickable.moving && !mainFlickable.dragging && !mainFlickable.flicking) {
|
|
||||||
mainFlickable.contentY = offsetToContentY(window.offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MovementDirection {
|
|
||||||
None = 0,
|
|
||||||
Up,
|
|
||||||
Down
|
|
||||||
}
|
|
||||||
property int direction: SlidingContainer.MovementDirection.None
|
|
||||||
|
|
||||||
function cancelAnimations() {
|
|
||||||
closeAnim.stop();
|
|
||||||
openAnim.stop();
|
|
||||||
}
|
|
||||||
function open() {
|
|
||||||
cancelAnimations();
|
|
||||||
openAnim.restart();
|
|
||||||
initiallyOpened = true;
|
|
||||||
}
|
|
||||||
function close() {
|
|
||||||
cancelAnimations();
|
|
||||||
closeAnim.restart();
|
|
||||||
initiallyOpened = false;
|
|
||||||
}
|
|
||||||
function expand() {
|
|
||||||
cancelAnimations();
|
|
||||||
expandAnim.restart();
|
|
||||||
initiallyOpened = true;
|
|
||||||
}
|
|
||||||
function updateState() {
|
|
||||||
cancelAnimations();
|
|
||||||
if (window.offset <= 0) {
|
|
||||||
// close immediately, so that we don't have to wait PlasmaCore.Units.longDuration
|
|
||||||
window.visible = false;
|
|
||||||
close();
|
|
||||||
} else if (window.direction === SlidingContainer.MovementDirection.None) {
|
|
||||||
if (window.offset < openThreshold) {
|
|
||||||
close();
|
|
||||||
} else {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
} else if (offset > openThreshold && window.direction === SlidingContainer.MovementDirection.Down) {
|
|
||||||
open();
|
|
||||||
} else if (mainFlickable.contentY > openThreshold) {
|
|
||||||
close();
|
|
||||||
} else {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: updateStateTimer
|
|
||||||
interval: 0
|
|
||||||
onTriggered: updateState()
|
|
||||||
}
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
id: closeAnim
|
|
||||||
target: mainFlickable
|
|
||||||
properties: "contentY"
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
to: window.closedContentY
|
|
||||||
onFinished: {
|
|
||||||
window.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PropertyAnimation {
|
|
||||||
id: openAnim
|
|
||||||
target: mainFlickable
|
|
||||||
properties: "contentY"
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
to: window.openedContentY
|
|
||||||
}
|
|
||||||
PropertyAnimation {
|
|
||||||
id: expandAnim
|
|
||||||
target: mainFlickable
|
|
||||||
properties: "contentY"
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// fullscreen background
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: Qt.rgba(0, 0, 0, 0.75)
|
|
||||||
opacity: (appletsShown ? 0.85 : 0.6) * Math.max(0, Math.min(1, offset / window.collapsedHeight))
|
|
||||||
Behavior on opacity { // smooth opacity changes
|
|
||||||
NumberAnimation { duration: 70 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaCore.ColorScope {
|
|
||||||
id: mainScope
|
|
||||||
colorGroup: PlasmaCore.Theme.ViewColorGroup
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Flickable {
|
|
||||||
id: mainFlickable
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property real oldContentY
|
|
||||||
contentY: contentHeight
|
|
||||||
|
|
||||||
onContentYChanged: {
|
|
||||||
if (contentY === oldContentY) {
|
|
||||||
window.direction = SlidingContainer.MovementDirection.None;
|
|
||||||
} else {
|
|
||||||
window.direction = contentY > oldContentY ? SlidingContainer.MovementDirection.Up : SlidingContainer.MovementDirection.Down;
|
|
||||||
}
|
|
||||||
window.offset = contentYToOffset(contentY);
|
|
||||||
oldContentY = contentY;
|
|
||||||
|
|
||||||
// close panel immediately after panel is not shown, and the flickable is not being dragged
|
|
||||||
if (initiallyOpened && window.offset <= 0 && !mainFlickable.dragging && !closeAnim.running && !openAnim.running) {
|
|
||||||
window.updateState();
|
|
||||||
focus = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boundsMovement: Flickable.StopAtBounds
|
|
||||||
contentWidth: window.width
|
|
||||||
contentHeight: window.height
|
|
||||||
bottomMargin: window.height
|
|
||||||
onMovementStarted: {
|
|
||||||
window.cancelAnimations();
|
|
||||||
window.userInteracting = true;
|
|
||||||
}
|
|
||||||
onFlickStarted: window.userInteracting = true;
|
|
||||||
onMovementEnded: {
|
|
||||||
window.userInteracting = false;
|
|
||||||
window.updateState();
|
|
||||||
}
|
|
||||||
onFlickEnded: {
|
|
||||||
window.userInteracting = true;
|
|
||||||
window.updateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: dismissArea
|
|
||||||
z: 2
|
|
||||||
width: parent.width
|
|
||||||
height: mainFlickable.contentHeight
|
|
||||||
onClicked: window.close();
|
|
||||||
|
|
||||||
// actual sliding contents
|
|
||||||
PlasmaComponents.Control {
|
|
||||||
id: contentArea
|
|
||||||
z: 1
|
|
||||||
x: Math.max(0, Math.min(window.drawerX, window.width - window.drawerWidth))
|
|
||||||
width: Math.min(window.width, window.drawerWidth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
|
|
||||||
property alias icon: icon.source
|
|
||||||
property alias text: label.text
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: icon
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
}
|
|
||||||
PlasmaComponents.Label {
|
|
||||||
id: label
|
|
||||||
visible: text.length > 0
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
|
||||||
font.pixelSize: parent.height / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.1
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
|
|
||||||
import "providers"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
required property QtObject provider
|
|
||||||
|
|
||||||
width: strengthIcon.height
|
|
||||||
Layout.minimumWidth: strengthIcon.height
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: strengthIcon
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: height
|
|
||||||
height: parent.height
|
|
||||||
|
|
||||||
source: provider.icon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQml.Models 2.12
|
import QtQml.Models 2.12
|
||||||
import QtGraphicalEffects 1.12
|
|
||||||
|
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
|
@ -16,21 +15,19 @@ import org.kde.plasma.plasmoid 2.0
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
import org.kde.plasma.workspace.components 2.0 as PlasmaWorkspace
|
|
||||||
import org.kde.taskmanager 0.1 as TaskManager
|
|
||||||
|
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
||||||
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
import org.kde.plasma.private.mobilehomescreencomponents 0.1 as HomeScreenComponents
|
|
||||||
|
|
||||||
import "LayoutManager.js" as LayoutManager
|
|
||||||
|
|
||||||
import "quicksettings"
|
|
||||||
import "indicators" as Indicators
|
|
||||||
import "indicators/providers" as IndicatorProviders
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property bool showingApp: !MobileShell.HomeScreenControls.homeScreenVisible
|
||||||
|
readonly property color backgroundColor: NanoShell.StartupFeedback.visible
|
||||||
|
? NanoShell.StartupFeedback.backgroundColor
|
||||||
|
: topPanel.colorScopeColor
|
||||||
|
|
||||||
|
Plasmoid.backgroundHints: showingApp ? PlasmaCore.Types.StandardBackground : PlasmaCore.Types.NoBackground
|
||||||
|
|
||||||
width: 480
|
width: 480
|
||||||
height: PlasmaCore.Units.gridUnit
|
height: PlasmaCore.Units.gridUnit
|
||||||
|
|
||||||
|
|
@ -44,330 +41,46 @@ Item {
|
||||||
Binding {
|
Binding {
|
||||||
target: MobileShell.TopPanelControls
|
target: MobileShell.TopPanelControls
|
||||||
property: "inSwipe"
|
property: "inSwipe"
|
||||||
value: slidingPanel.userInteracting
|
value: drawer.dragging
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: MobileShell.TopPanelControls
|
target: MobileShell.TopPanelControls
|
||||||
|
|
||||||
function onStartSwipe() {
|
function onStartSwipe() {
|
||||||
swipeMouseArea.startSwipe(0);
|
swipeArea.startSwipe();
|
||||||
}
|
}
|
||||||
function onEndSwipe() {
|
function onEndSwipe() {
|
||||||
swipeMouseArea.endSwipe();
|
swipeArea.endSwipe();
|
||||||
}
|
}
|
||||||
function onRequestRelativeScroll(offsetY) {
|
function onRequestRelativeScroll(offsetY) {
|
||||||
swipeMouseArea.updateOffset(offsetY);
|
swipeArea.updateOffset(offsetY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//END API implementation
|
//END API implementation
|
||||||
|
|
||||||
Plasmoid.backgroundHints: showingApp ? PlasmaCore.Types.StandardBackground : PlasmaCore.Types.NoBackground
|
|
||||||
|
|
||||||
property Item toolBox
|
|
||||||
property int buttonHeight: width/4
|
|
||||||
property bool reorderingApps: false
|
|
||||||
property var layoutManager: LayoutManager
|
|
||||||
|
|
||||||
readonly property color backgroundColor: NanoShell.StartupFeedback.visible ? NanoShell.StartupFeedback.backgroundColor : topPanel.colorScopeColor
|
|
||||||
readonly property bool showingApp: !MobileShell.HomeScreenControls.homeScreenVisible
|
|
||||||
|
|
||||||
readonly property bool hasTasks: tasksModel.count > 0
|
|
||||||
|
|
||||||
Containment.onAppletAdded: {
|
|
||||||
addApplet(applet, x, y);
|
|
||||||
LayoutManager.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addApplet(applet, x, y) {
|
|
||||||
var compactContainer = compactContainerComponent.createObject(topPanel.applets)
|
|
||||||
print("Applet added: " + applet + " " + applet.title)
|
|
||||||
|
|
||||||
applet.parent = compactContainer;
|
|
||||||
compactContainer.applet = applet;
|
|
||||||
applet.anchors.fill = compactContainer;
|
|
||||||
applet.visible = true;
|
|
||||||
|
|
||||||
//FIXME: make a way to instantiate fullRepresentationItem without the open/close dance
|
|
||||||
applet.expanded = true
|
|
||||||
applet.expanded = false
|
|
||||||
|
|
||||||
var fullContainer = null;
|
|
||||||
if (applet.pluginName == "org.kde.plasma.notifications") {
|
|
||||||
fullContainer = fullNotificationsContainerComponent.createObject(fullRepresentationView.contentItem, {"fullRepresentationModel": fullRepresentationModel, "fullRepresentationView": fullRepresentationView});
|
|
||||||
} else {
|
|
||||||
fullContainer = fullContainerComponent.createObject(fullRepresentationView.contentItem, {"fullRepresentationModel": fullRepresentationModel, "fullRepresentationView": fullRepresentationView});
|
|
||||||
}
|
|
||||||
|
|
||||||
// applet.fullRepresentationItem.parent = fullContainer;
|
|
||||||
fullContainer.applet = applet;
|
|
||||||
fullContainer.contentItem = applet.fullRepresentationItem;
|
|
||||||
//applet.fullRepresentationItem.anchors.fill = fullContainer;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
LayoutManager.plasmoid = plasmoid;
|
// we want to bind global shortcuts here
|
||||||
LayoutManager.root = root;
|
MobileShell.VolumeProvider.bindShortcuts = true;
|
||||||
LayoutManager.layout = appletsLayout;
|
|
||||||
LayoutManager.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskManager.TasksModel {
|
|
||||||
id: tasksModel
|
|
||||||
sortMode: TaskManager.TasksModel.SortVirtualDesktop
|
|
||||||
groupMode: TaskManager.TasksModel.GroupDisabled
|
|
||||||
|
|
||||||
screenGeometry: plasmoid.screenGeometry
|
|
||||||
//FIXME: workaround
|
|
||||||
Component.onCompleted: tasksModel.countChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaCore.DataSource {
|
|
||||||
id: statusNotifierSource
|
|
||||||
engine: "statusnotifieritem"
|
|
||||||
interval: 0
|
|
||||||
onSourceAdded: {
|
|
||||||
connectSource(source)
|
|
||||||
}
|
|
||||||
Component.onCompleted: {
|
|
||||||
connectedSources = sources
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: appletsLayout
|
|
||||||
Layout.minimumHeight: Math.max(root.height, Math.round(Layout.preferredHeight / root.height) * root.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: REMOVE?
|
|
||||||
Component {
|
|
||||||
id: compactContainerComponent
|
|
||||||
Item {
|
|
||||||
property Item applet
|
|
||||||
visible: applet && (applet.status != PlasmaCore.Types.HiddenStatus && applet.status != PlasmaCore.Types.PassiveStatus)
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.minimumWidth: applet && applet.compactRepresentationItem ? Math.max(applet.compactRepresentationItem.Layout.minimumWidth, topPanel.applets.height) : topPanel.applets.height
|
|
||||||
Layout.maximumWidth: Layout.minimumWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: fullContainerComponent
|
|
||||||
FullContainer {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: fullNotificationsContainerComponent
|
|
||||||
FullNotificationsContainer {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// indicator providers
|
|
||||||
IndicatorProviders.BatteryProvider {
|
|
||||||
id: batteryProvider
|
|
||||||
|
|
||||||
readonly property var setting: HomeScreenComponents.QuickSetting {
|
|
||||||
text: i18n("Battery")
|
|
||||||
icon: "battery-full" + (batteryProvider.pluggedIn ? "-charging" : "")
|
|
||||||
enabled: false
|
|
||||||
settingsCommand: "plasma-open-settings kcm_mobile_power"
|
|
||||||
}
|
|
||||||
Component.onCompleted: quickSettings.quickSettingsModel.include(setting)
|
|
||||||
}
|
|
||||||
IndicatorProviders.BluetoothProvider {
|
|
||||||
id: bluetoothProvider
|
|
||||||
}
|
|
||||||
property alias signalStrengthProvider: signalStrengthProviderLoader.item
|
|
||||||
Loader {
|
|
||||||
id: signalStrengthProviderLoader
|
|
||||||
source: Qt.resolvedUrl("indicators/providers/SignalStrengthProvider.qml")
|
|
||||||
}
|
|
||||||
IndicatorProviders.VolumeProvider {
|
|
||||||
id: volumeProvider
|
|
||||||
|
|
||||||
readonly property var setting: HomeScreenComponents.QuickSetting {
|
|
||||||
text: i18n("Sound")
|
|
||||||
icon: "audio-speakers-symbolic"
|
|
||||||
enabled: false
|
|
||||||
settingsCommand: "plasma-open-settings kcm_pulseaudio"
|
|
||||||
function toggle() {
|
|
||||||
volumeProvider.showVolumeOverlay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component.onCompleted: quickSettings.quickSettingsModel.include(setting)
|
|
||||||
}
|
|
||||||
IndicatorProviders.WifiProvider {
|
|
||||||
id: wifiProvider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// top panel component
|
// top panel component
|
||||||
IndicatorsRow {
|
MobileShell.StatusBar {
|
||||||
id: topPanel
|
id: topPanel
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
z: 1
|
showDropShadow: !root.showingApp
|
||||||
colorGroup: showingApp ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
|
colorGroup: root.showingApp ? PlasmaCore.Theme.HeaderColorGroup : PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
backgroundColor: !showingApp ? "transparent" : root.backgroundColor
|
backgroundColor: !root.showingApp ? "transparent" : root.backgroundColor
|
||||||
showDropShadow: !showingApp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initial swipe down gesture
|
MobileShell.ActionDrawerOpenSurface {
|
||||||
MouseArea {
|
id: swipeArea
|
||||||
id: swipeMouseArea
|
actionDrawer: drawer
|
||||||
z: 99
|
|
||||||
property int oldMouseY: 0
|
|
||||||
|
|
||||||
function startSwipe(mouseX) {
|
|
||||||
slidingPanel.cancelAnimations();
|
|
||||||
slidingPanel.drawerX = Math.min(Math.max(0, mouseX - slidingPanel.drawerWidth/2), slidingPanel.width - slidingPanel.contentItem.width)
|
|
||||||
slidingPanel.userInteracting = true;
|
|
||||||
slidingPanel.flickable.contentY = slidingPanel.closedContentY;
|
|
||||||
slidingPanel.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function endSwipe() {
|
|
||||||
slidingPanel.userInteracting = false;
|
|
||||||
slidingPanel.updateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateOffset(offsetY) {
|
|
||||||
slidingPanel.updateOffset(offsetY);
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onPressed: {
|
|
||||||
oldMouseY = mouse.y;
|
|
||||||
startSwipe(mouse.x);
|
|
||||||
}
|
|
||||||
onReleased: endSwipe()
|
|
||||||
onCanceled: endSwipe()
|
|
||||||
onPositionChanged: {
|
|
||||||
updateOffset(mouse.y - oldMouseY);
|
|
||||||
oldMouseY = mouse.y;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sliding component
|
MobileShell.ActionDrawer {
|
||||||
SlidingContainer {
|
id: drawer
|
||||||
id: slidingPanel
|
|
||||||
width: plasmoid.availableScreenRect.width
|
|
||||||
height: plasmoid.availableScreenRect.height
|
|
||||||
topPanelHeight: topPanel.height
|
|
||||||
topEmptyAreaHeight: quickSettings.topEmptyAreaHeight
|
|
||||||
collapsedHeight: quickSettings.collapsedHeight
|
|
||||||
fullyOpenHeight: quickSettings.expandedHeight
|
|
||||||
|
|
||||||
appletsShown: fullRepresentationView.count > 0
|
|
||||||
|
|
||||||
offset: quickSettings.height
|
|
||||||
|
|
||||||
onClosed: quickSettings.closed()
|
|
||||||
|
|
||||||
contentItem: MouseArea {
|
|
||||||
// mousearea captures touch presses so that the flickable picks them up for swiping
|
|
||||||
implicitWidth: slidingPanel.wideScreen ? panelContents.implicitWidth : slidingPanel.width
|
|
||||||
implicitHeight: Math.min(slidingPanel.height, quickSettings.implicitHeight)
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
id: panelContents
|
|
||||||
width: slidingPanel.wideScreen ? Math.min(parent.width, implicitWidth) : parent.width
|
|
||||||
|
|
||||||
columns: slidingPanel.wideScreen ? 2 : 1
|
|
||||||
rows: slidingPanel.wideScreen ? 1 : 2
|
|
||||||
|
|
||||||
QuickSettingsPanel {
|
|
||||||
id: quickSettings
|
|
||||||
|
|
||||||
property int trueHeight: height + Math.round(Kirigami.Units.gridUnit * 1.5) // add height of bottom bar
|
|
||||||
|
|
||||||
z: 4
|
|
||||||
Layout.alignment: Qt.AlignTop
|
|
||||||
Layout.preferredWidth: slidingPanel.wideScreen ? Math.min(slidingPanel.width/2, PlasmaCore.Units.gridUnit * 25) : panelContents.width
|
|
||||||
|
|
||||||
parentSlidingPanel: slidingPanel
|
|
||||||
onExpandRequested: slidingPanel.expand()
|
|
||||||
onCloseRequested: slidingPanel.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// notifications and media player
|
|
||||||
ListView {
|
|
||||||
id: fullRepresentationView
|
|
||||||
implicitHeight: PlasmaCore.Units.gridUnit * 20
|
|
||||||
Layout.preferredWidth: slidingPanel.wideScreen ? Math.min(slidingPanel.width/2, quickSettings.width*fullRepresentationModel.count) : panelContents.width
|
|
||||||
Layout.preferredHeight: slidingPanel.wideScreen
|
|
||||||
? Math.min(PlasmaCore.Units.gridUnit * 20, Math.max(PlasmaCore.Units.gridUnit * 15, quickSettings.implicitHeight))
|
|
||||||
: Math.min(plasmoid.screenGeometry.height - quickSettings.implicitHeight - bottomBar.height + slidingPanel.topEmptyAreaHeight, implicitHeight)
|
|
||||||
|
|
||||||
z: 1
|
|
||||||
interactive: true//count > 0 && width < contentWidth
|
|
||||||
|
|
||||||
clip: slidingPanel.wideScreen
|
|
||||||
y: slidingPanel.wideScreen ? 0 : quickSettings.trueHeight
|
|
||||||
opacity: {
|
|
||||||
if (slidingPanel.wideScreen) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return fullRepresentationModel.count > 0 && slidingPanel.offset / slidingPanel.collapsedHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//preferredHighlightBegin: slidingPanel.drawerX
|
|
||||||
|
|
||||||
cacheBuffer: width * 100
|
|
||||||
highlightFollowsCurrentItem: true
|
|
||||||
highlightRangeMode: ListView.ApplyRange
|
|
||||||
highlightMoveDuration: PlasmaCore.Units.longDuration
|
|
||||||
snapMode: slidingPanel.wideScreen ? ListView.NoSnap : ListView.SnapOneItem
|
|
||||||
model: ObjectModel {
|
|
||||||
id: fullRepresentationModel
|
|
||||||
}
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
parent: fullRepresentationView.contentItem
|
|
||||||
anchors.fill: parent
|
|
||||||
z: -1
|
|
||||||
onClicked: slidingPanel.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DrawerBackground {
|
|
||||||
id: bottomBar
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
parent: slidingPanel.fixedArea
|
|
||||||
opacity: fullRepresentationView.opacity
|
|
||||||
visible: !slidingPanel.wideScreen && fullRepresentationModel.count > 1
|
|
||||||
z: 100
|
|
||||||
contentItem: RowLayout {
|
|
||||||
PlasmaComponents.TabBar {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
position: PlasmaComponents.TabBar.Footer
|
|
||||||
Text {
|
|
||||||
text:fullRepresentationModel.count
|
|
||||||
}
|
|
||||||
Repeater {
|
|
||||||
model: fullRepresentationView.count
|
|
||||||
delegate: PlasmaComponents.TabButton {
|
|
||||||
implicitHeight: parent.height
|
|
||||||
text: fullRepresentationModel.get(index).applet.title
|
|
||||||
checked: fullRepresentationView.currentIndex === index
|
|
||||||
|
|
||||||
onClicked: fullRepresentationView.currentIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PlasmaComponents.ToolButton {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
icon.name: "paint-none"
|
|
||||||
onClicked: slidingPanel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.1
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: delegateRoot
|
|
||||||
spacing: PlasmaCore.Units.smallSpacing
|
|
||||||
|
|
||||||
signal closeRequested
|
|
||||||
signal panelClosed
|
|
||||||
|
|
||||||
// Model interface
|
|
||||||
required property string text
|
|
||||||
required property string icon
|
|
||||||
required property bool enabled
|
|
||||||
required property string settingsCommand
|
|
||||||
required property var toggleFunction
|
|
||||||
property alias labelOpacity: label.opacity
|
|
||||||
|
|
||||||
required property real boundingWidth
|
|
||||||
|
|
||||||
property color disabledButtonColor: PlasmaCore.Theme.backgroundColor
|
|
||||||
property color disabledPressedButtonColor: Qt.darker(disabledButtonColor, 1.1)
|
|
||||||
property color enabledButtonColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {"alpha": 0.4*255})
|
|
||||||
property color enabledPressedButtonColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {"alpha": 0.6*255});
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: iconButton
|
|
||||||
Layout.preferredWidth: PlasmaCore.Units.iconSizes.large + PlasmaCore.Units.smallSpacing
|
|
||||||
Layout.minimumHeight: width
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
radius: PlasmaCore.Units.smallSpacing
|
|
||||||
border.color: delegateRoot.enabled ?
|
|
||||||
Qt.darker(Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.highlightColor, {}), 1.25) :
|
|
||||||
Kirigami.ColorUtils.adjustColor(PlasmaCore.ColorScope.textColor, {"alpha": 0.2*255})
|
|
||||||
color: {
|
|
||||||
if (delegateRoot.enabled) {
|
|
||||||
return iconMouseArea.pressed ? enabledPressedButtonColor : enabledButtonColor
|
|
||||||
} else {
|
|
||||||
return iconMouseArea.pressed ? disabledPressedButtonColor : disabledButtonColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: icon
|
|
||||||
anchors.centerIn: parent
|
|
||||||
implicitWidth: Math.round(parent.width * 0.6)
|
|
||||||
implicitHeight: width
|
|
||||||
source: delegateRoot.icon
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: iconMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
if (delegateRoot.toggle) {
|
|
||||||
delegateRoot.toggle();
|
|
||||||
} else if (delegateRoot.toggleFunction) {
|
|
||||||
delegateRoot.toggleFunction();
|
|
||||||
} else if (delegateRoot.settingsCommand) {
|
|
||||||
NanoShell.StartupFeedback.open(
|
|
||||||
delegateRoot.icon,
|
|
||||||
delegateRoot.text,
|
|
||||||
icon.Kirigami.ScenePosition.x + icon.width/2,
|
|
||||||
icon.Kirigami.ScenePosition.y + icon.height/2,
|
|
||||||
Math.min(icon.width, icon.height))
|
|
||||||
plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand);
|
|
||||||
root.closeRequested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressAndHold: {
|
|
||||||
if (delegateRoot.settingsCommand) {
|
|
||||||
NanoShell.StartupFeedback.open(
|
|
||||||
delegateRoot.icon,
|
|
||||||
delegateRoot.text,
|
|
||||||
icon.Kirigami.ScenePosition.x + icon.width/2,
|
|
||||||
icon.Kirigami.ScenePosition.y + icon.height/2,
|
|
||||||
Math.min(icon.width, icon.height))
|
|
||||||
closeRequested();
|
|
||||||
plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand);
|
|
||||||
} else if (delegateRoot.toggleFunction) {
|
|
||||||
delegateRoot.toggleFunction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
|
||||||
id: label
|
|
||||||
|
|
||||||
Layout.maximumWidth: delegateRoot.boundingWidth - arrow.width - PlasmaCore.Units.smallSpacing * 2
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
|
|
||||||
text: delegateRoot.text
|
|
||||||
bottomPadding: PlasmaCore.Units.smallSpacing * 2
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.pixelSize: PlasmaCore.Theme.defaultFont.pixelSize * 0.8
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
PlasmaCore.SvgItem {
|
|
||||||
id: arrow
|
|
||||||
anchors {
|
|
||||||
left: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
verticalCenterOffset: -PlasmaCore.Units.smallSpacing
|
|
||||||
}
|
|
||||||
visible: delegateRoot.settingsCommand
|
|
||||||
width: PlasmaCore.Units.iconSizes.small/2
|
|
||||||
height: width
|
|
||||||
elementId: "down-arrow"
|
|
||||||
svg: PlasmaCore.Svg {
|
|
||||||
imagePath: "widgets/arrows"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: labelMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
if (delegateRoot.settingsCommand) {
|
|
||||||
NanoShell.StartupFeedback.open(
|
|
||||||
delegateRoot.icon,
|
|
||||||
delegateRoot.text,
|
|
||||||
icon.Kirigami.ScenePosition.x + icon.width/2,
|
|
||||||
icon.Kirigami.ScenePosition.y + icon.height/2,
|
|
||||||
Math.min(icon.width, icon.height))
|
|
||||||
plasmoid.nativeInterface.executeCommand(delegateRoot.settingsCommand);
|
|
||||||
closeRequested();
|
|
||||||
} else if (delegateRoot.toggleFunction) {
|
|
||||||
delegateRoot.toggleFunction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.14
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import QtGraphicalEffects 1.12
|
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
|
||||||
import org.kde.bluezqt 1.0 as BluezQt
|
|
||||||
import org.kde.colorcorrect 0.1 as CC
|
|
||||||
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
|
|
||||||
|
|
||||||
import org.kde.plasma.components 3.0 as PC3
|
|
||||||
|
|
||||||
import "../"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
implicitWidth: column.implicitWidth + PlasmaCore.Units.smallSpacing * 6
|
|
||||||
implicitHeight: expandedHeight
|
|
||||||
|
|
||||||
signal expandRequested
|
|
||||||
signal closeRequested
|
|
||||||
signal closed
|
|
||||||
|
|
||||||
property bool expandedMode: parentSlidingPanel.wideScreen
|
|
||||||
|
|
||||||
readonly property real expandedRatio: expandedMode
|
|
||||||
? 1
|
|
||||||
: Math.max(0, Math.min(1, (parentSlidingPanel.offset - collapsedHeight) /(expandedHeight-collapsedHeight)))
|
|
||||||
|
|
||||||
readonly property real topEmptyAreaHeight: parentSlidingPanel.userInteracting
|
|
||||||
? (root.height - collapsedHeight) * (1 - expandedRatio)
|
|
||||||
: (expandedMode ? 0 : root.height - collapsedHeight)
|
|
||||||
|
|
||||||
|
|
||||||
readonly property real collapsedHeight: column.Layout.minimumHeight + background.margins.top + background.fixedMargins.bottom
|
|
||||||
|
|
||||||
readonly property real expandedHeight: column.Layout.maximumHeight + background.margins.top + background.fixedMargins.bottom
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root.parentSlidingPanel
|
|
||||||
function onUserInteractingChanged() {
|
|
||||||
if (!parentSlidingPanel.userInteracting) {
|
|
||||||
if (root.expandedRatio > 0.7) {
|
|
||||||
root.expandedMode = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property NanoShell.FullScreenOverlay parentSlidingPanel
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: root.Window.window
|
|
||||||
function onVisibilityChanged() {
|
|
||||||
root.expandedMode = parentSlidingPanel.wideScreen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signal plasmoidTriggered(var applet, var id)
|
|
||||||
Layout.minimumHeight: flow.implicitHeight + PlasmaCore.Units.largeSpacing*2
|
|
||||||
|
|
||||||
onClosed: quickSettingsModel.panelClosed()
|
|
||||||
|
|
||||||
readonly property SettingsModel quickSettingsModel: SettingsModel {}
|
|
||||||
|
|
||||||
PlasmaCore.FrameSvgItem {
|
|
||||||
id: background
|
|
||||||
implicitHeight: root.expandedHeight
|
|
||||||
enabledBorders: parentSlidingPanel.wideScreen ? PlasmaCore.FrameSvg.AllBorders : PlasmaCore.FrameSvg.BottomBorder
|
|
||||||
anchors.fill: parent
|
|
||||||
imagePath: "widgets/background"
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: column
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
leftMargin: parent.fixedMargins.left
|
|
||||||
rightMargin: parent.fixedMargins.right
|
|
||||||
bottomMargin: parent.fixedMargins.bottom * (parentSlidingPanel.wideScreen ? 1 : 0.5) // HACK: fix the bottom arrow not being centered, bottom margins aren't properly calculated it seems
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
bottom: parent.bottom
|
|
||||||
}
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
height: Layout.minimumHeight * (1 - root.expandedRatio) + (Layout.maximumHeight * root.expandedRatio)
|
|
||||||
|
|
||||||
readonly property real cellSizeHint: PlasmaCore.Units.iconSizes.large + PlasmaCore.Units.smallSpacing * 6
|
|
||||||
readonly property real columnWidth: Math.floor(width / Math.floor(width / cellSizeHint))
|
|
||||||
|
|
||||||
// top indicators (clock, widgets, etc.)
|
|
||||||
IndicatorsRow {
|
|
||||||
id: indicatorsRow
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
Layout.leftMargin: -Kirigami.Units.largeSpacing
|
|
||||||
Layout.rightMargin: -Kirigami.Units.largeSpacing
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: parentSlidingPanel.topPanelHeight + PlasmaCore.Units.smallSpacing * 2
|
|
||||||
|
|
||||||
showSecondRow: true
|
|
||||||
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
|
||||||
backgroundColor: "transparent"
|
|
||||||
showDropShadow: false
|
|
||||||
}
|
|
||||||
|
|
||||||
// quicksettings list
|
|
||||||
ColumnLayout {
|
|
||||||
clip: expandedRatio > 0 && expandedRatio < 1 // only clip when necessary to improve performance
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.minimumHeight: flow.Layout.minimumHeight
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
Layout.topMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
Flow {
|
|
||||||
id: flow
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.minimumHeight: cellSizeHint
|
|
||||||
Layout.preferredHeight: implicitHeight
|
|
||||||
Layout.maximumHeight: (flow.cellSizeHint * Math.ceil((flow.children.length - 1) / flow.columns))
|
|
||||||
|
|
||||||
readonly property real cellSizeHint: PlasmaCore.Units.iconSizes.large + PlasmaCore.Units.smallSpacing * 6
|
|
||||||
readonly property real columns: Math.floor(width / cellSizeHint)
|
|
||||||
readonly property real columnWidth: Math.floor(width / columns)
|
|
||||||
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: quickSettingsModel
|
|
||||||
delegate: Delegate {
|
|
||||||
id: delegateItem
|
|
||||||
required property var modelData
|
|
||||||
width: Math.max(implicitWidth + PlasmaCore.Units.smallSpacing * 2, boundingWidth)
|
|
||||||
boundingWidth: root.expandedRatio < 0.4
|
|
||||||
? flow.width / (flow.columns + 1)
|
|
||||||
: (flow.width / (flow.columns + 1)) * (1 - root.expandedRatio) + (flow.width / flow.columns) * root.expandedRatio
|
|
||||||
|
|
||||||
labelOpacity: y > 0 ? 1 : root.expandedRatio
|
|
||||||
opacity: y <= 0 ? 1 : root.expandedRatio
|
|
||||||
text: modelData.text
|
|
||||||
icon: modelData.icon
|
|
||||||
enabled: modelData.enabled
|
|
||||||
settingsCommand: modelData.settingsCommand
|
|
||||||
toggleFunction: modelData.toggle
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: delegateItem
|
|
||||||
onCloseRequested: root.closeRequested();
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
onClosed: delegateItem.panelClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
move: Transition {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: PlasmaCore.Units.shortDuration
|
|
||||||
easing.type: Easing.Linear
|
|
||||||
properties: "x,y"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BrightnessItem {
|
|
||||||
id: brightnessSlider
|
|
||||||
Layout.topMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
Layout.bottomMargin: PlasmaCore.Units.smallSpacing
|
|
||||||
Layout.leftMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
Layout.rightMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
opacity: root.expandedRatio
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bottom "handle bar"
|
|
||||||
ColumnLayout {
|
|
||||||
id: bottomBar
|
|
||||||
spacing: 0
|
|
||||||
visible: !parentSlidingPanel.wideScreen
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
implicitHeight: visible ? Math.round(PlasmaCore.Units.gridUnit * 1.3) : 0
|
|
||||||
|
|
||||||
Kirigami.Separator {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: PlasmaCore.Theme.disabledTextColor
|
|
||||||
opacity: 0.3
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
color: PlasmaCore.Theme.disabledTextColor
|
|
||||||
source: expandedRatio >= 0.5 ? "go-up-symbolic" : "go-down-symbolic"
|
|
||||||
implicitWidth: PlasmaCore.Units.gridUnit
|
|
||||||
implicitHeight: width
|
|
||||||
Layout.alignment: Qt.AlignCenter
|
|
||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
onTapped: {
|
|
||||||
if (root.expandedMode) {
|
|
||||||
root.closeRequested();
|
|
||||||
} else {
|
|
||||||
root.expandRequested();
|
|
||||||
root.expandedMode = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,238 +7,9 @@
|
||||||
|
|
||||||
#include "phonepanel.h"
|
#include "phonepanel.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <qplatformdefs.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <KConfigGroup>
|
|
||||||
#include <KIO/ApplicationLauncherJob>
|
|
||||||
#include <KLocalizedString>
|
|
||||||
#include <KNotification>
|
|
||||||
|
|
||||||
#include <QDBusPendingReply>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QScreen>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QtConcurrent/QtConcurrent>
|
|
||||||
|
|
||||||
#define FORMAT24H "HH:mm:ss"
|
|
||||||
|
|
||||||
constexpr int SCREENSHOT_DELAY = 200;
|
|
||||||
|
|
||||||
/* -- Static Helpers --------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
static QImage allocateImage(const QVariantMap &metadata)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
const uint width = metadata.value(QStringLiteral("width")).toUInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint height = metadata.value(QStringLiteral("height")).toUInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint format = metadata.value(QStringLiteral("format")).toUInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QImage(width, height, QImage::Format(format));
|
|
||||||
}
|
|
||||||
|
|
||||||
static QImage readImage(int fileDescriptor, const QVariantMap &metadata)
|
|
||||||
{
|
|
||||||
QFile file;
|
|
||||||
if (!file.open(fileDescriptor, QFileDevice::ReadOnly, QFileDevice::AutoCloseHandle)) {
|
|
||||||
close(fileDescriptor);
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage result = allocateImage(metadata);
|
|
||||||
if (result.isNull()) {
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream stream(&file);
|
|
||||||
stream.readRawData(reinterpret_cast<char *>(result.bits()), result.sizeInBytes());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhonePanel::PhonePanel(QObject *parent, const QVariantList &args)
|
PhonePanel::PhonePanel(QObject *parent, const QVariantList &args)
|
||||||
: Plasma::Containment(parent, args)
|
: Plasma::Containment(parent, args)
|
||||||
{
|
{
|
||||||
// setHasConfigurationInterface(true);
|
|
||||||
m_kscreenInterface = new org::kde::KScreen(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/kscreen"), QDBusConnection::sessionBus(), this);
|
|
||||||
m_screenshotInterface = new OrgKdeKWinScreenShot2Interface(QStringLiteral("org.kde.KWin.ScreenShot2"),
|
|
||||||
QStringLiteral("/org/kde/KWin/ScreenShot2"),
|
|
||||||
QDBusConnection::sessionBus(),
|
|
||||||
this);
|
|
||||||
|
|
||||||
m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
|
|
||||||
m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig);
|
|
||||||
|
|
||||||
// watch for changes to locale config, to update 12/24 hour time
|
|
||||||
connect(m_localeConfigWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) -> void {
|
|
||||||
if (group.name() == "Locale") {
|
|
||||||
// we have to reparse for new changes (from system settings)
|
|
||||||
m_localeConfig->reparseConfiguration();
|
|
||||||
Q_EMIT isSystem24HourFormatChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PhonePanel::~PhonePanel() = default;
|
PhonePanel::~PhonePanel() = default;
|
||||||
|
|
||||||
void PhonePanel::executeCommand(const QString &command)
|
|
||||||
{
|
|
||||||
qWarning() << "Executing" << command;
|
|
||||||
const QStringList commandAndArguments = QProcess::splitCommand(command);
|
|
||||||
QProcess::startDetached(commandAndArguments.front(), commandAndArguments.mid(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhonePanel::toggleTorch()
|
|
||||||
{
|
|
||||||
// FIXME this is hardcoded to the PinePhone for now
|
|
||||||
static auto FLASH_SYSFS_PATH = "/sys/devices/platform/led-controller/leds/white:flash/brightness";
|
|
||||||
int fd = open(FLASH_SYSFS_PATH, O_WRONLY);
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
qWarning() << "Unable to open file %s" << FLASH_SYSFS_PATH;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
write(fd, m_running ? "0" : "1", 1);
|
|
||||||
close(fd);
|
|
||||||
m_running = !m_running;
|
|
||||||
Q_EMIT torchChanged(m_running);
|
|
||||||
}
|
|
||||||
bool PhonePanel::torchEnabled() const
|
|
||||||
{
|
|
||||||
return m_running;
|
|
||||||
}
|
|
||||||
bool PhonePanel::autoRotate()
|
|
||||||
{
|
|
||||||
QDBusPendingReply<bool> reply = m_kscreenInterface->getAutoRotate();
|
|
||||||
reply.waitForFinished();
|
|
||||||
if (reply.isError()) {
|
|
||||||
qWarning() << "Getting auto rotate failed:" << reply.error().name() << reply.error().message();
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return reply.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhonePanel::setAutoRotate(bool value)
|
|
||||||
{
|
|
||||||
QDBusPendingReply<> reply = m_kscreenInterface->setAutoRotate(value);
|
|
||||||
reply.waitForFinished();
|
|
||||||
if (reply.isError()) {
|
|
||||||
qWarning() << "Setting auto rotate failed:" << reply.error().name() << reply.error().message();
|
|
||||||
} else {
|
|
||||||
emit autoRotateChanged(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhonePanel::handleMetaDataReceived(const QVariantMap &metadata, int fd)
|
|
||||||
{
|
|
||||||
const QString type = metadata.value(QStringLiteral("type")).toString();
|
|
||||||
if (type != QLatin1String("raw")) {
|
|
||||||
qWarning() << "Unsupported metadata type:" << type;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto watcher = new QFutureWatcher<QImage>(this);
|
|
||||||
connect(watcher, &QFutureWatcher<QImage>::finished, this, [watcher]() {
|
|
||||||
watcher->deleteLater();
|
|
||||||
|
|
||||||
QString filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
|
||||||
if (filePath.isEmpty()) {
|
|
||||||
qWarning() << "Couldn't find a writable location for the screenshot!";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QDir picturesDir(filePath);
|
|
||||||
if (!picturesDir.mkpath(QStringLiteral("Screenshots"))) {
|
|
||||||
qWarning() << "Couldn't create folder at" << picturesDir.path() + QStringLiteral("/Screenshots") << "to take screenshot.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
filePath += QStringLiteral("/Screenshots/Screenshot_%1.png").arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss")));
|
|
||||||
const auto m_result = watcher->result();
|
|
||||||
if (m_result.isNull() || !m_result.save(filePath)) {
|
|
||||||
qWarning() << "Screenshot failed";
|
|
||||||
} else {
|
|
||||||
KNotification *notif = new KNotification("captured");
|
|
||||||
notif->setComponentName(QStringLiteral("plasma_phone_components"));
|
|
||||||
notif->setTitle(i18n("New Screenshot"));
|
|
||||||
notif->setUrls({QUrl::fromLocalFile(filePath)});
|
|
||||||
notif->setText(i18n("New screenshot saved to %1", filePath));
|
|
||||||
notif->sendEvent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
watcher->setFuture(QtConcurrent::run(readImage, fd, metadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhonePanel::takeScreenshot()
|
|
||||||
{
|
|
||||||
// wait ~200 ms to wait for rest of animations
|
|
||||||
QTimer::singleShot(SCREENSHOT_DELAY, [=]() {
|
|
||||||
int lPipeFds[2];
|
|
||||||
if (pipe2(lPipeFds, O_CLOEXEC) != 0) {
|
|
||||||
qWarning() << "Could not take screenshot";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't have access to the ScreenPool so we'll just take the first screen
|
|
||||||
QVariantMap options;
|
|
||||||
options.insert(QStringLiteral("native-resolution"), true);
|
|
||||||
|
|
||||||
auto pendingCall = m_screenshotInterface->CaptureScreen(
|
|
||||||
qGuiApp->screens().constFirst()->name(), options,
|
|
||||||
QDBusUnixFileDescriptor(lPipeFds[1]));
|
|
||||||
close(lPipeFds[1]);
|
|
||||||
auto pipeFileDescriptor = lPipeFds[0];
|
|
||||||
|
|
||||||
auto watcher = new QDBusPendingCallWatcher(pendingCall, this);
|
|
||||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, pipeFileDescriptor]() {
|
|
||||||
watcher->deleteLater();
|
|
||||||
const QDBusPendingReply<QVariantMap> reply = *watcher;
|
|
||||||
|
|
||||||
if (reply.isError()) {
|
|
||||||
qWarning() << "Screenshot request failed:" << reply.error().message();
|
|
||||||
} else {
|
|
||||||
handleMetaDataReceived(reply, pipeFileDescriptor);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PhonePanel::isSystem24HourFormat()
|
|
||||||
{
|
|
||||||
KConfigGroup localeSettings = KConfigGroup(m_localeConfig, "Locale");
|
|
||||||
|
|
||||||
QString timeFormat = localeSettings.readEntry("TimeFormat", QStringLiteral(FORMAT24H));
|
|
||||||
return timeFormat == QStringLiteral(FORMAT24H);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhonePanel::launchApp(const QString &app)
|
|
||||||
{
|
|
||||||
const KService::Ptr appService = KService::serviceByDesktopName(app);
|
|
||||||
if (!appService) {
|
|
||||||
qWarning() << "Could not find" << app;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto job = new KIO::ApplicationLauncherJob(appService, this);
|
|
||||||
job->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
K_PLUGIN_CLASS_WITH_JSON(PhonePanel, "metadata.json")
|
|
||||||
|
|
||||||
#include "phonepanel.moc"
|
|
||||||
|
|
|
||||||
|
|
@ -9,50 +9,13 @@
|
||||||
|
|
||||||
#include <Plasma/Containment>
|
#include <Plasma/Containment>
|
||||||
|
|
||||||
#include <KConfigWatcher>
|
|
||||||
#include <KSharedConfig>
|
|
||||||
|
|
||||||
#include "kscreeninterface.h"
|
|
||||||
#include "screenshot2interface.h"
|
|
||||||
|
|
||||||
class PhonePanel : public Plasma::Containment
|
class PhonePanel : public Plasma::Containment
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool autoRotateEnabled READ autoRotate WRITE setAutoRotate NOTIFY autoRotateChanged);
|
|
||||||
Q_PROPERTY(bool torchEnabled READ torchEnabled NOTIFY torchChanged);
|
|
||||||
Q_PROPERTY(bool isSystem24HourFormat READ isSystem24HourFormat NOTIFY isSystem24HourFormatChanged);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PhonePanel(QObject *parent, const QVariantList &args);
|
PhonePanel(QObject *parent, const QVariantList &args);
|
||||||
~PhonePanel() override;
|
~PhonePanel() override;
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void executeCommand(const QString &command);
|
|
||||||
void launchApp(const QString &app);
|
|
||||||
void toggleTorch();
|
|
||||||
void takeScreenshot();
|
|
||||||
|
|
||||||
bool autoRotate();
|
|
||||||
void setAutoRotate(bool value);
|
|
||||||
|
|
||||||
bool torchEnabled() const;
|
|
||||||
|
|
||||||
bool isSystem24HourFormat();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void autoRotateChanged(bool value);
|
|
||||||
void torchChanged(bool value);
|
|
||||||
void isSystem24HourFormatChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void handleMetaDataReceived(const QVariantMap &metadata, int fd);
|
|
||||||
bool m_running = false;
|
|
||||||
|
|
||||||
KConfigWatcher::Ptr m_localeConfigWatcher;
|
|
||||||
KSharedConfig::Ptr m_localeConfig;
|
|
||||||
|
|
||||||
org::kde::KScreen *m_kscreenInterface;
|
|
||||||
OrgKdeKWinScreenShot2Interface *m_screenshotInterface;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ ColumnLayout {
|
||||||
id: timeSource
|
id: timeSource
|
||||||
engine: "time"
|
engine: "time"
|
||||||
connectedSources: ["Local"]
|
connectedSources: ["Local"]
|
||||||
interval: 1000
|
interval: 60000
|
||||||
|
intervalAlignment: PlasmaCore.Types.AlignToMinute
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,12 @@ import QtQuick 2.12
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.1
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtGraphicalEffects 1.12
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.plasma.workspace.keyboardlayout 1.0
|
import org.kde.plasma.workspace.keyboardlayout 1.0
|
||||||
import org.kde.notificationmanager 1.1 as Notifications
|
import org.kde.notificationmanager 1.1 as Notifications
|
||||||
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
import "../components"
|
import "../components"
|
||||||
|
|
||||||
PlasmaCore.ColorScope {
|
PlasmaCore.ColorScope {
|
||||||
|
|
@ -78,9 +81,18 @@ PlasmaCore.ColorScope {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
height: PlasmaCore.Units.gridUnit
|
height: PlasmaCore.Units.gridUnit * 1.25
|
||||||
opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight)
|
opacity: 1 - (passwordFlickable.contentY / passwordFlickable.columnHeight)
|
||||||
sourceComponent: SimpleHeaderBar {}
|
sourceComponent: MobileShell.StatusBar {
|
||||||
|
id: statusBar
|
||||||
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||||
|
backgroundColor: "transparent"
|
||||||
|
|
||||||
|
showSecondRow: false
|
||||||
|
showDropShadow: true
|
||||||
|
showTime: false
|
||||||
|
disableSystemTray: true // HACK: prevent SIGABRT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// phone lockscreen component
|
// phone lockscreen component
|
||||||
|
|
@ -125,11 +137,10 @@ PlasmaCore.ColorScope {
|
||||||
alignment: Qt.AlignHCenter
|
alignment: Qt.AlignHCenter
|
||||||
Layout.bottomMargin: PlasmaCore.Units.gridUnit * 2 // keep spacing even if media controls are gone
|
Layout.bottomMargin: PlasmaCore.Units.gridUnit * 2 // keep spacing even if media controls are gone
|
||||||
}
|
}
|
||||||
MediaControls {
|
MobileShell.MediaControlsWidget {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
|
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
|
||||||
Layout.minimumWidth: PlasmaCore.Units.gridUnit * 15
|
|
||||||
Layout.leftMargin: PlasmaCore.Units.gridUnit
|
Layout.leftMargin: PlasmaCore.Units.gridUnit
|
||||||
Layout.rightMargin: PlasmaCore.Units.gridUnit
|
Layout.rightMargin: PlasmaCore.Units.gridUnit
|
||||||
}
|
}
|
||||||
|
|
@ -184,11 +195,10 @@ PlasmaCore.ColorScope {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: PlasmaCore.Units.gridUnit * 20
|
Layout.minimumWidth: PlasmaCore.Units.gridUnit * 20
|
||||||
}
|
}
|
||||||
MediaControls {
|
MobileShell.MediaControlsWidget {
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
|
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
|
||||||
Layout.minimumWidth: PlasmaCore.Units.gridUnit * 20
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of the KDE project.
|
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.5
|
|
||||||
import QtQuick.Layouts 1.1
|
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
|
||||||
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: mpris2Source.hasPlayer
|
|
||||||
implicitHeight: controlsRow.height + controlsRow.y
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: controlsRow
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
y: PlasmaCore.Units.smallSpacing // some distance to the password field
|
|
||||||
width: parent.width
|
|
||||||
height: PlasmaCore.Units.gridUnit * 3
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
enabled: mpris2Source.canControl
|
|
||||||
|
|
||||||
PlasmaCore.DataSource {
|
|
||||||
id: mpris2Source
|
|
||||||
|
|
||||||
readonly property string source: "@multiplex"
|
|
||||||
readonly property var playerData: data[source]
|
|
||||||
|
|
||||||
readonly property bool hasPlayer: sources.length > 1 && !!playerData
|
|
||||||
readonly property string identity: hasPlayer ? playerData.Identity : ""
|
|
||||||
readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing"
|
|
||||||
readonly property bool canControl: hasPlayer && playerData.CanControl
|
|
||||||
readonly property bool canGoBack: hasPlayer && playerData.CanGoPrevious
|
|
||||||
readonly property bool canGoNext: hasPlayer && playerData.CanGoNext
|
|
||||||
|
|
||||||
readonly property var currentMetadata: hasPlayer ? playerData.Metadata : ({})
|
|
||||||
|
|
||||||
readonly property string track: {
|
|
||||||
var xesamTitle = currentMetadata["xesam:title"]
|
|
||||||
if (xesamTitle) {
|
|
||||||
return xesamTitle
|
|
||||||
}
|
|
||||||
// if no track title is given, print out the file name
|
|
||||||
var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : ""
|
|
||||||
if (!xesamUrl) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var lastSlashPos = xesamUrl.lastIndexOf('/')
|
|
||||||
if (lastSlashPos < 0) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
|
|
||||||
return decodeURIComponent(lastUrlPart)
|
|
||||||
}
|
|
||||||
readonly property string artist: currentMetadata["xesam:artist"] || ""
|
|
||||||
readonly property string albumArt: currentMetadata["mpris:artUrl"] || ""
|
|
||||||
|
|
||||||
engine: "mpris2"
|
|
||||||
connectedSources: [source]
|
|
||||||
|
|
||||||
function startOperation(op) {
|
|
||||||
var service = serviceForSource(source)
|
|
||||||
var operation = service.operationDescription(op)
|
|
||||||
return service.startOperationCall(operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
function goPrevious() {
|
|
||||||
startOperation("Previous");
|
|
||||||
}
|
|
||||||
function goNext() {
|
|
||||||
startOperation("Next");
|
|
||||||
}
|
|
||||||
function playPause(source) {
|
|
||||||
startOperation("PlayPause");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: albumArt
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.fillHeight: true
|
|
||||||
asynchronous: true
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
source: mpris2Source.albumArt
|
|
||||||
sourceSize.height: height
|
|
||||||
visible: status === Image.Loading || status === Image.Ready
|
|
||||||
}
|
|
||||||
|
|
||||||
Item { // spacer
|
|
||||||
width: PlasmaCore.Units.smallSpacing
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
PlasmaComponents3.Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
elide: Text.ElideRight
|
|
||||||
text: mpris2Source.track || i18nd("plasma_lookandfeel_org.kde.lookandfeel", "No media playing")
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize + 1
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaExtras.DescriptiveLabel {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
elide: Text.ElideRight
|
|
||||||
// if no artist is given, show player name instead
|
|
||||||
text: mpris2Source.artist || mpris2Source.identity || ""
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
font.pointSize: PlasmaCore.Theme.smallestFont.pointSize + 1
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents3.ToolButton {
|
|
||||||
enabled: mpris2Source.canGoBack
|
|
||||||
icon.name: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
|
|
||||||
onClicked: {
|
|
||||||
// fadeoutTimer.running = false
|
|
||||||
mpris2Source.goPrevious()
|
|
||||||
}
|
|
||||||
visible: mpris2Source.canGoBack || mpris2Source.canGoNext
|
|
||||||
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Previous track")
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents3.ToolButton {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height // make this button bigger
|
|
||||||
icon.name: mpris2Source.playing ? "media-playback-pause" : "media-playback-start"
|
|
||||||
onClicked: {
|
|
||||||
// fadeoutTimer.running = false
|
|
||||||
mpris2Source.playPause()
|
|
||||||
}
|
|
||||||
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Play or Pause media")
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents3.ToolButton {
|
|
||||||
enabled: mpris2Source.canGoNext
|
|
||||||
icon.name: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
|
|
||||||
onClicked: {
|
|
||||||
// fadeoutTimer.running = false
|
|
||||||
mpris2Source.goNext()
|
|
||||||
}
|
|
||||||
visible: mpris2Source.canGoBack || mpris2Source.canGoNext
|
|
||||||
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Next track")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
* SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.12
|
|
||||||
import QtQuick.Controls 2.15 as Controls
|
|
||||||
import QtQuick.Layouts 1.3
|
|
||||||
import QtGraphicalEffects 1.12
|
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
import org.kde.plasma.workspace.components 2.0 as PW
|
|
||||||
|
|
||||||
import "indicators" as Indicators
|
|
||||||
import "indicators/providers" as Providers
|
|
||||||
|
|
||||||
// a simple version of the task panel
|
|
||||||
// in the future, it should share components with the existing task panel
|
|
||||||
PlasmaCore.ColorScope {
|
|
||||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
|
||||||
|
|
||||||
property real textPixelSize: PlasmaCore.Units.gridUnit * 0.6
|
|
||||||
|
|
||||||
layer.enabled: true
|
|
||||||
layer.effect: DropShadow {
|
|
||||||
visible: true
|
|
||||||
cached: true
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 1
|
|
||||||
radius: 6.0
|
|
||||||
samples: 17
|
|
||||||
color: Qt.rgba(0,0,0,0.6)
|
|
||||||
}
|
|
||||||
|
|
||||||
Providers.SignalStrengthProvider {
|
|
||||||
id: signalStrengthProviderLoader
|
|
||||||
}
|
|
||||||
|
|
||||||
Controls.Control {
|
|
||||||
topPadding: PlasmaCore.Units.smallSpacing
|
|
||||||
bottomPadding: PlasmaCore.Units.smallSpacing
|
|
||||||
rightPadding: PlasmaCore.Units.smallSpacing * 3
|
|
||||||
leftPadding: PlasmaCore.Units.smallSpacing * 3
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
id: row
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Indicators.SignalStrength {
|
|
||||||
provider: signalStrengthProviderLoader
|
|
||||||
labelPixelSize: textPixelSize
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
|
|
||||||
// spacing in the middle
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: indicators
|
|
||||||
spacing: PlasmaCore.Units.smallSpacing * 1.5
|
|
||||||
Layout.fillHeight: true
|
|
||||||
|
|
||||||
Indicators.Bluetooth {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
Indicators.Wifi {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
Indicators.Volume {
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
Indicators.Battery {
|
|
||||||
spacing: PlasmaCore.Units.smallSpacing * 1.5
|
|
||||||
labelPixelSize: textPixelSize
|
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
import org.kde.plasma.workspace.components 2.0 as PW
|
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
visible: pmSource.data["Battery"]["Has Cumulative"]
|
|
||||||
|
|
||||||
property real labelPixelSize
|
|
||||||
|
|
||||||
PW.BatteryIcon {
|
|
||||||
id: battery
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.fillHeight: true
|
|
||||||
hasBattery: true
|
|
||||||
percent: pmSource.data["Battery"]["Percent"]
|
|
||||||
pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false
|
|
||||||
|
|
||||||
height: batteryLabel.height
|
|
||||||
width: batteryLabel.height
|
|
||||||
|
|
||||||
PlasmaCore.DataSource {
|
|
||||||
id: pmSource
|
|
||||||
engine: "powermanagement"
|
|
||||||
connectedSources: ["Battery", "AC Adapter"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
|
||||||
id: batteryLabel
|
|
||||||
text: i18n("%1%", battery.percent)
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
|
||||||
font.pixelSize: labelPixelSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.2
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.bluezqt 1.0 as BluezQt
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: connectionIcon
|
|
||||||
|
|
||||||
property bool deviceConnected : false
|
|
||||||
|
|
||||||
source: deviceConnected ? "preferences-system-bluetooth-activated" : "preferences-system-bluetooth";
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
|
|
||||||
visible: BluezQt.Manager.bluetoothOperational
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
function updateStatus()
|
|
||||||
{
|
|
||||||
var connectedDevices = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < BluezQt.Manager.devices.length; ++i) {
|
|
||||||
var device = BluezQt.Manager.devices[i];
|
|
||||||
if (device.connected) {
|
|
||||||
connectedDevices.push(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deviceConnected = connectedDevices.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
BluezQt.Manager.deviceAdded.connect(updateStatus);
|
|
||||||
BluezQt.Manager.deviceRemoved.connect(updateStatus);
|
|
||||||
BluezQt.Manager.deviceChanged.connect(updateStatus);
|
|
||||||
BluezQt.Manager.bluetoothBlockedChanged.connect(updateStatus);
|
|
||||||
BluezQt.Manager.bluetoothOperationalChanged.connect(updateStatus);
|
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.6
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
|
|
||||||
import org.kde.plasma.plasmoid 2.0
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
|
|
||||||
property alias icon: icon.source
|
|
||||||
property alias text: label.text
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: icon
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
}
|
|
||||||
PlasmaComponents.Label {
|
|
||||||
id: label
|
|
||||||
visible: text.length > 0
|
|
||||||
color: PlasmaCore.ColorScope.textColor
|
|
||||||
font.pixelSize: parent.height / 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-FileCopyrightText: 2019 Aditya Mehra <Aix.m@outlook.com>
|
|
||||||
SPDX-FileCopyrightText: 2014-2015 Harald Sitter <sitter@kde.org>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.2
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.private.volume 0.1
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
|
|
||||||
id: paIcon
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
property bool volumeFeedback: true
|
|
||||||
property int maxVolumeValue: Math.round(100 * PulseAudio.NormalVolume / 100.0)
|
|
||||||
property int volumeStep: Math.round(5 * PulseAudio.NormalVolume / 100.0)
|
|
||||||
readonly property string dummyOutputName: "auto_null"
|
|
||||||
source: paSinkModel.preferredSink && !isDummyOutput(paSinkModel.preferredSink)
|
|
||||||
? iconName(paSinkModel.preferredSink.volume, paSinkModel.preferredSink.muted)
|
|
||||||
: iconName(0, true)
|
|
||||||
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
|
|
||||||
visible: paSinkModel.preferredSink && paSinkModel.preferredSink.muted
|
|
||||||
|
|
||||||
function iconName(volume, muted, prefix) {
|
|
||||||
if (!prefix) {
|
|
||||||
prefix = "audio-volume";
|
|
||||||
}
|
|
||||||
var icon = null;
|
|
||||||
var percent = volume / maxVolumeValue;
|
|
||||||
if (percent <= 0.0 || muted) {
|
|
||||||
icon = prefix + "-muted";
|
|
||||||
} else if (percent <= 0.25) {
|
|
||||||
icon = prefix + "-low";
|
|
||||||
} else if (percent <= 0.75) {
|
|
||||||
icon = prefix + "-medium";
|
|
||||||
} else {
|
|
||||||
icon = prefix + "-high";
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDummyOutput(output) {
|
|
||||||
return output && output.name === dummyOutputName;
|
|
||||||
}
|
|
||||||
|
|
||||||
SinkModel {
|
|
||||||
id: paSinkModel
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeOSD {
|
|
||||||
id: osd
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeFeedback {
|
|
||||||
id: feedback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
|
|
||||||
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.2
|
|
||||||
import QtQuick.Layouts 1.4
|
|
||||||
import org.kde.plasma.core 2.0 as PlasmaCore
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
|
||||||
id: connectionIcon
|
|
||||||
|
|
||||||
source: connectionIconProvider.connectionIcon
|
|
||||||
colorGroup: PlasmaCore.ColorScope.colorGroup
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
|
|
||||||
PlasmaComponents.BusyIndicator {
|
|
||||||
id: connectingIndicator
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
running: connectionIconProvider.connecting
|
|
||||||
visible: running
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaNM.NetworkStatus {
|
|
||||||
id: networkStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaNM.NetworkModel {
|
|
||||||
id: connectionModel
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaNM.Handler {
|
|
||||||
id: handler
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaNM.ConnectionIcon {
|
|
||||||
id: connectionIconProvider
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
|
||||||
* SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.1
|
|
||||||
|
|
||||||
import org.kde.plasma.mm 1.0
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
|
|
||||||
property string icon: "network-mobile-" + Math.floor(SignalIndicator.strength / 20) * 20
|
|
||||||
|
|
||||||
property string label: !SignalIndicator.available ? ""
|
|
||||||
: SignalIndicator.simLocked ? i18n("SIM Locked") : SignalIndicator.name
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -5,12 +5,13 @@
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.kde.plasma.private.mobilehomescreencomponents 0.1 as HomeScreenComponents
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
import org.kde.plasma.networkmanagement 0.2 as PlasmaNM
|
||||||
|
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Airplane Mode")
|
text: i18n("Airplane Mode")
|
||||||
icon: "network-flightmode-on"
|
icon: "network-flightmode-on"
|
||||||
|
status: ""
|
||||||
enabled: PlasmaNM.Configuration.airplaneModeEnabled
|
enabled: PlasmaNM.Configuration.airplaneModeEnabled
|
||||||
|
|
||||||
PlasmaNM.Handler {
|
PlasmaNM.Handler {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.kde.colorcorrect 0.1 as CC
|
import org.kde.colorcorrect 0.1 as CC
|
||||||
import org.kde.plasma.private.mobilehomescreencomponents 0.1 as HomeScreenComponents
|
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
||||||
|
|
||||||
HomeScreenComponents.QuickSetting {
|
MobileShell.QuickSetting {
|
||||||
text: i18n("Night Color")
|
text: i18n("Night Color")
|
||||||
icon: "redshift-status-on"
|
icon: "redshift-status-on"
|
||||||
enabled: compositorAdaptor.active
|
enabled: compositorAdaptor.active
|
||||||
|
status: ""
|
||||||
settingsCommand: "plasma-open-settings kcm_nightcolor"
|
settingsCommand: "plasma-open-settings kcm_nightcolor"
|
||||||
|
|
||||||
CC.CompositorAdaptor {
|
CC.CompositorAdaptor {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ for (var j = 0; j < desktopsArray.length; j++) {
|
||||||
var panel = new Panel("org.kde.phone.panel");
|
var panel = new Panel("org.kde.phone.panel");
|
||||||
panel.location = "top";
|
panel.location = "top";
|
||||||
panel.addWidget("org.kde.plasma.notifications");
|
panel.addWidget("org.kde.plasma.notifications");
|
||||||
panel.addWidget("org.kde.plasma.mediacontroller");
|
|
||||||
panel.height = 1.25 * gridUnit; // HACK: supposed to be gridUnit + smallSpacing, but it doesn't seem to give the correct number
|
panel.height = 1.25 * gridUnit; // HACK: supposed to be gridUnit + smallSpacing, but it doesn't seem to give the correct number
|
||||||
|
|
||||||
var bottomPanel = new Panel("org.kde.phone.taskpanel")
|
var bottomPanel = new Panel("org.kde.phone.taskpanel")
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue