Commit a173cf9b authored by Devin Lin's avatar Devin Lin 🎨
Browse files

homescreens/halcyon: Add new homescreen

parent b9f90668
Pipeline #187254 passed with stage
in 1 minute and 15 seconds
......@@ -213,4 +213,12 @@ Item {
z: 999999
anchors.fill: parent
}
// listen to app launch errors
Connections {
target: MobileShell.ApplicationListModel
function onLaunchError(msg) {
MobileShell.HomeScreenControls.closeAppLaunchAnimation()
}
}
}
......@@ -2,3 +2,4 @@
# SPDX-License-Identifier: GPL-2.0-or-later
add_subdirectory(default)
add_subdirectory(halcyon)
# 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)
#! /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
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
// 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
}
}
// 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)
}
}
}
}
/*
* 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"
}
}
}
/*
* 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();
}
}
}
// 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 <