homescreens/halcyon: Add new homescreen

This commit is contained in:
Devin Lin 2022-06-08 18:09:35 -04:00
parent b9f9066880
commit a173cf9b9d
13 changed files with 752 additions and 0 deletions

View file

@ -213,4 +213,12 @@ Item {
z: 999999 z: 999999
anchors.fill: parent anchors.fill: parent
} }
// listen to app launch errors
Connections {
target: MobileShell.ApplicationListModel
function onLaunchError(msg) {
MobileShell.HomeScreenControls.closeAppLaunchAnimation()
}
}
} }

View file

@ -2,3 +2,4 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
add_subdirectory(default) add_subdirectory(default)
add_subdirectory(halcyon)

View file

@ -0,0 +1,27 @@
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
set(homescreen_SRCS
homescreen.cpp
)
add_library(plasma_containment_phone_homescreen_halcyon MODULE ${homescreen_SRCS})
kcoreaddons_desktop_to_json(plasma_containment_phone_homescreen_halcyon package/metadata.desktop)
target_link_libraries(plasma_containment_phone_homescreen_halcyon
Qt::Gui
KF5::Plasma
Qt::Qml
Qt::Quick
KF5::I18n
KF5::Service
KF5::KIOGui
KF5::Notifications
KF5::WaylandClient
KF5::WindowSystem
)
install(TARGETS plasma_containment_phone_homescreen_halcyon DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets)
plasma_install_package(package org.kde.phone.homescreen.halcyon)

View file

@ -0,0 +1,9 @@
#! /usr/bin/env bash
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
$EXTRACTRC `find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` >> rc.cpp
$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.phone.homescreen.simple.pot
rm -f rc.cpp

View file

@ -0,0 +1,2 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later

View file

@ -0,0 +1,2 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later

View file

@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.1-or-later
import QtQuick 2.15
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.12
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
ColumnLayout {
id: root
readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
readonly property bool is24HourTime: MobileShell.ShellUtil.isSystem24HourFormat
spacing: 0
Label {
text: Qt.formatTime(timeSource.data["Local"]["DateTime"], root.is24HourTime ? "h:mm" : "h:mm ap")
color: "white"
style: softwareRendering ? Text.Outline : Text.Normal
styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" // no outline, doesn't matter
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
font.weight: Font.Bold // this font weight may switch to regular on distros that don't have a light variant
font.pointSize: 28
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 1
radius: 4
samples: 6
color: Qt.rgba(0, 0, 0, 0.5)
}
}
Label {
Layout.topMargin: PlasmaCore.Units.smallSpacing
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
text: Qt.formatDate(timeSource.data["Local"]["DateTime"], "ddd, MMM d")
color: "white"
style: softwareRendering ? Text.Outline : Text.Normal
styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" // no outline, doesn't matter
font.pointSize: 12
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 1
radius: 4
samples: 6
color: Qt.rgba(0, 0, 0, 0.5)
}
}
PlasmaCore.DataSource {
id: timeSource
engine: "time"
connectedSources: ["Local"]
interval: 1000
}
}

View file

@ -0,0 +1,153 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <espidev@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.4
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.3 as Controls
import QtGraphicalEffects 1.6
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 org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.private.containmentlayoutmanager 1.0 as ContainmentLayoutManager
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
import org.kde.kirigami 2.19 as Kirigami
MouseArea {
id: delegate
property alias iconItem: icon
signal launch(int x, int y, var source, string title, string storageId)
signal dragStarted(string imageSource, int x, int y, string mimeData)
onLaunch: {
if (icon !== "") {
MobileShell.HomeScreenControls.openAppLaunchAnimation(
icon,
title,
delegate.iconItem.Kirigami.ScenePosition.x + delegate.iconItem.width/2,
delegate.iconItem.Kirigami.ScenePosition.y + delegate.iconItem.height/2,
Math.min(delegate.iconItem.width, delegate.iconItem.height));
}
MobileShell.ApplicationListModel.setMinimizedDelegate(index, delegate);
MobileShell.ApplicationListModel.runApplication(storageId);
}
onPressAndHold: {
dialogLoader.active = true;
dialogLoader.item.open();
}
onClicked: {
// launch app
if (model.applicationRunning) {
delegate.launch(0, 0, "", model.applicationName, model.applicationStorageId);
} else {
delegate.launch(delegate.x + (PlasmaCore.Units.smallSpacing * 2), delegate.y + (PlasmaCore.Units.smallSpacing * 2), icon.source, model.applicationName, model.applicationStorageId);
}
}
hoverEnabled: true
Loader {
id: dialogLoader
active: false
sourceComponent: PlasmaComponents.Menu {
title: label.text
PlasmaComponents.MenuItem {
icon.name: "emblem-favorite"
text: i18n("Remove from favourites")
onClicked: {
MobileShell.FavoritesModel.removeFavorite(model.index);
}
}
onClosed: dialogLoader.active = false
}
}
Rectangle {
anchors.fill: parent
radius: height / 2
color: delegate.pressed ? Qt.rgba(255, 255, 255, 0.2) : (delegate.containsMouse ? Qt.rgba(255, 255, 255, 0.1) : "transparent")
}
RowLayout {
anchors {
fill: parent
leftMargin: PlasmaCore.Units.smallSpacing * 2
topMargin: PlasmaCore.Units.smallSpacing
rightMargin: PlasmaCore.Units.smallSpacing * 2
bottomMargin: PlasmaCore.Units.smallSpacing
}
spacing: 0
PlasmaCore.IconItem {
id: icon
Layout.alignment: Qt.AlignLeft
Layout.minimumWidth: Layout.minimumHeight
Layout.preferredWidth: Layout.minimumHeight
Layout.minimumHeight: parent.height
Layout.preferredHeight: Layout.minimumHeight
usesPlasmaTheme: false
source: model.applicationIcon
Rectangle {
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
visible: model.applicationRunning
radius: width
width: PlasmaCore.Units.smallSpacing
height: width
color: PlasmaCore.Theme.highlightColor
}
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 1
radius: 4
samples: 6
color: Qt.rgba(0, 0, 0, 0.5)
}
}
PlasmaComponents.Label {
id: label
visible: text.length > 0
Layout.fillWidth: true
Layout.leftMargin: PlasmaCore.Units.smallSpacing * 2
Layout.rightMargin: PlasmaCore.Units.largeSpacing
wrapMode: Text.WordWrap
maximumLineCount: 1
elide: Text.ElideRight
text: model.applicationName
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize
font.weight: Font.Bold
color: "white"
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 1
radius: 4
samples: 6
color: Qt.rgba(0, 0, 0, 0.5)
}
}
}
}

View file

@ -0,0 +1,173 @@
/*
* SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.15 as Controls
import QtGraphicalEffects 1.6
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 org.kde.kquickcontrolsaddons 2.0
import org.kde.plasma.private.containmentlayoutmanager 1.0 as ContainmentLayoutManager
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
import org.kde.kirigami 2.19 as Kirigami
MouseArea {
id: delegate
width: GridView.view.cellWidth
height: GridView.view.cellHeight
property int reservedSpaceForLabel
property alias iconItem: icon
readonly property real margins: Math.floor(width * 0.2)
signal launch(int x, int y, var source, string title, string storageId)
onPressAndHold: {
dialogLoader.active = true;
dialogLoader.item.open();
}
function launchApp() {
// launch app
if (model.applicationRunning) {
delegate.launch(0, 0, "", model.applicationName, model.applicationStorageId);
} else {
delegate.launch(delegate.x + (PlasmaCore.Units.smallSpacing * 2), delegate.y + (PlasmaCore.Units.smallSpacing * 2), icon.source, model.applicationName, model.applicationStorageId);
}
}
Loader {
id: dialogLoader
active: false
sourceComponent: PlasmaComponents.Menu {
title: label.text
PlasmaComponents.MenuItem {
icon.name: "emblem-favorite"
text: i18n("Add to favourites")
onClicked: {
MobileShell.FavoritesModel.addFavorite(model.applicationStorageId, 0, MobileShell.ApplicationListModel.Favorites);
}
}
onClosed: dialogLoader.active = false
}
}
// grow/shrink animation
property real zoomScale: 1
transform: Scale {
origin.x: delegate.width / 2;
origin.y: delegate.height / 2;
xScale: delegate.zoomScale
yScale: delegate.zoomScale
}
property bool launchAppRequested: false
NumberAnimation on zoomScale {
id: shrinkAnim
running: false
duration: MobileShell.MobileShellSettings.animationsEnabled ? 80 : 1
to: MobileShell.MobileShellSettings.animationsEnabled ? 0.8 : 1
onFinished: {
if (!delegate.pressed) {
growAnim.restart();
}
}
}
NumberAnimation on zoomScale {
id: growAnim
running: false
duration: MobileShell.MobileShellSettings.animationsEnabled ? 80 : 1
to: 1
onFinished: {
if (delegate.launchAppRequested) {
delegate.launchApp();
delegate.launchAppRequested = false;
}
}
}
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onPressedChanged: {
if (pressed) {
growAnim.stop();
shrinkAnim.restart();
} else if (!pressed && !shrinkAnim.running) {
growAnim.restart();
}
}
// launch app handled by press animation
onClicked: launchAppRequested = true;
ColumnLayout {
anchors {
fill: parent
leftMargin: margins
topMargin: margins
rightMargin: margins
bottomMargin: margins
}
spacing: 0
PlasmaCore.IconItem {
id: icon
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.fillWidth: true
Layout.minimumHeight: Math.floor(parent.height - delegate.reservedSpaceForLabel)
Layout.preferredHeight: Layout.minimumHeight
usesPlasmaTheme: false
source: model.applicationIcon
Rectangle {
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
visible: model.applicationRunning
radius: width
width: PlasmaCore.Units.smallSpacing
height: width
color: theme.highlightColor
}
}
PlasmaComponents.Label {
id: label
visible: text.length > 0
Layout.fillWidth: true
Layout.topMargin: PlasmaCore.Units.smallSpacing
Layout.preferredHeight: delegate.reservedSpaceForLabel
wrapMode: Text.WordWrap
Layout.leftMargin: -parent.anchors.leftMargin + PlasmaCore.Units.smallSpacing
Layout.rightMargin: -parent.anchors.rightMargin + PlasmaCore.Units.smallSpacing
maximumLineCount: 2
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
elide: Text.ElideRight
text: model.applicationName
font.pointSize: theme.defaultFont.pointSize * 0.85
font.weight: Font.Bold
color: "white"
}
}
}

View file

@ -0,0 +1,81 @@
/*
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.15 as Controls
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PC3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kirigami 2.10 as Kirigami
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
GridView {
id: gridView
clip: true
signal launched
readonly property int reservedSpaceForLabel: metrics.height
cellWidth: width / Math.min(Math.floor(width / (PlasmaCore.Units.iconSizes.huge + Kirigami.Units.largeSpacing * 2)), 8)
cellHeight: cellWidth + reservedSpaceForLabel
property int columns: Math.floor(width / cellWidth)
property int rows: Math.ceil(model.count / columns)
cacheBuffer: Math.max(0, rows * cellHeight)
model: MobileShell.ApplicationListModel
header: Controls.Control {
implicitWidth: gridView.width
topPadding: PlasmaCore.Units.largeSpacing
bottomPadding: PlasmaCore.Units.largeSpacing
leftPadding: PlasmaCore.Units.smallSpacing
contentItem: PlasmaExtras.Heading {
color: "white"
level: 1
font.weight: Font.Bold
text: i18n("Applications")
}
}
PC3.Label {
id: metrics
text: "M\nM"
visible: false
font.pointSize: PlasmaCore.Theme.defaultFont.pointSize * 0.85
font.weight: Font.Bold
}
delegate: GridAppDelegate {
id: delegate
width: gridView.cellWidth
height: gridView.cellHeight
reservedSpaceForLabel: gridView.reservedSpaceForLabel
onLaunch: (x, y, icon, title, storageId) => {
if (icon !== "") {
MobileShell.HomeScreenControls.openAppLaunchAnimation(
icon,
title,
delegate.iconItem.Kirigami.ScenePosition.x + delegate.iconItem.width/2,
delegate.iconItem.Kirigami.ScenePosition.y + delegate.iconItem.height/2,
Math.min(delegate.iconItem.width, delegate.iconItem.height));
}
MobileShell.ApplicationListModel.setMinimizedDelegate(index, delegate);
MobileShell.ApplicationListModel.runApplication(storageId);
gridView.launched();
}
}
}

View file

@ -0,0 +1,134 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.12
import QtQuick.Controls 2.15 as QQC2
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.extras 2.0 as PlasmaExtras
import org.kde.plasma.components 3.0 as PC3
import org.kde.draganddrop 2.0 as DragDrop
import org.kde.kirigami 2.19 as Kirigami
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Item {
id: root
property bool interactive: true
property var searchWidget
property alias page: swipeView.currentIndex
function triggerHomescreen() {
swipeView.setCurrentIndex(0);
favouritesList.contentY = favouritesList.originY;
}
QQC2.SwipeView {
id: swipeView
opacity: 1 - searchWidget.openFactor
interactive: root.interactive
anchors.fill: parent
anchors.topMargin: MobileShell.Shell.topMargin
anchors.bottomMargin: MobileShell.Shell.bottomMargin
anchors.leftMargin: MobileShell.Shell.leftMargin
anchors.rightMargin: MobileShell.Shell.rightMargin
Item {
height: swipeView.height
width: swipeView.width
ListView {
id: favouritesList
clip: true
interactive: root.interactive
property real delegateHeight: PlasmaCore.Units.gridUnit * 3
anchors.fill: parent
anchors.leftMargin: Math.round(parent.width * 0.1)
anchors.rightMargin: Math.round(parent.width * 0.1)
// open wallpaper menu when held on click
TapHandler {
onLongPressed: {
plasmoid.action("configure").trigger();
plasmoid.editMode = false;
}
}
model: MobileShell.FavoritesModel
header: MobileShell.BaseItem {
topPadding: Math.round(swipeView.height * 0.2)
bottomPadding: PlasmaCore.Units.largeSpacing
implicitWidth: favouritesList.width
contentItem: Clock {}
}
delegate: DrawerListDelegate {
id: delegate
width: favouritesList.width
height: visible ? favouritesList.delegateHeight : 0
}
ColumnLayout {
id: placeholder
spacing: PlasmaCore.Units.gridUnit
visible: favouritesList.count == 0
opacity: 0.9
anchors.topMargin: Math.round(swipeView.height * 0.2) - (favouritesList.contentY - favouritesList.originY)
anchors.fill: parent
Kirigami.Icon {
id: icon
Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
implicitWidth: PlasmaCore.Units.iconSizes.large
implicitHeight: width
source: "emblem-favorite"
color: "white"
}
PlasmaExtras.Heading {
Layout.fillWidth: true
Layout.maximumWidth: placeholder.width * 0.75
Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
color: "white"
level: 3
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
text: i18n("Add applications to your favourites so they show up here.")
}
}
}
}
ColumnLayout {
id: column
height: swipeView.height
width: swipeView.width
property real horizontalMargin: Math.max(Kirigami.Units.largeSpacing, column.width * 0.1 / 2)
GridAppList {
interactive: root.interactive
Layout.leftMargin: column.horizontalMargin
Layout.rightMargin: column.horizontalMargin
Layout.fillWidth: true
Layout.fillHeight: true
// open search widget when pulled down
onDraggingChanged: {
if (!dragging && (contentY < originY - PlasmaCore.Units.gridUnit * 3)) {
searchWidget.open();
}
}
}
}
}
}

View file

@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Window 2.15
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 org.kde.plasma.private.mobileshell 1.0 as MobileShell
MobileShell.HomeScreen {
id: root
onResetHomeScreenPosition: {
homescreen.triggerHomescreen();
}
onHomeTriggered: {
search.close();
}
Component.onCompleted: {
MobileShell.ApplicationListModel.loadApplications();
MobileShell.FavoritesModel.applet = plasmoid;
MobileShell.FavoritesModel.loadApplications();
forceActiveFocus();
}
Rectangle {
id: darkenBackground
color: homescreen.page == 1 ? Qt.rgba(0, 0, 0, 0.7) : Qt.rgba(0, 0, 0, 0.2)
anchors.fill: parent
z: -1
Behavior on color {
ColorAnimation { duration: PlasmaCore.Units.longDuration }
}
}
// homescreen component
contentItem: Item {
HomeScreen {
id: homescreen
anchors.fill: parent
// make the homescreen not interactable when task switcher or startup feedback is on
interactive: !root.overlayShown
searchWidget: search
}
// search component
MobileShell.KRunnerWidget {
id: search
anchors.fill: parent
visible: openFactor > 0
// close search component when task switcher is shown or hidden
Connections {
target: MobileShell.HomeScreenControls.taskSwitcher
function onVisibleChanged() {
searchWidget.close();
}
}
}
}
}

View file

@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
# SPDX-License-Identifier: GPL-2.0-or-later
[Desktop Entry]
Encoding=UTF-8
Keywords=
Name=Halcyon
Description=A mobile homescreen focused on simplicity and ease-of-use.
Type=Service
X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment
X-Plasma-API=declarativeappletscript
X-KDE-Library=plasma_containment_phone_homescreen_halcyon
X-KDE-PluginInfo-Author=Devin Lin
X-KDE-PluginInfo-Category=Containments
X-KDE-PluginInfo-Email=devin@kde.org
X-KDE-PluginInfo-License=GPLv2+
X-KDE-PluginInfo-Name=org.kde.phone.homescreen.halcyon
X-KDE-PluginInfo-Version=
X-KDE-PluginInfo-Website=
X-Plasma-ContainmentType=Desktop
X-Plasma-MainScript=ui/main.qml
X-Plasma-Provides=org.kde.plasma.launchermenu