diff --git a/containments/homescreens/folio/qml/main.qml b/containments/homescreens/folio/qml/main.qml index f667410a..088e6792 100644 --- a/containments/homescreens/folio/qml/main.qml +++ b/containments/homescreens/folio/qml/main.qml @@ -25,12 +25,32 @@ 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 Folio.HomeScreen folio: root.plasmoid + // Tracks whether the Game Center grid is visible within gaming mode. + // Starts true when gaming mode turns on; set to false by a game launch. + property bool gameCenterOpen: false + property bool showGameCenterHint: false + + Timer { + id: gameCenterHintTimer + interval: 2600 + onTriggered: root.showGameCenterHint = false + } + + Connections { + target: ShellSettings.Settings + function onGamingModeEnabledChanged() { + root.gameCenterOpen = ShellSettings.Settings.gamingModeEnabled + } + } + Component.onCompleted: { folio.FolioSettings.load(); folio.FavouritesModel.load(); @@ -89,6 +109,12 @@ ContainmentItem { 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. @@ -186,7 +212,7 @@ ContainmentItem { // task panel containment; this window only provides the visible dock. Window { id: dockOverlay - visible: ShellSettings.Settings.convergenceModeEnabled + visible: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled color: "transparent" width: Screen.width height: Kirigami.Units.gridUnit * 3 @@ -286,6 +312,7 @@ ContainmentItem { Window { id: drawerOverlay visible: ShellSettings.Settings.convergenceModeEnabled + && !ShellSettings.Settings.gamingModeEnabled && folio.HomeScreenState.appDrawerOpenProgress > 0 color: "transparent" width: Screen.width @@ -645,6 +672,68 @@ ContainmentItem { } } + // 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() + } + } + + onVisibleChanged: { + if (!visible) { + folio.ApplicationListSearchModel.categoryFilter = "" + } + } + } + + // Small persistent button at the top-right corner of the screen that lets + // the user return to the Game Center after launching a game. + GamingHUD { + visible: ShellSettings.Settings.gamingModeEnabled && !root.gameCenterOpen + 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 { + NumberAnimation { duration: Kirigami.Units.shortDuration; easing.type: Easing.InOutQuad } + } + + 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 diff --git a/containments/panel/qml/main.qml b/containments/panel/qml/main.qml index 3944ee9f..2e151540 100644 --- a/containments/panel/qml/main.qml +++ b/containments/panel/qml/main.qml @@ -34,11 +34,17 @@ ContainmentItem { // Whether the startup feedback is showing readonly property bool showingStartupFeedback: MobileShellState.ShellDBusObject.startupFeedbackModel.activeWindowIsStartupFeedback + readonly property bool gamingMode: ShellSettings.Settings.gamingModeEnabled + // Whether an app is maximized and showing (does not include startup feedback) readonly property bool showingApp: windowMaximizedTracker.showingWindow && !showingStartupFeedback // Whether the currently showing app is in "fullscreen" readonly property bool fullscreen: { + if (gamingMode) { + return true; + } + // In convergence mode the status bar is always visible, like a desktop panel. if (ShellSettings.Settings.convergenceModeEnabled) { return false; @@ -69,7 +75,7 @@ ContainmentItem { } } - readonly property real panelHeight: MobileShell.Constants.topPanelHeight + readonly property real panelHeight: gamingMode ? 0 : MobileShell.Constants.topPanelHeight onPanelHeightChanged: setWindowProperties() function setWindowProperties() { @@ -123,6 +129,11 @@ ContainmentItem { function onConvergenceModeEnabledChanged() { root.setWindowProperties(); } + + function onGamingModeEnabledChanged() { + root.setWindowProperties(); + MobileShellState.ShellDBusClient.panelState = ShellSettings.Settings.gamingModeEnabled ? "hidden" : "default"; + } } Component.onCompleted: { @@ -136,7 +147,7 @@ ContainmentItem { // MaximizeArea by the panel height. Window { id: topBarSpaceReserver - visible: ShellSettings.Settings.convergenceModeEnabled + visible: ShellSettings.Settings.convergenceModeEnabled && !ShellSettings.Settings.gamingModeEnabled color: "transparent" flags: Qt.FramelessWindowHint | Qt.WindowTransparentForInput height: root.panelHeight @@ -152,6 +163,7 @@ ContainmentItem { // Visual panel component StatusPanel { id: statusPanel + visible: !ShellSettings.Settings.gamingModeEnabled anchors.fill: parent containmentItem: root } diff --git a/containments/taskpanel/qml/main.qml b/containments/taskpanel/qml/main.qml index 9f3aa4cf..f9f4bb87 100644 --- a/containments/taskpanel/qml/main.qml +++ b/containments/taskpanel/qml/main.qml @@ -38,12 +38,14 @@ ContainmentItem { readonly property bool inLandscape: MobileShell.Constants.navigationPanelOnSide(Screen.width, Screen.height) - readonly property real navigationPanelHeight: MobileShell.Constants.navigationPanelThickness + readonly property bool gamingMode: ShellSettings.Settings.gamingModeEnabled + + readonly property real navigationPanelHeight: gamingMode ? 0 : MobileShell.Constants.navigationPanelThickness onNavigationPanelHeightChanged: setWindowProperties() readonly property real intendedWindowThickness: navigationPanelHeight readonly property real intendedWindowLength: inLandscape ? Screen.height : Screen.width - readonly property real intendedWindowOffset: inLandscape ? MobileShell.Constants.topPanelHeight : 0; // offset for top panel + readonly property real intendedWindowOffset: (inLandscape && !gamingMode) ? MobileShell.Constants.topPanelHeight : 0; // offset for top panel readonly property int intendedWindowLocation: inLandscape ? PlasmaCore.Types.RightEdge : PlasmaCore.Types.BottomEdge onIntendedWindowLengthChanged: maximizeTimer.restart() // ensure it always takes up the full length of the screen @@ -136,6 +138,11 @@ ContainmentItem { function onConvergenceModeEnabledChanged() { root.setWindowProperties(); } + + function onGamingModeEnabledChanged() { + root.setWindowProperties(); + navigationPanel.offset = ShellSettings.Settings.gamingModeEnabled ? root.navigationPanelHeight : 0; + } } Component.onCompleted: setWindowProperties(); @@ -153,6 +160,7 @@ ContainmentItem { Window { id: dockSpaceReserver visible: ShellSettings.Settings.convergenceModeEnabled + && !ShellSettings.Settings.gamingModeEnabled && !(ShellSettings.Settings.autoHidePanelsEnabled && windowMaximizedTracker.showingWindow) color: "transparent" @@ -177,7 +185,9 @@ ContainmentItem { return (windowMaximizedTracker.showingWindow || isCurrentWindowFullscreen) && !showingStartupFeedback } readonly property alias isCurrentWindowFullscreen: windowMaximizedTracker.isCurrentWindowFullscreen - readonly property bool fullscreen: isCurrentWindowFullscreen || (ShellSettings.Settings.autoHidePanelsEnabled && opaqueBar) + readonly property bool fullscreen: ShellSettings.Settings.gamingModeEnabled + || isCurrentWindowFullscreen + || (ShellSettings.Settings.autoHidePanelsEnabled && opaqueBar) WindowPlugin.WindowMaximizedTracker { id: windowMaximizedTracker @@ -205,6 +215,7 @@ ContainmentItem { Item { id: navigationPanel + visible: !ShellSettings.Settings.gamingModeEnabled anchors.fill: parent property real offset: 0 diff --git a/kwin/scripts/convergentwindows/contents/ui/main.qml b/kwin/scripts/convergentwindows/contents/ui/main.qml index 4bc06e1e..ce00568b 100644 --- a/kwin/scripts/convergentwindows/contents/ui/main.qml +++ b/kwin/scripts/convergentwindows/contents/ui/main.qml @@ -60,6 +60,12 @@ Loader { return; } + if (ShellSettings.Settings.gamingModeEnabled) { + window.noBorder = true; + window.setMaximize(true, true); + return; + } + if (ShellSettings.Settings.convergenceModeEnabled) { window.noBorder = false; } else { @@ -128,6 +134,16 @@ Loader { } } } + + function onGamingModeEnabledChanged() { + const windows = KWinComponents.Workspace.windows; + + for (let i = 0; i < windows.length; i++) { + if (windows[i].normalWindow) { + root.run(windows[i]); + } + } + } } Connections {