mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 00:47:22 +00:00
Replace the fullscreen convergence pointer tracker with edge-local hover areas so dock buttons keep receiving hover, tooltip, and cursor events.
1622 lines
70 KiB
QML
1622 lines
70 KiB
QML
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
|
|
|
import QtQuick
|
|
import QtQuick.Window
|
|
import QtQuick.Layouts
|
|
import QtQuick.Effects
|
|
import QtQuick.Shapes 1.8
|
|
|
|
import org.kde.kirigami as Kirigami
|
|
|
|
import org.kde.plasma.plasmoid 2.0
|
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
|
|
import org.kde.plasma.private.mobileshell as MobileShell
|
|
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
|
import org.kde.plasma.private.mobileshell.windowplugin as WindowPlugin
|
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
|
import org.kde.plasma.private.mobileshell.gamingshellplugin as GamingShell
|
|
|
|
import org.kde.layershell 1.0 as LayerShell
|
|
import org.kde.plasma.private.sessions 2.0
|
|
import org.kde.coreaddons as KCoreAddons
|
|
import org.kde.kcmutils as KCM
|
|
import org.kde.kirigamiaddons.components as KirigamiAddonsComponents
|
|
|
|
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
|
|
|
import "./gaming"
|
|
|
|
import "./private"
|
|
|
|
ContainmentItem {
|
|
id: root
|
|
property var folio: root.plasmoid
|
|
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
|
|
|
// Tracks whether the Game Center grid is visible within gaming mode.
|
|
// If gaming mode is already enabled at startup, open it immediately so
|
|
// the user is never left without controls.
|
|
property bool gameCenterOpen: ShellSettings.Settings.gamingModeEnabled
|
|
property bool showGameCenterHint: false
|
|
|
|
// State saved when gaming mode activates, restored when it deactivates
|
|
property string _savedPowerProfile: ""
|
|
property bool _savedDnd: false
|
|
property bool _gamingSessionActive: false
|
|
|
|
function _applyGamingModeState(enabled) {
|
|
root.gameCenterOpen = enabled
|
|
GamingShell.GamepadManager.active = enabled
|
|
|
|
if (enabled === root._gamingSessionActive) {
|
|
return
|
|
}
|
|
|
|
if (enabled) {
|
|
// Save current state and apply gaming optimizations
|
|
root._savedDnd = MobileShellState.ShellDBusClient.doNotDisturb
|
|
MobileShellState.ShellDBusClient.doNotDisturb = true
|
|
|
|
if (GamingShell.PowerProfileControl.available) {
|
|
root._savedPowerProfile = GamingShell.PowerProfileControl.activeProfile
|
|
GamingShell.PowerProfileControl.activeProfile = "performance"
|
|
}
|
|
|
|
GamingShell.GameModeControl.requestStart()
|
|
root._gamingSessionActive = true
|
|
} else {
|
|
// Restore previous state
|
|
MobileShellState.ShellDBusClient.doNotDisturb = root._savedDnd
|
|
|
|
if (GamingShell.PowerProfileControl.available && root._savedPowerProfile.length > 0) {
|
|
GamingShell.PowerProfileControl.activeProfile = root._savedPowerProfile
|
|
}
|
|
|
|
GamingShell.GameModeControl.requestEnd()
|
|
root._gamingSessionActive = false
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: gameCenterHintTimer
|
|
interval: 2600
|
|
onTriggered: root.showGameCenterHint = false
|
|
}
|
|
|
|
Connections {
|
|
target: ShellSettings.Settings
|
|
function onGamingModeEnabledChanged() {
|
|
root._applyGamingModeState(ShellSettings.Settings.gamingModeEnabled)
|
|
}
|
|
}
|
|
|
|
// Gamepad Guide button toggles Game Center overlay
|
|
Connections {
|
|
target: GamingShell.GamepadManager
|
|
enabled: ShellSettings.Settings.gamingModeEnabled
|
|
function onButtonPressed(button, gamepadIndex) {
|
|
if (button === GamingShell.GamepadManager.ButtonGuide) {
|
|
root.gameCenterOpen = !root.gameCenterOpen
|
|
}
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
root._applyGamingModeState(ShellSettings.Settings.gamingModeEnabled)
|
|
folio.FolioSettings.load();
|
|
folio.FavouritesModel.load();
|
|
folio.PageListModel.load();
|
|
}
|
|
|
|
property MobileShell.MaskManager maskManager: MobileShell.MaskManager {
|
|
height: root.height
|
|
width: root.width
|
|
}
|
|
|
|
property MobileShell.MaskManager frontMaskManager: MobileShell.MaskManager {
|
|
height: root.height
|
|
width: root.width
|
|
}
|
|
|
|
// wallpaper blur layer
|
|
MobileShell.BlurEffect {
|
|
id: wallpaperBlur
|
|
active: folio.FolioSettings.wallpaperBlurEffect > 0
|
|
anchors.fill: parent
|
|
sourceLayer: Plasmoid.wallpaperGraphicsObject
|
|
maskSourceLayer: folio.FolioSettings.wallpaperBlurEffect > 1 ? maskManager.maskLayer : null
|
|
|
|
fullBlur: Math.min(1,
|
|
Math.max(
|
|
1 - homeScreen.contentOpacity,
|
|
// Convergence: no blur for popup drawer
|
|
ShellSettings.Settings.convergenceModeEnabled ? 0 : folio.HomeScreenState.appDrawerOpenProgress * 2,
|
|
folio.HomeScreenState.searchWidgetOpenProgress * 1.5, // blur faster during swipe
|
|
folio.HomeScreenState.folderOpenProgress
|
|
)
|
|
)
|
|
}
|
|
|
|
WindowPlugin.WindowMaximizedTracker {
|
|
id: windowMaximizedTracker
|
|
screenGeometry: Plasmoid.containment.screenGeometry
|
|
}
|
|
|
|
// In gaming mode, reopen Game Center when the last window goes away
|
|
// so the user is never stranded on a bare wallpaper.
|
|
Connections {
|
|
target: windowMaximizedTracker
|
|
enabled: ShellSettings.Settings.gamingModeEnabled
|
|
function onShowingWindowChanged() {
|
|
if (!windowMaximizedTracker.showingWindow && !root.gameCenterOpen) {
|
|
root.gameCenterOpen = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close app drawer when a new window appears
|
|
Connections {
|
|
target: WindowPlugin.WindowUtil
|
|
function onWindowCreated() {
|
|
if (folio.HomeScreenState.viewState === Folio.HomeScreenState.AppDrawerView) {
|
|
folio.HomeScreenState.closeAppDrawer();
|
|
}
|
|
}
|
|
}
|
|
|
|
function homeAction() {
|
|
const isInWindow = (!WindowPlugin.WindowUtil.isShowingDesktop && windowMaximizedTracker.showingWindow);
|
|
|
|
// Always close action drawer
|
|
if (MobileShellState.ShellDBusClient.isActionDrawerOpen) {
|
|
MobileShellState.ShellDBusClient.closeActionDrawer();
|
|
}
|
|
|
|
if (ShellSettings.Settings.gamingModeEnabled) {
|
|
// In gaming mode Home/Menu should reopen the Game Center overlay.
|
|
root.gameCenterOpen = true;
|
|
return;
|
|
}
|
|
|
|
if (ShellSettings.Settings.convergenceModeEnabled) {
|
|
// Convergence: toggle the app drawer as a layer-shell overlay
|
|
// without disturbing open windows.
|
|
switch (folio.HomeScreenState.viewState) {
|
|
case Folio.HomeScreenState.AppDrawerView:
|
|
folio.HomeScreenState.closeAppDrawer();
|
|
break;
|
|
case Folio.HomeScreenState.FolderView:
|
|
folio.HomeScreenState.closeFolder();
|
|
break;
|
|
case Folio.HomeScreenState.SearchWidgetView:
|
|
folio.HomeScreenState.closeSearchWidget();
|
|
break;
|
|
case Folio.HomeScreenState.SettingsView:
|
|
folio.HomeScreenState.closeSettingsView();
|
|
break;
|
|
default:
|
|
folio.HomeScreenState.openAppDrawer();
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (isInWindow) {
|
|
folio.HomeScreenState.closeFolder();
|
|
folio.HomeScreenState.closeSearchWidget();
|
|
folio.HomeScreenState.closeAppDrawer();
|
|
folio.HomeScreenState.goToPage(0, false);
|
|
|
|
WindowPlugin.WindowUtil.minimizeAll();
|
|
|
|
// Always ensure settings view is closed
|
|
if (folio.HomeScreenState.viewState == Folio.HomeScreenState.SettingsView) {
|
|
folio.HomeScreenState.closeSettingsView();
|
|
}
|
|
|
|
} else { // If we are already on the homescreen
|
|
switch (folio.HomeScreenState.viewState) {
|
|
case Folio.HomeScreenState.PageView:
|
|
if (folio.HomeScreenState.currentPage === 0) {
|
|
folio.HomeScreenState.openAppDrawer();
|
|
} else {
|
|
folio.HomeScreenState.goToPage(0, false);
|
|
}
|
|
break;
|
|
case Folio.HomeScreenState.AppDrawerView:
|
|
folio.HomeScreenState.closeAppDrawer();
|
|
break;
|
|
case Folio.HomeScreenState.SearchWidgetView:
|
|
folio.HomeScreenState.closeSearchWidget();
|
|
break;
|
|
case Folio.HomeScreenState.FolderView:
|
|
folio.HomeScreenState.closeFolder();
|
|
break;
|
|
case Folio.HomeScreenState.SettingsView:
|
|
folio.HomeScreenState.closeSettingsView();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Plasmoid.onActivated: homeAction()
|
|
|
|
Rectangle {
|
|
id: appDrawerBackground
|
|
anchors.fill: parent
|
|
// Convergence: no scrim (popup has own background); mobile: dark scrim
|
|
color: ShellSettings.Settings.convergenceModeEnabled
|
|
? "transparent"
|
|
: Qt.rgba(0, 0, 0, 0.6)
|
|
|
|
opacity: folio.HomeScreenState.appDrawerOpenProgress
|
|
}
|
|
|
|
Rectangle {
|
|
id: searchWidgetBackground
|
|
anchors.fill: parent
|
|
color: Qt.rgba(0, 0, 0, 0.3)
|
|
|
|
opacity: folio.HomeScreenState.searchWidgetOpenProgress
|
|
}
|
|
|
|
Rectangle {
|
|
id: settingsViewBackground
|
|
anchors.fill: parent
|
|
color: Qt.rgba(0, 0, 0, 0.3)
|
|
|
|
opacity: folio.HomeScreenState.settingsOpenProgress
|
|
}
|
|
|
|
// Unified convergence chrome — renders the visible top bar, workspace
|
|
// frame, and dock in one mapped surface so they appear together.
|
|
// Invisible reserver surfaces in the panel/taskpanel containments still
|
|
// provide the exclusive zones that shrink KWin's MaximizeArea.
|
|
Window {
|
|
id: convergenceChrome
|
|
readonly property bool active: ShellSettings.Settings.convergenceModeEnabled
|
|
&& !ShellSettings.Settings.gamingModeEnabled
|
|
&& !folio.overviewActive
|
|
|
|
visible: active
|
|
color: "transparent"
|
|
width: Screen.width
|
|
height: Screen.height
|
|
|
|
LayerShell.Window.scope: "convergence-chrome"
|
|
LayerShell.Window.layer: LayerShell.Window.LayerTop
|
|
LayerShell.Window.anchors: LayerShell.Window.AnchorTop | LayerShell.Window.AnchorBottom
|
|
| LayerShell.Window.AnchorLeft | LayerShell.Window.AnchorRight
|
|
LayerShell.Window.exclusionZone: -1
|
|
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityOnDemand
|
|
|
|
// Auto-hide: slide dock content off-screen when a window is
|
|
// maximized. The reveal strip at the screen edge brings it back.
|
|
property real dockOffset: 0
|
|
property bool inputRegionInitialized: false
|
|
readonly property real topBarHeight: MobileShell.Constants.topPanelHeight
|
|
readonly property real dockHeight: MobileShell.Constants.convergenceDockHeight
|
|
readonly property real frameThickness: MobileShell.Constants.convergenceWorkspaceFrameThickness
|
|
readonly property real topBarHitHeight: topBarHeight + frameThickness
|
|
readonly property real frameRadius: Math.min(MobileShell.Constants.convergenceWorkspaceFrameRadius, Math.max(0, Math.min(workAreaWidth, workAreaHeight) / 2))
|
|
readonly property real workAreaX: frameThickness
|
|
readonly property real workAreaY: topBarHitHeight
|
|
readonly property real workAreaWidth: Math.max(0, width - frameThickness * 2)
|
|
readonly property real workAreaHeight: Math.max(0, height - topBarHeight - dockHeight - frameThickness * 2)
|
|
readonly property real leftEdgeHotzoneWidth: Math.max(frameThickness, Math.round(Kirigami.Units.gridUnit * 0.7))
|
|
readonly property real rightEdgeHotzoneWidth: leftEdgeHotzoneWidth
|
|
readonly property real leftLauncherWidth: Math.min(Kirigami.Units.gridUnit * 22, width * 0.42)
|
|
readonly property real leftLauncherHeight: Math.min(Kirigami.Units.gridUnit * 16, workAreaHeight * 0.66)
|
|
readonly property bool leftLauncherEnabled: root.folio.HomeScreenState.appDrawerOpenProgress <= 0
|
|
readonly property real layoutMenuWidth: Math.min(Kirigami.Units.gridUnit * 16, width * 0.34)
|
|
readonly property int layoutMenuWindowCount: Math.max(0, ShellSettings.Settings.dynamicTilingLayoutWindowCount)
|
|
readonly property bool layoutMenuEnabled: ShellSettings.Settings.dynamicTilingEnabled
|
|
&& layoutMenuWindowCount >= 2
|
|
&& root.folio.HomeScreenState.appDrawerOpenProgress <= 0
|
|
readonly property real leftFrameBulgeIdleDepth: Math.max(frameThickness * 0.45, Kirigami.Units.gridUnit * 0.16)
|
|
readonly property real leftFrameBulgeHoverDepth: 0
|
|
property real leftFrameBulgeDepth: !leftLauncherEnabled || leftLauncherOpen || leftEdgeHovered
|
|
? leftFrameBulgeHoverDepth
|
|
: leftFrameBulgeIdleDepth
|
|
property real rightFrameBulgeDepth: !layoutMenuEnabled || layoutMenuOpen || rightEdgeHovered
|
|
? leftFrameBulgeHoverDepth
|
|
: leftFrameBulgeIdleDepth
|
|
// Long, thin thickening of the lower-left workspace wall. Vertical
|
|
// tangents at all three anchors keep the curve smooth as it blends
|
|
// into the straight wall above and below.
|
|
readonly property real leftFrameBulgeEffectiveDepth: Math.max(leftFrameBulgeDepth, 0.01)
|
|
readonly property real leftFrameBulgeApexX: workAreaX + leftFrameBulgeEffectiveDepth
|
|
readonly property real leftFrameBulgeHalfLength: Kirigami.Units.gridUnit * 7.5
|
|
readonly property real leftFrameBulgeApexY: workAreaY + workAreaHeight * 0.7
|
|
readonly property real leftFrameBulgeEdgeTopY: leftFrameBulgeApexY - leftFrameBulgeHalfLength
|
|
readonly property real leftFrameBulgeEdgeBottomY: leftFrameBulgeApexY + leftFrameBulgeHalfLength
|
|
// Bezier control-handle length along the vertical tangent at each
|
|
// anchor. ~0.55 of the half-length gives a clean, taut oval profile.
|
|
readonly property real leftFrameBulgeTangent: leftFrameBulgeHalfLength * 0.55
|
|
readonly property real rightFrameBulgeEffectiveDepth: Math.max(rightFrameBulgeDepth, 0.01)
|
|
readonly property real rightFrameBulgeApexX: workAreaX + workAreaWidth - rightFrameBulgeEffectiveDepth
|
|
readonly property real rightFrameBulgeHalfLength: leftFrameBulgeHalfLength
|
|
readonly property real rightFrameBulgeApexY: leftFrameBulgeApexY
|
|
readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength
|
|
readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength
|
|
readonly property real rightFrameBulgeTangent: rightFrameBulgeHalfLength * 0.55
|
|
readonly property color chromeColor: Kirigami.Theme.backgroundColor
|
|
readonly property color edgeColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
readonly property int dockAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
|
|
|
|
// Height of the input-receive strip kept at the screen edge when
|
|
// the dock is hidden. Matches the navigation panel convention.
|
|
readonly property real revealStripHeight: MobileShell.Constants.convergenceDockRevealHeight
|
|
|
|
// True once the hover-reveal timer fires; cleared on hover-exit.
|
|
property bool hoverRevealing: false
|
|
property bool leftEdgeHovered: false
|
|
property bool leftLauncherHovered: false
|
|
property bool leftLauncherOpen: false
|
|
property bool rightEdgeHovered: false
|
|
property bool layoutMenuHovered: false
|
|
property bool layoutMenuOpen: false
|
|
|
|
readonly property bool shouldHide: ShellSettings.Settings.autoHidePanelsEnabled
|
|
&& windowMaximizedTracker.showingWindow && !hoverRevealing
|
|
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
|
|
|
function updateInputRegion() {
|
|
const topBarRegion = Qt.rect(0, 0, width, topBarHitHeight)
|
|
const leftEdgeRegion = Qt.rect(0, topBarHitHeight, leftEdgeHotzoneWidth, Math.max(0, height - topBarHitHeight - dockHeight))
|
|
const rightEdgeRegion = Qt.rect(width - rightEdgeHotzoneWidth, topBarHitHeight, rightEdgeHotzoneWidth, Math.max(0, height - topBarHitHeight - dockHeight))
|
|
const leftLauncherRegion = Qt.rect(0,
|
|
Math.max(0, height - dockHeight - leftLauncherHeight),
|
|
leftLauncherWidth,
|
|
leftLauncherHeight)
|
|
const layoutMenuRegion = Qt.rect(rightLayoutMenu.x,
|
|
rightLayoutMenu.y,
|
|
rightLayoutMenu.width,
|
|
rightLayoutMenu.height)
|
|
let regions = [topBarRegion, leftEdgeRegion]
|
|
|
|
if (layoutMenuEnabled) {
|
|
regions.push(rightEdgeRegion)
|
|
}
|
|
|
|
if (shouldHide && dockOffset >= dockHeight) {
|
|
regions.push(Qt.rect(0, height - revealStripHeight, width, revealStripHeight))
|
|
} else {
|
|
regions.push(Qt.rect(0, height - dockHeight, width, dockHeight))
|
|
}
|
|
|
|
if (leftLauncherOpen) {
|
|
regions.push(leftLauncherRegion)
|
|
}
|
|
|
|
if (layoutMenuEnabled && layoutMenuOpen) {
|
|
regions.push(layoutMenuRegion)
|
|
}
|
|
|
|
MobileShell.ShellUtil.setInputRegions(convergenceChrome, regions)
|
|
}
|
|
|
|
function launchStorageId(storageId) {
|
|
if (!storageId || String(storageId).length === 0) {
|
|
return
|
|
}
|
|
|
|
var normalizedId = String(storageId)
|
|
if (!normalizedId.endsWith(".desktop")) {
|
|
normalizedId += ".desktop"
|
|
}
|
|
MobileShell.AppLaunch.launchOrActivateApp(normalizedId)
|
|
}
|
|
|
|
function refreshLeftLauncherVisibility() {
|
|
if (!leftLauncherEnabled) {
|
|
leftLauncherCloseTimer.stop()
|
|
leftEdgeHovered = false
|
|
leftLauncherHovered = false
|
|
leftLauncherOpen = false
|
|
inputRegionTimer.restart()
|
|
return
|
|
}
|
|
|
|
if (leftEdgeHovered || leftLauncherHovered) {
|
|
leftLauncherCloseTimer.stop()
|
|
leftLauncherOpen = true
|
|
} else {
|
|
leftLauncherCloseTimer.restart()
|
|
}
|
|
inputRegionTimer.restart()
|
|
}
|
|
|
|
function refreshLayoutMenuVisibility() {
|
|
if (!layoutMenuEnabled) {
|
|
layoutMenuCloseTimer.stop()
|
|
rightEdgeHovered = false
|
|
layoutMenuHovered = false
|
|
layoutMenuOpen = false
|
|
inputRegionTimer.restart()
|
|
return
|
|
}
|
|
|
|
if (rightEdgeHovered || layoutMenuHovered) {
|
|
layoutMenuCloseTimer.stop()
|
|
layoutMenuOpen = true
|
|
} else {
|
|
layoutMenuCloseTimer.restart()
|
|
}
|
|
inputRegionTimer.restart()
|
|
}
|
|
|
|
onActiveChanged: {
|
|
hoverRevealTimer.stop()
|
|
hoverRevealing = false
|
|
inputRegionInitialized = false
|
|
dockOffset = shouldHide ? dockHeight : 0
|
|
inputRegionTimer.restart()
|
|
}
|
|
|
|
onShouldHideChanged: {
|
|
if (shouldHide) {
|
|
dockOffset = dockHeight
|
|
} else {
|
|
dockOffset = 0
|
|
}
|
|
inputRegionTimer.restart()
|
|
}
|
|
onLeftLauncherEnabledChanged: refreshLeftLauncherVisibility()
|
|
onLayoutMenuEnabledChanged: refreshLayoutMenuVisibility()
|
|
|
|
// Narrow the input region to a strip at the screen edge when hidden
|
|
// so that app controls near the bottom edge are not accidentally
|
|
// intercepted. Mirrors the same pattern used by NavigationPanel.
|
|
onDockOffsetChanged: {
|
|
inputRegionTimer.restart()
|
|
}
|
|
onWidthChanged: inputRegionTimer.restart()
|
|
onHeightChanged: inputRegionTimer.restart()
|
|
onFrameSwapped: {
|
|
if (!inputRegionInitialized) {
|
|
inputRegionInitialized = true
|
|
inputRegionTimer.restart()
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: inputRegionTimer
|
|
interval: 0
|
|
repeat: false
|
|
onTriggered: convergenceChrome.updateInputRegion()
|
|
}
|
|
|
|
// Delay reveal briefly so a quick edge graze does not pop the
|
|
// dock up mid-interaction with the underlying application.
|
|
Timer {
|
|
id: hoverRevealTimer
|
|
interval: Kirigami.Units.shortDuration
|
|
repeat: false
|
|
onTriggered: convergenceChrome.hoverRevealing = true
|
|
}
|
|
|
|
Timer {
|
|
id: leftLauncherCloseTimer
|
|
interval: Kirigami.Units.shortDuration
|
|
repeat: false
|
|
onTriggered: {
|
|
if (!convergenceChrome.leftEdgeHovered
|
|
&& !convergenceChrome.leftLauncherHovered
|
|
&& convergenceChrome.leftLauncherOpen) {
|
|
convergenceChrome.leftLauncherOpen = false
|
|
inputRegionTimer.restart()
|
|
}
|
|
}
|
|
}
|
|
|
|
Timer {
|
|
id: layoutMenuCloseTimer
|
|
interval: Kirigami.Units.shortDuration
|
|
repeat: false
|
|
onTriggered: {
|
|
if (!convergenceChrome.rightEdgeHovered
|
|
&& !convergenceChrome.layoutMenuHovered
|
|
&& convergenceChrome.layoutMenuOpen) {
|
|
convergenceChrome.layoutMenuOpen = false
|
|
inputRegionTimer.restart()
|
|
}
|
|
}
|
|
}
|
|
|
|
Behavior on dockOffset {
|
|
MobileShell.MotionNumberAnimation {
|
|
type: MobileShell.Motion.SpatialDefault
|
|
duration: convergenceChrome.dockAnimationDuration
|
|
}
|
|
}
|
|
|
|
Behavior on leftFrameBulgeDepth {
|
|
MobileShell.MotionNumberAnimation {
|
|
type: MobileShell.Motion.SpatialDefault
|
|
duration: root.shortAnimationDuration
|
|
}
|
|
}
|
|
|
|
Behavior on rightFrameBulgeDepth {
|
|
MobileShell.MotionNumberAnimation {
|
|
type: MobileShell.Motion.SpatialDefault
|
|
duration: root.shortAnimationDuration
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: topBarSurface
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
height: convergenceChrome.topBarHeight
|
|
color: convergenceChrome.chromeColor
|
|
|
|
MobileShell.StatusBar {
|
|
anchors.fill: parent
|
|
showSecondRow: false
|
|
showTime: true
|
|
backgroundColor: "transparent"
|
|
}
|
|
}
|
|
|
|
Shape {
|
|
id: workspaceFrame
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
|
|
ShapePath {
|
|
fillColor: convergenceChrome.chromeColor
|
|
fillRule: ShapePath.OddEvenFill
|
|
strokeWidth: 0
|
|
|
|
startX: 0
|
|
startY: convergenceChrome.topBarHeight
|
|
PathLine { x: convergenceChrome.width; y: convergenceChrome.topBarHeight }
|
|
PathLine { x: convergenceChrome.width; y: convergenceChrome.height - convergenceChrome.dockHeight }
|
|
PathLine { x: 0; y: convergenceChrome.height - convergenceChrome.dockHeight }
|
|
PathLine { x: 0; y: convergenceChrome.topBarHeight }
|
|
|
|
PathMove { x: convergenceChrome.workAreaX + convergenceChrome.frameRadius; y: convergenceChrome.workAreaY }
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth - convergenceChrome.frameRadius; y: convergenceChrome.workAreaY }
|
|
PathArc { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth; y: convergenceChrome.workAreaY + convergenceChrome.frameRadius; radiusX: convergenceChrome.frameRadius; radiusY: convergenceChrome.frameRadius }
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth; y: convergenceChrome.rightFrameBulgeEdgeTopY }
|
|
PathCubic {
|
|
x: convergenceChrome.rightFrameBulgeApexX
|
|
y: convergenceChrome.rightFrameBulgeApexY
|
|
control1X: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
control1Y: convergenceChrome.rightFrameBulgeEdgeTopY + convergenceChrome.rightFrameBulgeTangent
|
|
control2X: convergenceChrome.rightFrameBulgeApexX
|
|
control2Y: convergenceChrome.rightFrameBulgeApexY - convergenceChrome.rightFrameBulgeTangent
|
|
}
|
|
PathCubic {
|
|
x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
y: convergenceChrome.rightFrameBulgeEdgeBottomY
|
|
control1X: convergenceChrome.rightFrameBulgeApexX
|
|
control1Y: convergenceChrome.rightFrameBulgeApexY + convergenceChrome.rightFrameBulgeTangent
|
|
control2X: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
control2Y: convergenceChrome.rightFrameBulgeEdgeBottomY - convergenceChrome.rightFrameBulgeTangent
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight - convergenceChrome.frameRadius }
|
|
PathArc { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth - convergenceChrome.frameRadius; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight; radiusX: convergenceChrome.frameRadius; radiusY: convergenceChrome.frameRadius }
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.frameRadius; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight }
|
|
PathArc { x: convergenceChrome.workAreaX; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight - convergenceChrome.frameRadius; radiusX: convergenceChrome.frameRadius; radiusY: convergenceChrome.frameRadius }
|
|
PathLine { x: convergenceChrome.workAreaX; y: convergenceChrome.leftFrameBulgeEdgeBottomY }
|
|
PathCubic {
|
|
x: convergenceChrome.leftFrameBulgeApexX
|
|
y: convergenceChrome.leftFrameBulgeApexY
|
|
control1X: convergenceChrome.workAreaX
|
|
control1Y: convergenceChrome.leftFrameBulgeEdgeBottomY - convergenceChrome.leftFrameBulgeTangent
|
|
control2X: convergenceChrome.leftFrameBulgeApexX
|
|
control2Y: convergenceChrome.leftFrameBulgeApexY + convergenceChrome.leftFrameBulgeTangent
|
|
}
|
|
PathCubic {
|
|
x: convergenceChrome.workAreaX
|
|
y: convergenceChrome.leftFrameBulgeEdgeTopY
|
|
control1X: convergenceChrome.leftFrameBulgeApexX
|
|
control1Y: convergenceChrome.leftFrameBulgeApexY - convergenceChrome.leftFrameBulgeTangent
|
|
control2X: convergenceChrome.workAreaX
|
|
control2Y: convergenceChrome.leftFrameBulgeEdgeTopY + convergenceChrome.leftFrameBulgeTangent
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX; y: convergenceChrome.workAreaY + convergenceChrome.frameRadius }
|
|
PathArc { x: convergenceChrome.workAreaX + convergenceChrome.frameRadius; y: convergenceChrome.workAreaY; radiusX: convergenceChrome.frameRadius; radiusY: convergenceChrome.frameRadius }
|
|
}
|
|
}
|
|
|
|
Shape {
|
|
id: workspaceFrameBorder
|
|
anchors.fill: parent
|
|
preferredRendererType: Shape.CurveRenderer
|
|
|
|
ShapePath {
|
|
fillColor: "transparent"
|
|
strokeColor: convergenceChrome.edgeColor
|
|
strokeWidth: 0.85
|
|
joinStyle: ShapePath.RoundJoin
|
|
|
|
startX: convergenceChrome.workAreaX + convergenceChrome.frameRadius
|
|
startY: convergenceChrome.workAreaY
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth - convergenceChrome.frameRadius; y: convergenceChrome.workAreaY }
|
|
PathQuad {
|
|
x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
y: convergenceChrome.workAreaY + convergenceChrome.frameRadius
|
|
controlX: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
controlY: convergenceChrome.workAreaY
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth; y: convergenceChrome.rightFrameBulgeEdgeTopY }
|
|
PathCubic {
|
|
x: convergenceChrome.rightFrameBulgeApexX
|
|
y: convergenceChrome.rightFrameBulgeApexY
|
|
control1X: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
control1Y: convergenceChrome.rightFrameBulgeEdgeTopY + convergenceChrome.rightFrameBulgeTangent
|
|
control2X: convergenceChrome.rightFrameBulgeApexX
|
|
control2Y: convergenceChrome.rightFrameBulgeApexY - convergenceChrome.rightFrameBulgeTangent
|
|
}
|
|
PathCubic {
|
|
x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
y: convergenceChrome.rightFrameBulgeEdgeBottomY
|
|
control1X: convergenceChrome.rightFrameBulgeApexX
|
|
control1Y: convergenceChrome.rightFrameBulgeApexY + convergenceChrome.rightFrameBulgeTangent
|
|
control2X: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
control2Y: convergenceChrome.rightFrameBulgeEdgeBottomY - convergenceChrome.rightFrameBulgeTangent
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight - convergenceChrome.frameRadius }
|
|
PathQuad {
|
|
x: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth - convergenceChrome.frameRadius
|
|
y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight
|
|
controlX: convergenceChrome.workAreaX + convergenceChrome.workAreaWidth
|
|
controlY: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX + convergenceChrome.frameRadius; y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight }
|
|
PathQuad {
|
|
x: convergenceChrome.workAreaX
|
|
y: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight - convergenceChrome.frameRadius
|
|
controlX: convergenceChrome.workAreaX
|
|
controlY: convergenceChrome.workAreaY + convergenceChrome.workAreaHeight
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX; y: convergenceChrome.leftFrameBulgeEdgeBottomY }
|
|
PathCubic {
|
|
x: convergenceChrome.leftFrameBulgeApexX
|
|
y: convergenceChrome.leftFrameBulgeApexY
|
|
control1X: convergenceChrome.workAreaX
|
|
control1Y: convergenceChrome.leftFrameBulgeEdgeBottomY - convergenceChrome.leftFrameBulgeTangent
|
|
control2X: convergenceChrome.leftFrameBulgeApexX
|
|
control2Y: convergenceChrome.leftFrameBulgeApexY + convergenceChrome.leftFrameBulgeTangent
|
|
}
|
|
PathCubic {
|
|
x: convergenceChrome.workAreaX
|
|
y: convergenceChrome.leftFrameBulgeEdgeTopY
|
|
control1X: convergenceChrome.leftFrameBulgeApexX
|
|
control1Y: convergenceChrome.leftFrameBulgeApexY - convergenceChrome.leftFrameBulgeTangent
|
|
control2X: convergenceChrome.workAreaX
|
|
control2Y: convergenceChrome.leftFrameBulgeEdgeTopY + convergenceChrome.leftFrameBulgeTangent
|
|
}
|
|
PathLine { x: convergenceChrome.workAreaX; y: convergenceChrome.workAreaY + convergenceChrome.frameRadius }
|
|
PathQuad {
|
|
x: convergenceChrome.workAreaX + convergenceChrome.frameRadius
|
|
y: convergenceChrome.workAreaY
|
|
controlX: convergenceChrome.workAreaX
|
|
controlY: convergenceChrome.workAreaY
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: dockSurface
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
height: convergenceChrome.dockHeight
|
|
color: "transparent"
|
|
|
|
HoverHandler {
|
|
id: dockHoverHandler
|
|
onHoveredChanged: {
|
|
if (hovered) {
|
|
hoverRevealTimer.start()
|
|
} else {
|
|
hoverRevealTimer.stop()
|
|
convergenceChrome.hoverRevealing = false
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
visible: !convergenceChrome.shouldHide || convergenceChrome.dockOffset < convergenceChrome.dockHeight
|
|
color: convergenceChrome.chromeColor
|
|
}
|
|
|
|
FavouritesBar {
|
|
id: dockOverlayBar
|
|
anchors.fill: parent
|
|
folio: root.folio
|
|
maskManager: root.maskManager
|
|
homeScreen: folioHomeScreen
|
|
suppressRunningTasks: runningAppsPanel.visible
|
|
transform: Translate { y: convergenceChrome.dockOffset }
|
|
// Dock is an opaque panel — use Window colorset so all content
|
|
// (labels, hover highlights, icon tints) follows the system theme
|
|
// instead of the containment's Complementary wallpaper context.
|
|
Kirigami.Theme.inherit: false
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: leftEdgeStrip
|
|
|
|
anchors.left: parent.left
|
|
anchors.top: topBarSurface.bottom
|
|
anchors.bottom: dockSurface.top
|
|
width: convergenceChrome.leftEdgeHotzoneWidth
|
|
|
|
MouseArea {
|
|
id: leftEdgeHoverArea
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.NoButton
|
|
enabled: convergenceChrome.leftLauncherEnabled
|
|
hoverEnabled: enabled
|
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
|
|
onContainsMouseChanged: {
|
|
convergenceChrome.leftEdgeHovered = containsMouse
|
|
convergenceChrome.refreshLeftLauncherVisibility()
|
|
}
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: rightEdgeStrip
|
|
|
|
anchors.right: parent.right
|
|
anchors.top: topBarSurface.bottom
|
|
anchors.bottom: dockSurface.top
|
|
width: convergenceChrome.layoutMenuEnabled ? convergenceChrome.rightEdgeHotzoneWidth : 0
|
|
|
|
MouseArea {
|
|
id: rightEdgeHoverArea
|
|
anchors.fill: parent
|
|
acceptedButtons: Qt.NoButton
|
|
enabled: convergenceChrome.layoutMenuEnabled
|
|
hoverEnabled: enabled
|
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
|
|
onContainsMouseChanged: {
|
|
convergenceChrome.rightEdgeHovered = containsMouse
|
|
convergenceChrome.refreshLayoutMenuVisibility()
|
|
}
|
|
}
|
|
}
|
|
|
|
DynamicTilingLayoutMenu {
|
|
id: rightLayoutMenu
|
|
|
|
width: convergenceChrome.layoutMenuWidth
|
|
height: preferredHeight
|
|
x: convergenceChrome.width - width
|
|
y: convergenceChrome.height - convergenceChrome.dockHeight - height
|
|
visible: convergenceChrome.layoutMenuOpen
|
|
opacity: convergenceChrome.layoutMenuOpen ? 1 : 0
|
|
maxHeight: convergenceChrome.workAreaHeight * 0.5
|
|
windowCount: convergenceChrome.layoutMenuWindowCount
|
|
currentMode: ShellSettings.Settings.dynamicTilingLayoutMode
|
|
surfaceColor: convergenceChrome.chromeColor
|
|
animationDuration: root.shortAnimationDuration
|
|
|
|
HoverHandler {
|
|
enabled: convergenceChrome.layoutMenuOpen
|
|
|
|
onHoveredChanged: {
|
|
convergenceChrome.layoutMenuHovered = hovered
|
|
convergenceChrome.refreshLayoutMenuVisibility()
|
|
}
|
|
}
|
|
|
|
transform: Translate {
|
|
y: convergenceChrome.layoutMenuOpen ? 0 : Kirigami.Units.gridUnit
|
|
x: convergenceChrome.layoutMenuOpen ? 0 : rightLayoutMenu.width - convergenceChrome.rightEdgeHotzoneWidth
|
|
}
|
|
|
|
onLayoutModeRequested: (mode) => {
|
|
if (ShellSettings.Settings.requestDynamicTilingLayoutMode !== undefined) {
|
|
ShellSettings.Settings.requestDynamicTilingLayoutMode(mode)
|
|
}
|
|
}
|
|
|
|
onDismissRequested: {
|
|
convergenceChrome.layoutMenuOpen = false
|
|
inputRegionTimer.restart()
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: leftEdgeLauncher
|
|
|
|
width: convergenceChrome.leftLauncherWidth
|
|
height: convergenceChrome.leftLauncherHeight
|
|
x: 0
|
|
y: convergenceChrome.height - convergenceChrome.dockHeight - height
|
|
visible: convergenceChrome.leftLauncherOpen
|
|
opacity: convergenceChrome.leftLauncherOpen ? 1 : 0
|
|
clip: true
|
|
|
|
transform: Translate {
|
|
y: convergenceChrome.leftLauncherOpen ? 0 : Kirigami.Units.gridUnit
|
|
x: convergenceChrome.leftLauncherOpen ? 0 : -leftEdgeLauncher.width + convergenceChrome.leftEdgeHotzoneWidth
|
|
}
|
|
|
|
Behavior on opacity {
|
|
MobileShell.MotionNumberAnimation {
|
|
type: MobileShell.Motion.EffectsFast
|
|
duration: root.shortAnimationDuration
|
|
}
|
|
}
|
|
|
|
readonly property real cornerRadius: Math.min(MobileShell.Constants.convergenceWorkspaceFrameRadius, height * 0.24)
|
|
|
|
HoverHandler {
|
|
enabled: convergenceChrome.leftLauncherOpen
|
|
|
|
onHoveredChanged: {
|
|
convergenceChrome.leftLauncherHovered = hovered
|
|
convergenceChrome.refreshLeftLauncherVisibility()
|
|
}
|
|
}
|
|
|
|
Shape {
|
|
id: leftLauncherSurface
|
|
anchors.fill: parent
|
|
|
|
ShapePath {
|
|
fillColor: convergenceChrome.chromeColor
|
|
strokeWidth: 0
|
|
|
|
startX: 0
|
|
startY: 0
|
|
PathLine { x: leftEdgeLauncher.width - leftEdgeLauncher.cornerRadius; y: 0 }
|
|
PathArc {
|
|
x: leftEdgeLauncher.width
|
|
y: leftEdgeLauncher.cornerRadius
|
|
radiusX: leftEdgeLauncher.cornerRadius
|
|
radiusY: leftEdgeLauncher.cornerRadius
|
|
}
|
|
PathLine { x: leftEdgeLauncher.width; y: leftEdgeLauncher.height }
|
|
PathLine { x: 0; y: leftEdgeLauncher.height }
|
|
PathLine { x: 0; y: 0 }
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
spacing: Kirigami.Units.gridUnit * 0.65
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
PlasmaComponents.Label {
|
|
Layout.fillWidth: true
|
|
text: i18n("Recently Used")
|
|
font.weight: Font.Medium
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
ListView {
|
|
id: recentAppsList
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 6.8
|
|
clip: true
|
|
interactive: false
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
model: folio.RecentApplicationsModel
|
|
|
|
delegate: MouseArea {
|
|
required property int index
|
|
required property var model
|
|
|
|
readonly property var delegateObject: model.delegate
|
|
readonly property var application: delegateObject ? delegateObject.application : null
|
|
readonly property bool validEntry: index < 5 && application !== null
|
|
|
|
width: recentAppsList.width
|
|
height: validEntry ? Kirigami.Units.gridUnit * 1.35 : 0
|
|
enabled: validEntry
|
|
hoverEnabled: validEntry
|
|
cursorShape: validEntry ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
|
|
onClicked: {
|
|
if (application) {
|
|
convergenceChrome.launchStorageId(application.storageId)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: parent.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
|
: "transparent"
|
|
}
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
Kirigami.Icon {
|
|
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
|
Layout.preferredHeight: Layout.preferredWidth
|
|
source: application ? application.icon : ""
|
|
}
|
|
|
|
PlasmaComponents.Label {
|
|
Layout.fillWidth: true
|
|
text: application ? application.name : ""
|
|
elide: Text.ElideRight
|
|
maximumLineCount: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
PlasmaComponents.Label {
|
|
Layout.fillWidth: true
|
|
text: i18n("Most Used")
|
|
font.weight: Font.Medium
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
ListView {
|
|
id: favouritesQuickList
|
|
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
clip: true
|
|
interactive: false
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
model: folio.MostUsedApplicationsModel
|
|
|
|
delegate: MouseArea {
|
|
required property int index
|
|
required property var model
|
|
|
|
readonly property var delegateObject: model.delegate
|
|
readonly property var application: delegateObject ? delegateObject.application : null
|
|
readonly property bool validEntry: index < 6 && application !== null
|
|
|
|
width: favouritesQuickList.width
|
|
height: validEntry ? Kirigami.Units.gridUnit * 1.35 : 0
|
|
enabled: validEntry
|
|
hoverEnabled: validEntry
|
|
cursorShape: validEntry ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
|
|
onClicked: {
|
|
if (application) {
|
|
convergenceChrome.launchStorageId(application.storageId)
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: parent.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
|
: "transparent"
|
|
}
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
Kirigami.Icon {
|
|
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
|
Layout.preferredHeight: Layout.preferredWidth
|
|
source: application ? application.icon : ""
|
|
}
|
|
|
|
PlasmaComponents.Label {
|
|
Layout.fillWidth: true
|
|
text: application ? application.name : ""
|
|
elide: Text.ElideRight
|
|
maximumLineCount: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// App-drawer overlay — renders the popup drawer above application
|
|
// windows in convergence mode. Same pattern as the dock overlay:
|
|
// a fullscreen layer-shell surface at LayerTop so that it appears
|
|
// over normal windows without minimizing them.
|
|
Window {
|
|
id: drawerOverlay
|
|
visible: ShellSettings.Settings.convergenceModeEnabled
|
|
&& !ShellSettings.Settings.gamingModeEnabled
|
|
&& folio.HomeScreenState.appDrawerOpenProgress > 0
|
|
color: "transparent"
|
|
width: Screen.width
|
|
height: Screen.height
|
|
|
|
LayerShell.Window.scope: "drawer-overlay"
|
|
LayerShell.Window.layer: LayerShell.Window.LayerTop
|
|
LayerShell.Window.anchors: LayerShell.Window.AnchorTop | LayerShell.Window.AnchorBottom
|
|
| LayerShell.Window.AnchorLeft | LayerShell.Window.AnchorRight
|
|
LayerShell.Window.exclusionZone: -1
|
|
LayerShell.Window.keyboardInteractivity: LayerShell.Window.KeyboardInteractivityOnDemand
|
|
|
|
// Click outside the popup to dismiss
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: folio.HomeScreenState.closeAppDrawer()
|
|
}
|
|
|
|
AppDrawer {
|
|
id: overlayDrawer
|
|
folio: root.folio
|
|
homeScreen: folioHomeScreen
|
|
|
|
readonly property real popupWidth: Math.min(Kirigami.Units.gridUnit * 28, parent.width * 0.5)
|
|
readonly property real dockHeight: MobileShell.Constants.convergenceDockHeight
|
|
readonly property real sideInset: MobileShell.Constants.convergenceWorkspaceFrameThickness
|
|
readonly property real connectedPanelGap: 0
|
|
readonly property real popupTopY: MobileShell.Constants.topPanelHeight
|
|
+ MobileShell.Constants.convergenceWorkspaceFrameThickness
|
|
readonly property real popupBottomY: parent.height
|
|
- dockHeight
|
|
- MobileShell.Constants.convergenceWorkspaceFrameThickness
|
|
readonly property real popupHeight: Math.max(0, popupBottomY - popupTopY)
|
|
|
|
width: popupWidth
|
|
height: popupHeight
|
|
|
|
opacity: folio.HomeScreenState.appDrawerOpenProgress < 0.5
|
|
? 0 : (folio.HomeScreenState.appDrawerOpenProgress - 0.5) * 2
|
|
|
|
property real animationY: (1 - folio.HomeScreenState.appDrawerOpenProgress) * (Kirigami.Units.gridUnit * 2)
|
|
|
|
x: sideInset
|
|
y: (opacity > 0)
|
|
? popupTopY + animationY
|
|
: parent.height
|
|
|
|
headerHeight: Math.round(Kirigami.Units.gridUnit * 3)
|
|
headerItem: AppDrawerHeader {
|
|
id: overlayDrawerHeader
|
|
folio: root.folio
|
|
onReleaseFocusRequested: overlayDrawer.forceActiveFocus()
|
|
}
|
|
|
|
Keys.onPressed: (event) => {
|
|
if (event.text.trim().length > 0) {
|
|
overlayDrawerHeader.addSearchText(event.text);
|
|
overlayDrawerHeader.forceActiveFocus();
|
|
event.accepted = true;
|
|
} else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right
|
|
|| event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
|
|
overlayDrawerHeader.forceActiveFocus();
|
|
event.accepted = true;
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: folio.HomeScreenState
|
|
|
|
function onAppDrawerOpened() {
|
|
folio.ApplicationListSearchModel.categoryFilter = ""
|
|
overlayDrawer.forceActiveFocus()
|
|
}
|
|
}
|
|
}
|
|
|
|
CategoryPanel {
|
|
id: categoryPanel
|
|
folio: root.folio
|
|
|
|
width: Kirigami.Units.gridUnit * 9
|
|
height: overlayDrawer.popupHeight
|
|
x: overlayDrawer.x + overlayDrawer.width + overlayDrawer.connectedPanelGap
|
|
y: overlayDrawer.y
|
|
opacity: overlayDrawer.opacity
|
|
|
|
onCategorySelected: (catId) => {
|
|
folio.ApplicationListSearchModel.categoryFilter = catId
|
|
overlayDrawerHeader.clearSearchText()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: powerPanel
|
|
|
|
// Width: just enough for one icon button + side margins
|
|
readonly property real tileSize: Kirigami.Units.iconSizes.medium + 2 * Kirigami.Units.largeSpacing
|
|
|
|
width: tileSize
|
|
height: overlayDrawer.popupHeight
|
|
x: runningAppsPanel.visible
|
|
? runningAppsPanel.x + runningAppsPanel.width + overlayDrawer.connectedPanelGap
|
|
: categoryPanel.x + categoryPanel.width + overlayDrawer.connectedPanelGap
|
|
y: overlayDrawer.y
|
|
opacity: overlayDrawer.opacity
|
|
radius: 0
|
|
color: "transparent"
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
}
|
|
|
|
KCoreAddons.KUser {
|
|
id: kuser
|
|
}
|
|
|
|
SessionManagement {
|
|
id: powerSession
|
|
}
|
|
|
|
// Close button anchored to top — smaller than power icons
|
|
Rectangle {
|
|
id: closeButton
|
|
anchors.top: parent.top
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
height: Kirigami.Units.iconSizes.smallMedium + 2 * Kirigami.Units.smallSpacing
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: closeArea.containsPress
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
: closeArea.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
: "transparent"
|
|
Kirigami.Icon {
|
|
anchors.centerIn: parent
|
|
width: Kirigami.Units.iconSizes.smallMedium
|
|
height: width
|
|
source: "window-close-symbolic"
|
|
active: closeArea.containsMouse
|
|
isMask: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
PlasmaComponents.ToolTip {
|
|
text: i18n("Close")
|
|
visible: closeArea.containsMouse
|
|
}
|
|
MouseArea {
|
|
id: closeArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: folio.HomeScreenState.closeAppDrawer()
|
|
}
|
|
}
|
|
|
|
// Separator below close button
|
|
Rectangle {
|
|
anchors.top: closeButton.bottom
|
|
anchors.topMargin: Kirigami.Units.smallSpacing
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
height: 1
|
|
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.15)
|
|
}
|
|
|
|
// Power buttons centred vertically in the panel
|
|
Column {
|
|
id: powerColumn
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: width
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: lockArea.containsPress
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
: lockArea.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
: "transparent"
|
|
Kirigami.Icon {
|
|
anchors.centerIn: parent
|
|
width: Kirigami.Units.iconSizes.medium
|
|
height: width
|
|
source: "system-lock-screen"
|
|
active: lockArea.containsMouse
|
|
isMask: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
PlasmaComponents.ToolTip {
|
|
text: i18n("Lock Screen")
|
|
visible: lockArea.containsMouse
|
|
}
|
|
MouseArea {
|
|
id: lockArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
powerSession.lock()
|
|
folio.HomeScreenState.closeAppDrawer()
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: width
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: rebootArea.containsPress
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
: rebootArea.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
: "transparent"
|
|
Kirigami.Icon {
|
|
anchors.centerIn: parent
|
|
width: Kirigami.Units.iconSizes.medium
|
|
height: width
|
|
source: "system-reboot"
|
|
active: rebootArea.containsMouse
|
|
isMask: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
PlasmaComponents.ToolTip {
|
|
text: i18n("Restart")
|
|
visible: rebootArea.containsMouse
|
|
}
|
|
MouseArea {
|
|
id: rebootArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
folio.HomeScreenState.closeAppDrawer()
|
|
powerSession.requestReboot()
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: width
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: shutdownArea.containsPress
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
: shutdownArea.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
: "transparent"
|
|
Kirigami.Icon {
|
|
anchors.centerIn: parent
|
|
width: Kirigami.Units.iconSizes.medium
|
|
height: width
|
|
source: "system-shutdown"
|
|
active: shutdownArea.containsMouse
|
|
isMask: true
|
|
color: Kirigami.Theme.textColor
|
|
}
|
|
PlasmaComponents.ToolTip {
|
|
text: i18n("Shut Down")
|
|
visible: shutdownArea.containsMouse
|
|
}
|
|
MouseArea {
|
|
id: shutdownArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
folio.HomeScreenState.closeAppDrawer()
|
|
powerSession.requestShutdown()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Separator above user avatar
|
|
Rectangle {
|
|
anchors.bottom: userSection.top
|
|
anchors.bottomMargin: Kirigami.Units.smallSpacing
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: Kirigami.Units.smallSpacing
|
|
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
height: 1
|
|
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.15)
|
|
}
|
|
|
|
// User avatar anchored to bottom — tooltip shows name, click opens user settings
|
|
Rectangle {
|
|
id: userSection
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
height: width
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: userArea.containsPress
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.2)
|
|
: userArea.containsMouse
|
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
|
: "transparent"
|
|
|
|
KirigamiAddonsComponents.Avatar {
|
|
anchors.centerIn: parent
|
|
width: Kirigami.Units.iconSizes.medium
|
|
height: width
|
|
source: kuser.faceIconUrl
|
|
name: kuser.fullName || kuser.loginName
|
|
}
|
|
PlasmaComponents.ToolTip {
|
|
text: kuser.fullName || kuser.loginName
|
|
visible: userArea.containsMouse
|
|
}
|
|
MouseArea {
|
|
id: userArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
KCM.KCMLauncher.openSystemSettings("kcm_users")
|
|
folio.HomeScreenState.closeAppDrawer()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RunningAppsPanel {
|
|
id: runningAppsPanel
|
|
folio: root.folio
|
|
|
|
x: categoryPanel.x + categoryPanel.width + overlayDrawer.connectedPanelGap
|
|
y: overlayDrawer.y
|
|
width: Math.max(0, parent.width - x - powerPanel.width - overlayDrawer.sideInset - overlayDrawer.connectedPanelGap)
|
|
height: overlayDrawer.popupHeight
|
|
opacity: overlayDrawer.opacity
|
|
visible: hasTasks && opacity > 0
|
|
|
|
onTaskActivated: folio.HomeScreenState.closeAppDrawer()
|
|
}
|
|
|
|
Shape {
|
|
id: drawerSurface
|
|
x: overlayDrawer.x
|
|
y: overlayDrawer.y
|
|
width: bodyWidth + cornerRadius
|
|
height: overlayDrawer.popupHeight
|
|
opacity: overlayDrawer.opacity
|
|
visible: opacity > 0 && bodyWidth > 0 && height > 0
|
|
z: -1
|
|
preferredRendererType: Shape.CurveRenderer
|
|
asynchronous: true
|
|
enabled: false
|
|
|
|
readonly property real bodyWidth: Math.max(0, powerPanel.x + powerPanel.width - overlayDrawer.x)
|
|
readonly property real cornerRadius: Math.min(MobileShell.Constants.convergenceWorkspaceFrameRadius,
|
|
Math.max(0.01, Math.min(bodyWidth, height) / 2))
|
|
|
|
ShapePath {
|
|
id: drawerSurfacePath
|
|
readonly property real cornerRadius: drawerSurface.cornerRadius
|
|
|
|
fillColor: Kirigami.Theme.backgroundColor
|
|
strokeWidth: 0
|
|
startX: 0
|
|
startY: 0
|
|
|
|
PathLine { x: drawerSurface.bodyWidth + drawerSurfacePath.cornerRadius; y: 0 }
|
|
PathArc { x: drawerSurface.bodyWidth; y: drawerSurfacePath.cornerRadius; radiusX: drawerSurfacePath.cornerRadius; radiusY: drawerSurfacePath.cornerRadius; direction: PathArc.Counterclockwise }
|
|
PathLine { x: drawerSurface.bodyWidth; y: drawerSurface.height - drawerSurfacePath.cornerRadius }
|
|
PathArc { x: drawerSurface.bodyWidth + drawerSurfacePath.cornerRadius; y: drawerSurface.height; radiusX: drawerSurfacePath.cornerRadius; radiusY: drawerSurfacePath.cornerRadius; direction: PathArc.Counterclockwise }
|
|
PathLine { x: 0; y: drawerSurface.height }
|
|
PathLine { x: 0; y: 0 }
|
|
}
|
|
}
|
|
}
|
|
|
|
// Game Center overlay — full-screen grid of games shown when gaming mode
|
|
// is active. Sits at LayerTop so it covers running application windows
|
|
// without going above system notifications.
|
|
GameCenterOverlay {
|
|
id: gameCenterOverlay
|
|
folio: root.folio
|
|
visible: ShellSettings.Settings.gamingModeEnabled && root.gameCenterOpen
|
|
|
|
onGameStarted: root.gameCenterOpen = false
|
|
onDismissRequested: {
|
|
root.gameCenterOpen = false
|
|
if (ShellSettings.Settings.gamingDismissHintEnabled) {
|
|
root.showGameCenterHint = true
|
|
gameCenterHintTimer.restart()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Small persistent button at the top-right corner of the screen that lets
|
|
// the user return to the Game Center after launching a game.
|
|
// Keep the Loader active for the full duration of gaming mode so the
|
|
// opacity Behavior in GamingHUD can animate both fade-in and fade-out.
|
|
//
|
|
// Hide the HUD while a game window covers the screen. A mapped LayerShell
|
|
// surface prevents KWin from using DRM direct scanout for the fullscreen
|
|
// game window. Setting showing=false triggers the opacity fade-out and then
|
|
// sets visible=false, which unmaps the Wayland surface and lets KWin bypass
|
|
// the compositor render loop entirely for the game frame.
|
|
Loader {
|
|
active: ShellSettings.Settings.gamingModeEnabled
|
|
sourceComponent: GamingHUD {
|
|
visible: showing
|
|
showing: !root.gameCenterOpen && !windowMaximizedTracker.showingWindow
|
|
onOpenRequested: root.gameCenterOpen = true
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: gameCenterHint
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.top: parent.top
|
|
anchors.topMargin: Kirigami.Units.gridUnit * 2
|
|
visible: root.showGameCenterHint && ShellSettings.Settings.gamingDismissHintEnabled
|
|
opacity: visible ? 1 : 0
|
|
z: 2000
|
|
radius: Kirigami.Units.cornerRadius
|
|
color: Qt.rgba(0, 0, 0, 0.65)
|
|
border.width: 1
|
|
border.color: Qt.rgba(1, 1, 1, 0.2)
|
|
|
|
Behavior on opacity {
|
|
MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
|
|
}
|
|
|
|
implicitWidth: hintText.implicitWidth + Kirigami.Units.gridUnit * 2
|
|
implicitHeight: hintText.implicitHeight + Kirigami.Units.largeSpacing
|
|
|
|
PlasmaComponents.Label {
|
|
id: hintText
|
|
anchors.centerIn: parent
|
|
text: i18n("Gaming mode is still on. Use Home or the gamepad icon to reopen Game Center.")
|
|
color: "white"
|
|
wrapMode: Text.WordWrap
|
|
width: Math.min(root.width * 0.8, Kirigami.Units.gridUnit * 30)
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
|
|
MobileShell.HomeScreen {
|
|
id: homeScreen
|
|
anchors.fill: parent
|
|
|
|
plasmoidItem: root
|
|
onResetHomeScreenPosition: {
|
|
// NOTE: empty, because this is handled by homeAction()
|
|
}
|
|
|
|
onHomeTriggered: root.homeAction()
|
|
|
|
contentItem: Item {
|
|
// homescreen component
|
|
FolioHomeScreen {
|
|
id: folioHomeScreen
|
|
folio: root.folio
|
|
maskManager: root.maskManager
|
|
anchors.fill: parent
|
|
|
|
topMargin: homeScreen.topMargin
|
|
bottomMargin: homeScreen.bottomMargin
|
|
leftMargin: homeScreen.leftMargin
|
|
rightMargin: homeScreen.rightMargin
|
|
|
|
// Ensure is the focused item at start
|
|
Component.onCompleted: forceActiveFocus()
|
|
|
|
onWallpaperSelectorTriggered: wallpaperSelectorLoader.active = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// top blur layer for items on top of the base homescreen
|
|
MobileShell.BlurEffect {
|
|
id: homescreenBlur
|
|
anchors.fill: parent
|
|
active: folio.FolioSettings.wallpaperBlurEffect > 1 && ((delegateDragItem.visible && folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Folder) || wallpaperSelectorLoader.active)
|
|
visible: active
|
|
fullBlur: 0
|
|
|
|
sourceLayer: homeScreenLayer
|
|
maskSourceLayer: frontMaskManager.maskLayer
|
|
|
|
// stacking both wallpaper and homescreen layers so we can blur them in one pass
|
|
Item {
|
|
id: homeScreenLayer
|
|
anchors.fill: parent
|
|
opacity: 0
|
|
|
|
// wallpaper blur
|
|
ShaderEffectSource {
|
|
anchors.fill: parent
|
|
|
|
textureSize: homescreenBlur.textureSize
|
|
sourceItem: Plasmoid.wallpaperGraphicsObject
|
|
hideSource: false
|
|
}
|
|
|
|
// homescreen blur
|
|
ShaderEffectSource {
|
|
anchors.fill: parent
|
|
|
|
textureSize: homescreenBlur.textureSize
|
|
sourceItem: homeScreen
|
|
hideSource: false
|
|
}
|
|
}
|
|
}
|
|
|
|
// drag and drop component
|
|
DelegateDragItem {
|
|
id: delegateDragItem
|
|
folio: root.folio
|
|
maskManager: root.frontMaskManager
|
|
}
|
|
|
|
// drag and drop for widgets
|
|
WidgetDragItem {
|
|
id: widgetDragItem
|
|
folio: root.folio
|
|
}
|
|
|
|
// loader for wallpaper selector
|
|
Loader {
|
|
id: wallpaperSelectorLoader
|
|
anchors.fill: parent
|
|
asynchronous: true
|
|
active: false
|
|
|
|
onLoaded: {
|
|
wallpaperSelectorLoader.item.open();
|
|
}
|
|
|
|
sourceComponent: MobileShell.WallpaperSelector {
|
|
maskManager: root.frontMaskManager
|
|
horizontal: root.width > root.height
|
|
edge: horizontal ? Qt.LeftEdge : Qt.BottomEdge
|
|
topMargin: horizontal ? folioHomeScreen.topMargin : 0
|
|
bottomMargin: horizontal ? 0 : folioHomeScreen.bottomMargin
|
|
leftMargin: horizontal ? folioHomeScreen.leftMargin : 0
|
|
rightMargin: horizontal ? folioHomeScreen.rightMargin : 0
|
|
onClosed: {
|
|
wallpaperSelectorLoader.active = false;
|
|
}
|
|
|
|
onWallpaperSettingsRequested: {
|
|
close();
|
|
folioHomeScreen.openConfigure();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|