2024-10-31 05:05:44 +00:00
// SPDX-FileCopyrightText: 2021-2024 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick 2.15
import QtQuick . Controls 2.15
import QtQuick . Window 2.2
2025-03-20 02:06:33 +00:00
import QtQuick . Layouts
2024-10-31 05:05:44 +00:00
2026-05-24 13:48:57 +00:00
import org . kde . plasma . clock
2025-03-20 02:06:33 +00:00
import org . kde . plasma . components 3.0 as PlasmaComponents
2024-10-31 05:05:44 +00:00
import org . kde . plasma . private . mobileshell as MobileShell
import org . kde . plasma . components 3.0 as PC3
import org . kde . kirigami as Kirigami
2026-05-24 13:48:57 +00:00
import org . kde . plasma . workspace . calendar as PlasmaCalendar
2024-10-31 05:05:44 +00:00
import org . kde . plasma . private . mobileshell . quicksettingsplugin as QS
2026-04-16 15:25:37 +00:00
import org . kde . plasma . private . mobileshell . shellsettingsplugin as ShellSettings
2024-10-31 05:05:44 +00:00
/ * *
* Root element that contains all the ActionDrawer ' s contents , and is anchored to the screen .
* /
2024-11-08 04:06:22 +00:00
Item {
2024-10-31 05:05:44 +00:00
id: root
required property var actionDrawer
required property QS . QuickSettingsModel quickSettingsModel
readonly property real minimizedQuickSettingsOffset: contentContainerLoader . minimizedQuickSettingsOffset
readonly property real maximizedQuickSettingsOffset: contentContainerLoader . maximizedQuickSettingsOffset
2025-03-22 12:56:32 +00:00
readonly property bool swipeAreaMoving: swipeAreaBase . moving || swipeAreaPortrait . moving
2026-05-19 07:13:08 +00:00
readonly property bool isConvergence: ShellSettings . Settings . convergenceModeEnabled
readonly property real convergenceFrameThickness: MobileShell . Constants . convergenceWorkspaceFrameThickness
2026-05-24 13:48:57 +00:00
readonly property real convergenceSurfaceTopInset: MobileShell . Constants . topPanelHeight + convergenceFrameThickness
2026-05-19 07:13:08 +00:00
readonly property real convergenceSurfaceBottomInset: MobileShell . Constants . convergenceDockHeight + convergenceFrameThickness
readonly property real convergenceSurfaceSideInset: 0
readonly property real convergenceSurfaceWidth: Math . max ( 0 , width - convergenceSurfaceSideInset * 2 )
readonly property real convergenceSurfaceHeight: Math . max ( 0 , height - convergenceSurfaceTopInset - convergenceSurfaceBottomInset )
2026-05-24 14:31:28 +00:00
readonly property real convergenceFloatingMargin: Kirigami . Units . gridUnit
readonly property real convergenceClickAwayGutter: Kirigami . Units . largeSpacing
readonly property real convergenceBottomClickAwayHeight: Kirigami . Units . gridUnit * 2
readonly property real convergenceLeftSurfaceBottomInset: convergenceSurfaceBottomInset + convergenceBottomClickAwayHeight
readonly property real convergenceQuickSettingsLeft: contentContainerLoader . item ? contentContainerLoader.item.quickSettingsPanelLeft : convergenceSurfaceWidth * 0.5
readonly property real convergenceNotificationRightMargin: Math . max ( convergenceSurfaceSideInset , width - Math . max ( convergenceSurfaceSideInset , convergenceQuickSettingsLeft - convergenceClickAwayGutter ) )
readonly property real convergenceLeftSurfaceTopInset: convergenceSurfaceTopInset + convergenceFloatingMargin
readonly property real convergenceLeftSurfaceLeftInset: convergenceSurfaceSideInset + convergenceFloatingMargin
readonly property real convergenceLeftSurfaceRightInset: convergenceNotificationRightMargin + convergenceFloatingMargin
readonly property real convergenceLeftSurfaceBottomMargin: convergenceLeftSurfaceBottomInset + convergenceFloatingMargin
2025-03-22 12:56:32 +00:00
2025-09-18 13:29:53 +00:00
Kirigami.Theme.colorSet: Kirigami . Theme . Complementary
2025-03-20 02:06:33 +00:00
Kirigami.Theme.inherit: false
readonly property alias brightnessPressedValue: quickSettings . brightnessPressedValue
2024-10-31 05:05:44 +00:00
function applyMinMax ( val ) {
return Math . max ( 0 , Math . min ( 1 , val ) ) ;
}
2025-03-20 02:06:33 +00:00
function startSwipe ( ) {
actionDrawer . cancelAnimations ( ) ;
actionDrawer . dragging = true ;
// Immediately open action drawer if we interact with it and it's already open
// This allows us to have 2 quick flicks from minimized -> expanded
if ( actionDrawer . visible && ! actionDrawer . opened ) {
actionDrawer . opened = true ;
}
}
2024-11-01 22:41:57 +00:00
2025-03-20 02:06:33 +00:00
function endSwipe ( ) {
actionDrawer . dragging = false ;
actionDrawer . updateState ( ) ;
}
function moveSwipe ( totalDeltaX , totalDeltaY , deltaX , deltaY ) {
actionDrawer . offset += deltaY ;
}
2024-11-14 03:34:01 +00:00
2024-10-31 05:05:44 +00:00
// Background color
2024-11-08 04:06:22 +00:00
Rectangle {
anchors.fill: parent
2026-06-01 10:38:17 +00:00
color: MobileShell . SurfaceColors . withAlpha ( MobileShell . SurfaceColors . accentSurface ( Kirigami . Theme . backgroundColor , 0.18 , 0.10 ) , 0.9 )
2026-05-21 09:12:44 +00:00
Behavior on color { MobileShell . MotionColorAnimation { type: MobileShell . Motion . StandardDecel } }
2026-04-16 15:25:37 +00:00
opacity: {
let base = Math . max ( 0 , Math . min ( brightnessPressedValue , actionDrawer . offset / root . minimizedQuickSettingsOffset ) ) ;
return ShellSettings . Settings . convergenceModeEnabled ? base * 0.3 : base ;
}
2024-11-08 04:06:22 +00:00
}
2024-10-31 05:05:44 +00:00
2025-03-20 02:06:33 +00:00
// The base swipe area.
// Used to cover the full surface of the drawer to allow dismissing or expanding it.
MobileShell . SwipeArea {
id: swipeAreaBase
mode: MobileShell . SwipeArea . VerticalOnly
anchors.fill: parent
onSwipeStarted: root . startSwipe ( )
onSwipeEnded: root . endSwipe ( )
onSwipeMove: ( totalDeltaX , totalDeltaY , deltaX , deltaY ) = > root . moveSwipe ( totalDeltaX , totalDeltaY , deltaX , deltaY )
onTouchpadScrollStarted: root . startSwipe ( )
onTouchpadScrollEnded: root . endSwipe ( )
onTouchpadScrollMove: ( totalDeltaX , totalDeltaY , deltaX , deltaY ) = > root . moveSwipe ( totalDeltaX , totalDeltaY , deltaX , deltaY )
// Proxy in the layout that switches between landscape and portrait mode.
ColumnLayout {
anchors.fill: parent
2025-07-17 05:42:35 +00:00
visible: root . actionDrawer . mode != MobileShell . ActionDrawer . Portrait
2025-03-20 02:06:33 +00:00
LayoutItemProxy { target: contentContainerLoader }
}
2026-05-24 14:31:28 +00:00
Item {
id: convergenceLeftSurface
visible: root . isConvergence && actionDrawer . mode != MobileShell . ActionDrawer . Portrait
opacity: Math . max ( 0 , Math . min ( root . brightnessPressedValue , actionDrawer . offsetResistance / root . minimizedQuickSettingsOffset ) )
anchors {
top: parent . top
left: parent . left
right: parent . right
bottom: parent . bottom
topMargin: root . convergenceLeftSurfaceTopInset
leftMargin: root . convergenceLeftSurfaceLeftInset
rightMargin: root . convergenceLeftSurfaceRightInset
bottomMargin: root . convergenceLeftSurfaceBottomMargin
}
MobileShell . PanelBackground {
anchors.fill: parent
panelType: MobileShell . PanelBackground . PanelType . Drawer
}
}
2025-03-20 02:06:33 +00:00
// Mouse area for dismissing action drawer in portrait mode when background is clicked.
MouseArea {
anchors.fill: parent
2025-07-17 05:42:35 +00:00
visible: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait
2025-03-20 02:06:33 +00:00
// dismiss drawer when background is clicked
onClicked: root . actionDrawer . close ( ) ;
}
// The clear all notification history button.
Item {
id: toolButtons
height: visible ? spacer . height + toolLayout . height + toolLayout . anchors . topMargin + toolLayout.anchors.bottomMargin : 0
visible: actionDrawer . intendedToBeVisible
opacity: Math . max ( 0 , Math . min ( root . brightnessPressedValue , actionDrawer . offsetResistance / root . minimizedQuickSettingsOffset ) )
anchors {
2026-05-19 07:13:08 +00:00
topMargin: notificationDrawer . y + notificationDrawer . height + 1
2026-05-24 14:31:28 +00:00
leftMargin: actionDrawer . mode == MobileShell . ActionDrawer . Portrait ? 0 : ( notificationDrawer . isConvergence ? root.convergenceLeftSurfaceLeftInset : 10 )
rightMargin: actionDrawer . mode == MobileShell . ActionDrawer . Portrait ? 0 : ( notificationDrawer . isConvergence ? root.convergenceLeftSurfaceRightInset : notificationDrawer . notificationWidget . anchors . rightMargin + Kirigami . Units . gridUnit - notificationDrawer . anchors . leftMargin + 370 )
2025-03-20 02:06:33 +00:00
top: parent . top
left: parent . left
right: parent . right
}
Rectangle {
id: spacer
anchors.left: parent . left
anchors.right: parent . right
visible: notificationDrawer . listOverflowing
height: 1
opacity: 0.25
color: Kirigami . Theme . textColor
}
RowLayout {
id: toolLayout
anchors {
top: spacer . bottom
right: parent . right
left: parent . left
leftMargin: Kirigami . Units . largeSpacing
rightMargin: Kirigami . Units . largeSpacing
topMargin: Kirigami . Units . largeSpacing
bottomMargin: Kirigami . Units . largeSpacing
}
PlasmaComponents . ToolButton {
id: clearButton
Layout.alignment: Qt . AlignCenter
visible: notificationDrawer . hasNotifications
font.bold: true
font.pointSize: Kirigami . Theme . smallFont . pointSize
icon.name: "edit-clear-history"
text: i18n ( "Clear All Notifications" )
onClicked: notificationDrawer . notificationWidget . clearHistory ( )
}
}
}
2026-05-24 13:48:57 +00:00
2026-05-24 14:31:28 +00:00
Item {
id: convergenceMediaPanel
visible: root . isConvergence
&& actionDrawer . mode != MobileShell . ActionDrawer . Portrait
&& root . mediaControlsWidget . visible
opacity: Math . max ( 0 , Math . min ( root . brightnessPressedValue , actionDrawer . offsetResistance / root . minimizedQuickSettingsOffset ) )
height: visible ? root.mediaControlsWidget.implicitHeight : 0
anchors {
top: parent . top
left: parent . left
right: parent . right
topMargin: notificationDrawer . hasNotifications
? toolButtons . y + toolButtons . height + Kirigami . Units . smallSpacing
: notificationDrawer . y + notificationDrawer . height + Kirigami . Units . largeSpacing
leftMargin: root . convergenceLeftSurfaceLeftInset + Kirigami . Units . largeSpacing
rightMargin: root . convergenceLeftSurfaceRightInset + Kirigami . Units . largeSpacing
}
LayoutItemProxy {
anchors.fill: parent
target: root . mediaControlsWidget
}
}
2026-05-24 13:48:57 +00:00
Item {
id: convergenceCalendarPanel
visible: root . isConvergence && actionDrawer . mode != MobileShell . ActionDrawer . Portrait
opacity: Math . max ( 0 , Math . min ( root . brightnessPressedValue , actionDrawer . offsetResistance / root . minimizedQuickSettingsOffset ) )
clip: true
anchors {
top: parent . top
left: parent . left
right: parent . right
bottom: parent . bottom
2026-05-24 14:31:28 +00:00
topMargin: convergenceMediaPanel . visible
? convergenceMediaPanel . y + convergenceMediaPanel . height + Kirigami . Units . largeSpacing
: notificationDrawer . hasNotifications
? toolButtons . y + toolButtons . height + Kirigami . Units . largeSpacing
: notificationDrawer . y + notificationDrawer . height + Kirigami . Units . largeSpacing
leftMargin: root . convergenceLeftSurfaceLeftInset + Kirigami . Units . largeSpacing
rightMargin: root . convergenceLeftSurfaceRightInset + Kirigami . Units . largeSpacing
bottomMargin: root . convergenceLeftSurfaceBottomMargin + Kirigami . Units . largeSpacing
2026-05-24 13:48:57 +00:00
}
Kirigami.Theme.colorSet: Kirigami . Theme . View
Kirigami.Theme.inherit: false
PlasmaCalendar . MonthView {
anchors.fill: parent
anchors.margins: Kirigami . Units . smallSpacing
borderOpacity: 0.25
today: calendarClock . dateTime
eventPluginsManager: eventPluginsManager
}
}
2025-03-20 02:06:33 +00:00
}
// notification drawer ui
// separated from the main drawer ui swipe area to prevent scrolling conflicts
NotificationDrawer {
id: notificationDrawer
2026-05-19 07:13:08 +00:00
readonly property bool isConvergence: root . isConvergence
2025-03-20 02:06:33 +00:00
swipeArea: swipeAreaPortrait
actionDrawer: root . actionDrawer
mediaControlsWidget: root . mediaControlsWidget
contentContainer: root
2026-04-16 15:25:37 +00:00
opacity: {
let base = Math . max ( 0 , Math . min ( root . brightnessPressedValue , actionDrawer . offsetResistance / root . minimizedQuickSettingsOffset ) ) ;
return isConvergence ? Math . max ( 0 , Math . min ( 1 , actionDrawer . offset / root . minimizedQuickSettingsOffset ) ) : base ;
}
2025-03-20 02:06:33 +00:00
anchors {
top: parent . top
left: parent . left
right: parent . right
2026-05-24 14:31:28 +00:00
topMargin: isConvergence ? root.convergenceLeftSurfaceTopInset : 0
rightMargin: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait ? 0 : ( isConvergence ? root.convergenceLeftSurfaceRightInset : 360 )
leftMargin: actionDrawer . mode == MobileShell . ActionDrawer . Portrait ? 0 : ( isConvergence ? root.convergenceLeftSurfaceLeftInset : notificationDrawer . minWidthHeight * 0.06 )
2025-03-20 02:06:33 +00:00
}
2026-04-16 15:25:37 +00:00
2026-05-24 14:31:28 +00:00
maximumHeight: isConvergence ? root . convergenceSurfaceHeight * 0.32 : - 1
2026-04-18 17:04:49 +00:00
toolButtonsItem: toolButtons
2025-03-20 02:06:33 +00:00
}
// Secondary swipe area for uses in portrait.
// Covers the surface area of the quick settings panel to allow dismissing or expanding the drawer while also having it over top of the notification list.
MobileShell . SwipeArea {
id: swipeAreaPortrait
mode: MobileShell . SwipeArea . VerticalOnly
anchors {
top: parent . top
left: parent . left
right: parent . right
}
2025-07-17 05:42:35 +00:00
height: root . actionDrawer . mode === MobileShell . ActionDrawer . Portrait ? actionDrawer.offsetResistance : root . height
interactive: root . actionDrawer . mode === MobileShell . ActionDrawer . Portrait
2025-03-20 02:06:33 +00:00
onSwipeStarted: root . startSwipe ( )
onSwipeEnded: root . endSwipe ( )
onSwipeMove: ( totalDeltaX , totalDeltaY , deltaX , deltaY ) = > root . moveSwipe ( totalDeltaX , totalDeltaY , deltaX , deltaY )
onTouchpadScrollStarted: root . startSwipe ( )
onTouchpadScrollEnded: root . endSwipe ( )
onTouchpadScrollMove: ( totalDeltaX , totalDeltaY , deltaX , deltaY ) = > root . moveSwipe ( totalDeltaX , totalDeltaY , deltaX , deltaY )
// Proxy in the layout that switches between landscape and portrait mode.
ColumnLayout {
anchors.fill: parent
2025-07-17 05:42:35 +00:00
visible: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait
2025-03-20 02:06:33 +00:00
LayoutItemProxy { target: contentContainerLoader }
}
}
2024-10-31 05:05:44 +00:00
// Layout that switches between landscape and portrait mode
Loader {
id: contentContainerLoader
2025-03-20 02:06:33 +00:00
Layout.fillWidth: true
Layout.fillHeight: true
2024-10-31 05:05:44 +00:00
readonly property real minimizedQuickSettingsOffset: item ? item.minimizedQuickSettingsOffset : 0
readonly property real maximizedQuickSettingsOffset: item ? item.maximizedQuickSettingsOffset : 0
readonly property real offsetDist: root . actionDrawer . offset - minimizedQuickSettingsOffset
readonly property real totalOffsetDist: maximizedQuickSettingsOffset - minimizedQuickSettingsOffset
readonly property real minimizedToFullProgress: root . actionDrawer . openToPinnedMode ? ( root . actionDrawer . opened ? applyMinMax ( offsetDist / totalOffsetDist ) : 0 ) : 1
asynchronous: true
2025-07-17 05:42:35 +00:00
sourceComponent: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait ? portraitContentContainer : landscapeContentContainer
2024-10-31 05:05:44 +00:00
}
2025-03-20 02:06:33 +00:00
// The portrait content container.
2024-10-31 05:05:44 +00:00
Component {
id: portraitContentContainer
PortraitContentContainer {
actionDrawer: root . actionDrawer
width: root . width
height: root . height
quickSettings: root . quickSettings
statusBar: root . statusBar
mediaControlsWidget: root . mediaControlsWidget
}
}
2025-03-20 02:06:33 +00:00
// The landscape content container.
2024-10-31 05:05:44 +00:00
Component {
id: landscapeContentContainer
LandscapeContentContainer {
actionDrawer: root . actionDrawer
width: root . width
height: root . height
quickSettings: root . quickSettings
statusBar: root . statusBar
}
}
// Components shared between the two layouts.
// This allows us to avoid having to reload the components every time the screen size changes.
2025-07-17 05:42:35 +00:00
property QuickSettings quickSettings: QuickSettings {
2024-10-31 05:05:44 +00:00
id: quickSettings
actionDrawer: root . actionDrawer
quickSettingsModel: root . quickSettingsModel
2025-07-17 05:42:35 +00:00
fullViewProgress: ( root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait ) ? contentContainerLoader.minimizedToFullProgress : 1.0
2024-10-31 05:05:44 +00:00
}
property MobileShell . StatusBar statusBar: MobileShell . StatusBar {
id: statusBar
Kirigami.Theme.colorSet: Kirigami . Theme . Window
Kirigami.Theme.inherit: false
backgroundColor: "transparent"
2025-07-17 05:42:35 +00:00
showSecondRow: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait
2024-10-31 05:05:44 +00:00
showDropShadow: false
2025-07-17 05:42:35 +00:00
showTime: root . actionDrawer . mode == MobileShell . ActionDrawer . Portrait
2024-10-31 05:05:44 +00:00
2026-04-08 18:12:16 +00:00
// Disable system tray on lockscreen to prevent SIGABRT
disableSystemTray: root . actionDrawer . restrictedPermissions
2024-11-14 03:34:01 +00:00
opacity: brightnessPressedValue
2024-10-31 05:05:44 +00:00
}
property MobileShell . MediaControlsWidget mediaControlsWidget: MobileShell . MediaControlsWidget {
id: mediaWidget
2024-11-14 03:34:01 +00:00
opacity: brightnessPressedValue
2024-10-31 05:05:44 +00:00
}
2026-05-24 13:48:57 +00:00
Clock {
id: calendarClock
}
PlasmaCalendar . EventPluginsManager {
id: eventPluginsManager
}
2025-03-20 02:06:33 +00:00
}