mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-24 23:27:43 +00:00
Compare commits
10 commits
847e516fcf
...
5d83caaaa1
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d83caaaa1 | |||
| 75a0f7a21e | |||
| 434f46403c | |||
| 00643ab5f5 | |||
| 43fe97dc11 | |||
| 33ebcce63b | |||
| b0ce6acdea | |||
| 0ac87a712e | |||
| b1f8d17f88 | |||
| 60a0246256 |
25 changed files with 390 additions and 210 deletions
57
README.md
57
README.md
|
|
@ -1,22 +1,47 @@
|
||||||
# Shift
|
# Shift
|
||||||
|
|
||||||
SHIFT is an independent shell fork focused on convergence. It builds on KDE Plasma Mobile, keeps the upstream phone UI available, and layers desktop-oriented behaviour on top of the same session.
|
SHIFT is a convergent environment for desktops, gaming consoles, tablets, and smartphones.
|
||||||
|
|
||||||
SHIFT tracks [plasma-mobile](https://invent.kde.org/plasma/plasma-mobile) as its upstream base.
|
What does convergence mean? This project aims to offer the same environment across different form factors, with an interface that adapts to the device, input type, and required user experience.
|
||||||
The upstream phone UI remains intact; convergence-specific behaviour is added on top.
|
|
||||||
|
For example: Smartphones connected to an external display can benefit from a graphical experience change from undocked to docked, transforming the workspace from mobile to desktop. Another example: 2-in-1 laptops that convert to tablets or have 360° rotatable displays can switch the graphical interface while offering a tablet or desktop experience.
|
||||||
|
|
||||||
|
Desktop:
|
||||||
|
SHIFT aims to offer a complete desktop experience, featuring a dockbar, a top bar, an app drawer, and an actions drawer with a notification history, a System Tray, and a Quick Settings Menu. The desktop experience is available in dynamic tiling mode or with classic floating windows.
|
||||||
|
|
||||||
|
Smartphone / Tablet:
|
||||||
|
The environment is capable of adapting its interface to work with tablets and smartphones, adopting a touch-first interface and workflow optimized for touchscreens.
|
||||||
|
|
||||||
|
Gaming Shell:
|
||||||
|
The Gaming Shell aims to be optimized for gamepad navigation, offering integration with game launchers and game centers. The gaming experience must also adapt to different factors, adapting to handheld devices and gaming stations that also serve as workstations.
|
||||||
|
|
||||||
|
### Screenshot
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
### What convergence mode changes
|
### What convergence mode changes
|
||||||
|
|
||||||
* Navigation panel replaced by a dock with running-app indicators,
|
* The old navigation panel is replaced by a unified dock that combines
|
||||||
favourites, context menus, and hover tooltips
|
pinned apps, running windows, desktop pager controls, and quick
|
||||||
* App drawer opens as a floating popup above the dock
|
actions such as Home and Overview.
|
||||||
|
* The dock supports desktop workflows: pin and unpin, drag reorder,
|
||||||
* Window management: edge tiling, edge maximize, close buttons, task
|
grouped window entries, middle-click close, and thumbnail previews.
|
||||||
context menus, Overview integration
|
* The app drawer and action drawer run as convergence surfaces (bounded
|
||||||
* Status bar gains a system tray, date display, and hover highlights
|
and shaped), with calendar, notifications, quick settings, and
|
||||||
* Screen space reserved for the dock via layer-shell exclusive zone
|
desktop-focused utility actions.
|
||||||
* Desktop niceties: right-click wallpaper settings, minimize-all on
|
* The status bar integrates tray/date behavior for desktop usage and is
|
||||||
home press, clickable page indicators
|
coordinated with the convergence drawers and workspace chrome.
|
||||||
|
* Screen space is explicitly reserved for the dock, with geometry and
|
||||||
|
input-region guards to keep overlays and windows stable.
|
||||||
|
* Window workflows are desktop-oriented: edge tiling/maximize,
|
||||||
|
dynamic-tiling actions from task menus, and optional snap-layout
|
||||||
|
behavior.
|
||||||
|
* Overview and virtual desktop operations are integrated into dock and
|
||||||
|
drawer actions, including desktop/window management entries.
|
||||||
|
* Theme behavior is convergence-aware, with Shift defaults and
|
||||||
|
wallpaper-driven dynamic theming applied to shell surfaces.
|
||||||
|
|
||||||
### Locations
|
### Locations
|
||||||
|
|
||||||
|
|
@ -39,7 +64,9 @@ cmake --install build
|
||||||
|
|
||||||
Compatibility-sensitive identifiers such as `org.kde.plasma.mobileshell`
|
Compatibility-sensitive identifiers such as `org.kde.plasma.mobileshell`
|
||||||
are intentionally preserved in the runtime and build instructions. They are
|
are intentionally preserved in the runtime and build instructions. They are
|
||||||
part of the current compatibility boundary, not the public product name.
|
part of the current compatibility boundary.
|
||||||
|
SHIFT tracks [plasma-mobile](https://invent.kde.org/plasma/plasma-mobile) as its upstream base.
|
||||||
|
The upstream phone UI remains intact; convergence-specific behaviour is added on top.
|
||||||
|
|
||||||
### Disclaimer
|
### Disclaimer
|
||||||
|
|
||||||
|
|
@ -47,7 +74,7 @@ SHIFT is an independent project based on KDE Plasma Mobile.
|
||||||
|
|
||||||
It is **not affiliated with or endorsed by** KDE or the KDE community.
|
It is **not affiliated with or endorsed by** KDE or the KDE community.
|
||||||
|
|
||||||
Some visual elements (such as icons or graphical assets) may originate from KDE Plasma Mobile and are used in accordance with their respective licenses. These elements may be replaced in future versions as the project evolves.
|
Some visual elements may originate from KDE Plasma Mobile and are used in accordance with their respective licenses. These elements may be replaced in future versions as the project evolves.
|
||||||
|
|
||||||
All trademarks, including KDE, belong to their respective owners.
|
All trademarks, including KDE, belong to their respective owners.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ set_source_files_properties(
|
||||||
qml/components/AppLaunch.qml
|
qml/components/AppLaunch.qml
|
||||||
qml/components/Constants.qml
|
qml/components/Constants.qml
|
||||||
qml/components/Motion.qml
|
qml/components/Motion.qml
|
||||||
|
qml/components/SurfaceColors.qml
|
||||||
qml/dataproviders/AudioInfo.qml
|
qml/dataproviders/AudioInfo.qml
|
||||||
qml/dataproviders/BatteryInfo.qml
|
qml/dataproviders/BatteryInfo.qml
|
||||||
qml/dataproviders/BluetoothInfo.qml
|
qml/dataproviders/BluetoothInfo.qml
|
||||||
|
|
@ -60,6 +61,7 @@ ecm_target_qml_sources(mobileshellplugin SOURCES
|
||||||
qml/components/ScreenEdgeDragEffect.qml
|
qml/components/ScreenEdgeDragEffect.qml
|
||||||
qml/components/StartupFeedbackPanelFill.qml
|
qml/components/StartupFeedbackPanelFill.qml
|
||||||
qml/components/StartupFeedbackWindows.qml
|
qml/components/StartupFeedbackWindows.qml
|
||||||
|
qml/components/SurfaceColors.qml
|
||||||
qml/components/TextDropShadow.qml
|
qml/components/TextDropShadow.qml
|
||||||
qml/components/VelocityCalculator.qml
|
qml/components/VelocityCalculator.qml
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,10 +77,7 @@ Item {
|
||||||
// Background color
|
// Background color
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(Kirigami.Theme.backgroundColor.r,
|
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.9)
|
||||||
Kirigami.Theme.backgroundColor.g,
|
|
||||||
Kirigami.Theme.backgroundColor.b,
|
|
||||||
0.9)
|
|
||||||
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.StandardDecel } }
|
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.StandardDecel } }
|
||||||
opacity: {
|
opacity: {
|
||||||
let base = Math.max(0, Math.min(brightnessPressedValue, actionDrawer.offset / root.minimizedQuickSettingsOffset));
|
let base = Math.max(0, Math.min(brightnessPressedValue, actionDrawer.offset / root.minimizedQuickSettingsOffset));
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ QQC2.Popup {
|
||||||
padding: Kirigami.Units.smallSpacing
|
padding: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
||||||
|
readonly property color surfaceColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
|
||||||
property string currentPluginId: ""
|
property string currentPluginId: ""
|
||||||
property Item __currentItem: null
|
property Item __currentItem: null
|
||||||
|
|
||||||
|
|
@ -81,11 +82,11 @@ QQC2.Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Kirigami.ShadowedRectangle {
|
background: Kirigami.ShadowedRectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: popup.surfaceColor
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
|
||||||
border.color: Kirigami.ColorUtils.linearInterpolation(
|
border.color: Kirigami.ColorUtils.linearInterpolation(
|
||||||
Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2)
|
popup.surfaceColor, Kirigami.Theme.textColor, 0.2)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
shadow.size: Kirigami.Units.gridUnit
|
shadow.size: Kirigami.Units.gridUnit
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ Item {
|
||||||
id: actionDrawerSurfacePath
|
id: actionDrawerSurfacePath
|
||||||
readonly property real cornerRadius: actionDrawerSurface.cornerRadius
|
readonly property real cornerRadius: actionDrawerSurface.cornerRadius
|
||||||
|
|
||||||
fillColor: Kirigami.Theme.backgroundColor
|
fillColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
|
||||||
strokeWidth: 0
|
strokeWidth: 0
|
||||||
startX: actionDrawerSurface.width
|
startX: actionDrawerSurface.width
|
||||||
startY: 0
|
startY: 0
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ MobileShell.BaseItem {
|
||||||
required property string text
|
required property string text
|
||||||
required property string status
|
required property string status
|
||||||
required property string icon
|
required property string icon
|
||||||
|
// Visual active-state of the tile. Declared here to shadow the built-in
|
||||||
|
// Item.enabled: binding the real Item.enabled would disable the contentItem
|
||||||
|
// MouseArea, making an inactive tile impossible to tap (and thus turn on).
|
||||||
|
// Tiles are removed from the list via `available`, never disabled.
|
||||||
|
required property bool enabled
|
||||||
required property string settingsCommand
|
required property string settingsCommand
|
||||||
required property var toggleFunction
|
required property var toggleFunction
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ Item {
|
||||||
required property string text
|
required property string text
|
||||||
required property string status
|
required property string status
|
||||||
required property string icon
|
required property string icon
|
||||||
|
// Visual active-state only. Shadows the built-in Item.enabled so binding it
|
||||||
|
// does not disable the inner toggle/detail MouseAreas (which would make an
|
||||||
|
// inactive service impossible to switch on).
|
||||||
|
required property bool enabled
|
||||||
required property var toggleFunction
|
required property var toggleFunction
|
||||||
property bool compact: false
|
property bool compact: false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ QQC2.Popup {
|
||||||
|
|
||||||
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
readonly property int popupAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
||||||
readonly property int trayItemCount: trayList.count
|
readonly property int trayItemCount: trayList.count
|
||||||
|
readonly property color surfaceColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
popup.open();
|
popup.open();
|
||||||
|
|
@ -38,11 +39,11 @@ QQC2.Popup {
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Kirigami.ShadowedRectangle {
|
background: Kirigami.ShadowedRectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: popup.surfaceColor
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
|
||||||
border.color: Kirigami.ColorUtils.linearInterpolation(
|
border.color: Kirigami.ColorUtils.linearInterpolation(
|
||||||
Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.2)
|
popup.surfaceColor, Kirigami.Theme.textColor, 0.2)
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
shadow.size: Kirigami.Units.gridUnit
|
shadow.size: Kirigami.Units.gridUnit
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ Item {
|
||||||
property bool active: false
|
property bool active: false
|
||||||
property bool stateLayerEnabled: true
|
property bool stateLayerEnabled: true
|
||||||
property color color: Kirigami.Theme.textColor
|
property color color: Kirigami.Theme.textColor
|
||||||
property color activeColor: Kirigami.Theme.highlightColor
|
property color activeColor: SurfaceColors.accent()
|
||||||
property real hoverOpacity: 0.08
|
property real hoverOpacity: 0.08
|
||||||
property real pressedOpacity: 0.14
|
property real pressedOpacity: 0.14
|
||||||
property real activeOpacity: 0.12
|
property real activeOpacity: 0.12
|
||||||
|
|
|
||||||
|
|
@ -48,15 +48,24 @@ Item {
|
||||||
// adjust color depending on panel type
|
// adjust color depending on panel type
|
||||||
property color panelColor: {
|
property color panelColor: {
|
||||||
let tintPercent
|
let tintPercent
|
||||||
|
let accentTintDark
|
||||||
|
let accentTintLight
|
||||||
if (panelType === PanelBackground.PanelType.Popup) {
|
if (panelType === PanelBackground.PanelType.Popup) {
|
||||||
tintPercent = 0.035
|
tintPercent = 0.035
|
||||||
|
accentTintDark = 0.16
|
||||||
|
accentTintLight = 0.08
|
||||||
} else if (panelType === PanelBackground.PanelType.Base || panelType === PanelBackground.PanelType.Stacked || panelType === PanelBackground.PanelType.Flat) {
|
} else if (panelType === PanelBackground.PanelType.Base || panelType === PanelBackground.PanelType.Stacked || panelType === PanelBackground.PanelType.Flat) {
|
||||||
tintPercent = 0
|
tintPercent = 0
|
||||||
|
accentTintDark = 0.18
|
||||||
|
accentTintLight = 0.10
|
||||||
} else {
|
} else {
|
||||||
tintPercent = 0.06
|
tintPercent = 0.06
|
||||||
|
accentTintDark = 0.22
|
||||||
|
accentTintLight = 0.12
|
||||||
}
|
}
|
||||||
|
|
||||||
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", tintPercent)
|
const baseColor = Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, "white", tintPercent)
|
||||||
|
return SurfaceColors.accentSurface(baseColor, accentTintDark, accentTintLight)
|
||||||
}
|
}
|
||||||
// in some circumstances, panels can change there type
|
// in some circumstances, panels can change there type
|
||||||
// for example, popup notifition when opening the popup notifition drawer
|
// for example, popup notifition when opening the popup notifition drawer
|
||||||
|
|
|
||||||
47
components/mobileshell/qml/components/SurfaceColors.qml
Normal file
47
components/mobileshell/qml/components/SurfaceColors.qml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
// SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
function hasColor(color) {
|
||||||
|
return color && color.a > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function accent() {
|
||||||
|
if (hasColor(ShellSettings.Settings.accentColor)) {
|
||||||
|
return ShellSettings.Settings.accentColor
|
||||||
|
}
|
||||||
|
if ((ShellSettings.Settings.wallpaperThemeEnabled || ShellSettings.Settings.wallpaperAccentEnabled)
|
||||||
|
&& hasColor(ShellSettings.Settings.wallpaperThemeColor)) {
|
||||||
|
return ShellSettings.Settings.wallpaperThemeColor
|
||||||
|
}
|
||||||
|
return Kirigami.Theme.highlightColor
|
||||||
|
}
|
||||||
|
|
||||||
|
function mix(base, overlay, ratio) {
|
||||||
|
return Qt.rgba(
|
||||||
|
base.r + (overlay.r - base.r) * ratio,
|
||||||
|
base.g + (overlay.g - base.g) * ratio,
|
||||||
|
base.b + (overlay.b - base.b) * ratio,
|
||||||
|
base.a + (overlay.a - base.a) * ratio)
|
||||||
|
}
|
||||||
|
|
||||||
|
function withAlpha(color, alpha) {
|
||||||
|
return Qt.rgba(color.r, color.g, color.b, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
function accentSurface(base, darkRatio, lightRatio) {
|
||||||
|
if (!ShellSettings.Settings.wallpaperThemeEnabled && !ShellSettings.Settings.wallpaperAccentEnabled) {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
const darkSurface = Kirigami.ColorUtils.brightnessForColor(base) === Kirigami.ColorUtils.Dark
|
||||||
|
return mix(base, accent(), darkSurface ? darkRatio : lightRatio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ Window {
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
|
|
||||||
readonly property color backgroundColor: Qt.darker(Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95), 1.05)
|
readonly property color backgroundColor: Qt.darker(MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.95), 1.05)
|
||||||
color: popupDrawerOpened && visible ? backgroundColor : "transparent"
|
color: popupDrawerOpened && visible ? backgroundColor : "transparent"
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
MobileShell.MotionColorAnimation {
|
MobileShell.MotionColorAnimation {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ Window {
|
||||||
LayerShell.Window.layer: LayerShell.Window.LayerOverlay
|
LayerShell.Window.layer: LayerShell.Window.LayerOverlay
|
||||||
LayerShell.Window.exclusionZone: -1
|
LayerShell.Window.exclusionZone: -1
|
||||||
|
|
||||||
readonly property color backgroundColor: Qt.darker(Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.95), 1.05)
|
readonly property color backgroundColor: Qt.darker(MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.95), 1.05)
|
||||||
readonly property int overlayAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.25)
|
readonly property int overlayAnimationDuration: Math.round(MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow) * 1.25)
|
||||||
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,8 @@ MouseArea {
|
||||||
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18)
|
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18)
|
||||||
}
|
}
|
||||||
if (active) {
|
if (active) {
|
||||||
return Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, hovered ? 0.18 : 0.12)
|
const accent = MobileShell.SurfaceColors.accent()
|
||||||
|
return Qt.rgba(accent.r, accent.g, accent.b, hovered ? 0.18 : 0.12)
|
||||||
}
|
}
|
||||||
if (hovered) {
|
if (hovered) {
|
||||||
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
return Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
||||||
|
|
@ -756,7 +757,7 @@ MouseArea {
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: (leftDesktopBtn.index + 1).toString()
|
text: (leftDesktopBtn.index + 1).toString()
|
||||||
color: leftDesktopBtn.isCurrent ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
|
color: leftDesktopBtn.isCurrent ? MobileShell.SurfaceColors.accent() : Kirigami.Theme.textColor
|
||||||
font.pixelSize: Math.round(parent.height * 0.3)
|
font.pixelSize: Math.round(parent.height * 0.3)
|
||||||
font.bold: leftDesktopBtn.isCurrent
|
font.bold: leftDesktopBtn.isCurrent
|
||||||
}
|
}
|
||||||
|
|
@ -1487,7 +1488,7 @@ MouseArea {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12)
|
||||||
border.color: Qt.rgba(
|
border.color: Qt.rgba(
|
||||||
Kirigami.Theme.textColor.r,
|
Kirigami.Theme.textColor.r,
|
||||||
Kirigami.Theme.textColor.g,
|
Kirigami.Theme.textColor.g,
|
||||||
|
|
@ -1544,9 +1545,7 @@ MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
color: thumbEntry.containsMouse
|
color: thumbEntry.containsMouse
|
||||||
? Qt.rgba(Kirigami.Theme.highlightColor.r,
|
? MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accent(), 0.15)
|
||||||
Kirigami.Theme.highlightColor.g,
|
|
||||||
Kirigami.Theme.highlightColor.b, 0.15)
|
|
||||||
: "transparent"
|
: "transparent"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1790,7 +1789,7 @@ MouseArea {
|
||||||
width: Kirigami.Units.smallSpacing * 3
|
width: Kirigami.Units.smallSpacing * 3
|
||||||
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
|
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: Kirigami.Theme.highlightColor
|
color: MobileShell.SurfaceColors.accent()
|
||||||
opacity: taskDelegate.dynamicTilingMaximized ? 0.95 : 0
|
opacity: taskDelegate.dynamicTilingMaximized ? 0.95 : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
|
|
@ -1861,7 +1860,7 @@ MouseArea {
|
||||||
width: taskDelegate.model.IsActive === true ? Kirigami.Units.smallSpacing * 3 : Kirigami.Units.smallSpacing * 1.5
|
width: taskDelegate.model.IsActive === true ? Kirigami.Units.smallSpacing * 3 : Kirigami.Units.smallSpacing * 1.5
|
||||||
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
|
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
color: Kirigami.Theme.highlightColor
|
color: MobileShell.SurfaceColors.accent()
|
||||||
opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.45
|
opacity: taskDelegate.model.IsActive === true ? 1.0 : 0.45
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ Window {
|
||||||
readonly property string backButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB)
|
readonly property string backButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB)
|
||||||
readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonX)
|
readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonX)
|
||||||
readonly property string exitButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonY)
|
readonly property string exitButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonY)
|
||||||
|
|
||||||
readonly property string leftShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonLeftShoulder)
|
readonly property string leftShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonLeftShoulder)
|
||||||
readonly property string rightShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonRightShoulder)
|
readonly property string rightShoulderLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonRightShoulder)
|
||||||
readonly property string quickSettingsButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonBack)
|
readonly property string quickSettingsButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonBack)
|
||||||
|
|
@ -40,6 +41,41 @@ Window {
|
||||||
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
||||||
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
|
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
|
||||||
readonly property int launchFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.StandardAccel)
|
readonly property int launchFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.StandardAccel)
|
||||||
|
readonly property int shortestSide: Math.min(width, height)
|
||||||
|
readonly property bool compactMode: !ShellSettings.Settings.convergenceModeEnabled && shortestSide <= Kirigami.Units.gridUnit * 50
|
||||||
|
readonly property bool bigScreenMode: !compactMode
|
||||||
|
readonly property int horizontalPadding: compactMode ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 2
|
||||||
|
readonly property int verticalPadding: compactMode ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 2
|
||||||
|
readonly property real gridMinCellSize: compactMode ? Kirigami.Units.gridUnit * 6.8 : Kirigami.Units.gridUnit * 8.8
|
||||||
|
// Steam library assets heavily favor wide capsules and 16:9 media surfaces.
|
||||||
|
// Keep game tiles landscape-first to avoid mobile-style portrait cards.
|
||||||
|
readonly property real capsuleArtAspect: 16 / 9
|
||||||
|
|
||||||
|
function controlLegendText() {
|
||||||
|
if (GamingShell.GamepadManager.hasGamepad) {
|
||||||
|
if (runningGames.activeFocus) {
|
||||||
|
return i18n("%1: Select %2: Close %3: Back %4: Exit %5: Settings %6: Search",
|
||||||
|
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
||||||
|
quickSettingsButtonLabel, searchButtonLabel)
|
||||||
|
}
|
||||||
|
if (recentList.activeFocus) {
|
||||||
|
return i18n("%1: Play %2: Details %3: Back %4: Exit %5: Settings %6: Search",
|
||||||
|
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
||||||
|
quickSettingsButtonLabel, searchButtonLabel)
|
||||||
|
}
|
||||||
|
return i18n("%1: Play %2: Details %3: Back %4: Exit %5/%6: Filter %7: Settings %8: Search",
|
||||||
|
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
||||||
|
leftShoulderLabel, rightShoulderLabel, quickSettingsButtonLabel, searchButtonLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runningGames.activeFocus) {
|
||||||
|
return i18n("Mouse: click a running game to focus it. Keyboard: arrows move between cards, Enter selects, Esc closes.")
|
||||||
|
}
|
||||||
|
if (recentList.activeFocus) {
|
||||||
|
return i18n("Mouse: click a recent game to play it. Keyboard: arrows move between cards, Enter plays, Esc closes.")
|
||||||
|
}
|
||||||
|
return i18n("Mouse: click a game or details button. Keyboard: arrows navigate, Enter plays, I shows details, Esc closes.")
|
||||||
|
}
|
||||||
|
|
||||||
function pulsePrimaryGamepad(lowIntensity, highIntensity, durationMs) {
|
function pulsePrimaryGamepad(lowIntensity, highIntensity, durationMs) {
|
||||||
var pad = GamingShell.GamepadManager.primaryGamepad
|
var pad = GamingShell.GamepadManager.primaryGamepad
|
||||||
|
|
@ -481,9 +517,7 @@ Window {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
color: Qt.rgba(Kirigami.Theme.backgroundColor.r,
|
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), root.bigScreenMode ? 0.94 : 0.9)
|
||||||
Kirigami.Theme.backgroundColor.g,
|
|
||||||
Kirigami.Theme.backgroundColor.b, 0.92)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
|
|
@ -496,8 +530,11 @@ Window {
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Kirigami.Units.largeSpacing * 2
|
anchors.leftMargin: root.horizontalPadding
|
||||||
spacing: Kirigami.Units.largeSpacing
|
anchors.rightMargin: root.horizontalPadding
|
||||||
|
anchors.topMargin: root.verticalPadding
|
||||||
|
anchors.bottomMargin: root.verticalPadding
|
||||||
|
spacing: root.compactMode ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
// ---- header ----
|
// ---- header ----
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|
@ -506,7 +543,7 @@ Window {
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
text: i18n("Game Center")
|
text: i18n("Game Center")
|
||||||
level: 1
|
level: root.compactMode ? 2 : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
|
|
@ -561,7 +598,7 @@ Window {
|
||||||
|
|
||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
icon.name: "window-close"
|
icon.name: "window-close"
|
||||||
text: i18n("Exit Gaming Mode")
|
text: root.compactMode ? i18n("Exit") : i18n("Exit Gaming Mode")
|
||||||
display: QQC2.AbstractButton.TextBesideIcon
|
display: QQC2.AbstractButton.TextBesideIcon
|
||||||
Keys.onReturnPressed: clicked()
|
Keys.onReturnPressed: clicked()
|
||||||
Keys.onEnterPressed: clicked()
|
Keys.onEnterPressed: clicked()
|
||||||
|
|
@ -573,6 +610,7 @@ Window {
|
||||||
RunningGamesView {
|
RunningGamesView {
|
||||||
id: runningGames
|
id: runningGames
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
compactMode: root.compactMode
|
||||||
onTaskActivated: {
|
onTaskActivated: {
|
||||||
GamingShell.GameLauncherProvider.clearPendingLaunch()
|
GamingShell.GameLauncherProvider.clearPendingLaunch()
|
||||||
root.gameStarted()
|
root.gameStarted()
|
||||||
|
|
@ -627,7 +665,9 @@ Window {
|
||||||
ListView {
|
ListView {
|
||||||
id: recentList
|
id: recentList
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 5
|
readonly property int cardWidth: root.compactMode ? Kirigami.Units.gridUnit * 8 : Kirigami.Units.gridUnit * 10
|
||||||
|
readonly property int artHeight: Math.round(cardWidth / root.capsuleArtAspect)
|
||||||
|
Layout.preferredHeight: artHeight + Kirigami.Units.gridUnit * 1.7
|
||||||
orientation: ListView.Horizontal
|
orientation: ListView.Horizontal
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
clip: true
|
clip: true
|
||||||
|
|
@ -665,7 +705,7 @@ Window {
|
||||||
Keys.onDownPressed: grid.forceActiveFocus()
|
Keys.onDownPressed: grid.forceActiveFocus()
|
||||||
|
|
||||||
delegate: QQC2.ItemDelegate {
|
delegate: QQC2.ItemDelegate {
|
||||||
width: Kirigami.Units.gridUnit * 7
|
width: recentList.cardWidth
|
||||||
height: recentList.height
|
height: recentList.height
|
||||||
|
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
@ -679,16 +719,26 @@ Window {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
color: parent.isCurrent
|
color: parent.isCurrent
|
||||||
? Kirigami.Theme.highlightColor
|
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.22)
|
||||||
: (parent.hovered ? Kirigami.Theme.hoverColor : "transparent")
|
: (parent.hovered
|
||||||
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
||||||
|
: "transparent")
|
||||||
|
border.color: parent.isCurrent ? Kirigami.Theme.highlightColor : "transparent"
|
||||||
|
border.width: parent.isCurrent ? 2 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
Image {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.preferredHeight: recentList.artHeight
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
clip: true
|
||||||
|
color: Qt.rgba(Kirigami.Theme.alternateBackgroundColor.r, Kirigami.Theme.alternateBackgroundColor.g, Kirigami.Theme.alternateBackgroundColor.b, 0.8)
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
source: hasArt ? "file://" + modelData.artwork : ""
|
source: hasArt ? "file://" + modelData.artwork : ""
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
visible: hasArt
|
visible: hasArt
|
||||||
|
|
@ -696,19 +746,20 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Icon {
|
Kirigami.Icon {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
anchors.centerIn: parent
|
||||||
implicitWidth: Kirigami.Units.iconSizes.large
|
implicitWidth: Kirigami.Units.iconSizes.large
|
||||||
implicitHeight: Kirigami.Units.iconSizes.large
|
implicitHeight: Kirigami.Units.iconSizes.large
|
||||||
source: modelData.icon
|
source: modelData.icon
|
||||||
visible: !hasArt
|
visible: !hasArt
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: modelData.name
|
text: modelData.name
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignLeft
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.85
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.85
|
||||||
color: parent.parent.isCurrent
|
color: parent.parent.isCurrent
|
||||||
? Kirigami.Theme.highlightedTextColor
|
? Kirigami.Theme.highlightedTextColor
|
||||||
|
|
@ -736,9 +787,14 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- search + filter ----
|
// ---- search + filter ----
|
||||||
RowLayout {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: Kirigami.Units.largeSpacing
|
implicitHeight: searchFilterStack.implicitHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: searchFilterStack
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
Kirigami.SearchField {
|
Kirigami.SearchField {
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
@ -758,6 +814,7 @@ Window {
|
||||||
|
|
||||||
QQC2.TabBar {
|
QQC2.TabBar {
|
||||||
id: sourceFilterBar
|
id: sourceFilterBar
|
||||||
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
|
|
@ -783,6 +840,7 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- game grid ----
|
// ---- game grid ----
|
||||||
|
|
||||||
|
|
@ -794,11 +852,12 @@ Window {
|
||||||
|
|
||||||
model: GamingShell.GameLauncherProvider
|
model: GamingShell.GameLauncherProvider
|
||||||
|
|
||||||
readonly property real minCellSize: Kirigami.Units.gridUnit * 8
|
readonly property real minCellSize: root.gridMinCellSize
|
||||||
readonly property int columns: Math.max(2, Math.floor(width / minCellSize))
|
readonly property int columns: Math.max(2, Math.floor(width / minCellSize))
|
||||||
|
|
||||||
cellWidth: Math.floor(width / columns)
|
cellWidth: Math.floor(width / columns)
|
||||||
cellHeight: Math.floor(cellWidth * 1.5) + Kirigami.Units.gridUnit * 2
|
readonly property int artHeight: Math.round(cellWidth / root.capsuleArtAspect)
|
||||||
|
cellHeight: artHeight + (root.compactMode ? Kirigami.Units.gridUnit * 1.9 : Kirigami.Units.gridUnit * 2.2)
|
||||||
|
|
||||||
keyNavigationEnabled: true
|
keyNavigationEnabled: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
|
|
@ -873,7 +932,7 @@ Window {
|
||||||
|
|
||||||
QQC2.ItemDelegate {
|
QQC2.ItemDelegate {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
anchors.margins: root.compactMode ? 0 : Kirigami.Units.smallSpacing
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
readonly property bool isCurrent: GridView.isCurrentItem && grid.activeFocus
|
readonly property bool isCurrent: GridView.isCurrentItem && grid.activeFocus
|
||||||
|
|
@ -881,24 +940,30 @@ Window {
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||||
color: parent.isCurrent
|
color: parent.isCurrent
|
||||||
? Kirigami.Theme.highlightColor
|
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g,
|
||||||
: (parent.hovered ? Kirigami.Theme.hoverColor : "transparent")
|
Kirigami.Theme.highlightColor.b, 0.22)
|
||||||
|
: (parent.hovered
|
||||||
|
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g,
|
||||||
|
Kirigami.Theme.textColor.b, 0.08)
|
||||||
|
: "transparent")
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
border.color: parent.isCurrent ? Kirigami.Theme.highlightColor : "transparent"
|
||||||
|
border.width: parent.isCurrent ? 2 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
// ---- cover art tile ----
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: Kirigami.Units.smallSpacing
|
||||||
visible: hasArt
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.preferredHeight: grid.artHeight
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
clip: true
|
clip: true
|
||||||
color: "transparent"
|
color: Qt.rgba(Kirigami.Theme.alternateBackgroundColor.r,
|
||||||
|
Kirigami.Theme.alternateBackgroundColor.g,
|
||||||
|
Kirigami.Theme.alternateBackgroundColor.b, 0.85)
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
@ -906,6 +971,15 @@ Window {
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
smooth: true
|
smooth: true
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
visible: hasArt
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: root.compactMode ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.huge
|
||||||
|
implicitHeight: implicitWidth
|
||||||
|
source: icon
|
||||||
|
visible: !hasArt
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -929,76 +1003,35 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title beneath artwork
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
||||||
text: name
|
text: name
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignLeft
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
leftPadding: Kirigami.Units.smallSpacing
|
leftPadding: Kirigami.Units.smallSpacing
|
||||||
rightPadding: Kirigami.Units.smallSpacing
|
rightPadding: Kirigami.Units.smallSpacing
|
||||||
color: parent.parent.parent.isCurrent
|
color: parent.parent.parent.isCurrent
|
||||||
? Kirigami.Theme.highlightedTextColor
|
? Kirigami.Theme.highlightedTextColor
|
||||||
: Kirigami.Theme.textColor
|
: Kirigami.Theme.textColor
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---- fallback icon tile ----
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Kirigami.Units.smallSpacing
|
|
||||||
visible: !hasArt
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
Item { Layout.fillHeight: true }
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
implicitWidth: Kirigami.Units.iconSizes.huge
|
|
||||||
implicitHeight: Kirigami.Units.iconSizes.huge
|
|
||||||
source: icon
|
|
||||||
|
|
||||||
scale: parent.parent.parent.isCurrent ? 1.08 : 1.0
|
|
||||||
Behavior on scale {
|
|
||||||
MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: name
|
visible: lastPlayedText.length > 0
|
||||||
maximumLineCount: 2
|
text: lastPlayedText
|
||||||
wrapMode: Text.Wrap
|
maximumLineCount: 1
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: parent.parent.parent.isCurrent
|
leftPadding: Kirigami.Units.smallSpacing
|
||||||
? Kirigami.Theme.highlightedTextColor
|
rightPadding: Kirigami.Units.smallSpacing
|
||||||
: Kirigami.Theme.textColor
|
opacity: 0.65
|
||||||
}
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.78
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
visible: source !== "desktop"
|
|
||||||
radius: height / 2
|
|
||||||
color: root.sourceChipColor(source)
|
|
||||||
implicitHeight: sourceChipLabel.implicitHeight + Kirigami.Units.smallSpacing
|
|
||||||
implicitWidth: sourceChipLabel.implicitWidth + Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
PC3.Label {
|
|
||||||
id: sourceChipLabel
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: root.sourceLabel(source)
|
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.72
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
color: "white"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1049,6 +1082,7 @@ Window {
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: GamingShell.GamepadManager
|
model: GamingShell.GamepadManager
|
||||||
|
visible: root.bigScreenMode
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
@ -1086,19 +1120,12 @@ Window {
|
||||||
|
|
||||||
// Gamepad legend
|
// Gamepad legend
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
text: runningGames.activeFocus
|
Layout.fillWidth: true
|
||||||
? i18n("%1: Select %2: Close %3: Back %4: Exit %5: Settings %6: Search",
|
text: root.controlLegendText()
|
||||||
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * (root.compactMode ? 0.7 : 0.75)
|
||||||
quickSettingsButtonLabel, searchButtonLabel)
|
|
||||||
: recentList.activeFocus
|
|
||||||
? i18n("%1: Play %2: Details %3: Back %4: Exit %5: Settings %6: Search",
|
|
||||||
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
|
||||||
quickSettingsButtonLabel, searchButtonLabel)
|
|
||||||
: i18n("%1: Play %2: Details %3: Back %4: Exit %5/%6: Filter %7: Settings %8: Search",
|
|
||||||
actionButtonLabel, closeButtonLabel, backButtonLabel, exitButtonLabel,
|
|
||||||
leftShoulderLabel, rightShoulderLabel, quickSettingsButtonLabel, searchButtonLabel)
|
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
|
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1108,6 +1135,7 @@ Window {
|
||||||
GamingQuickSettings {
|
GamingQuickSettings {
|
||||||
id: quickSettings
|
id: quickSettings
|
||||||
z: 50
|
z: 50
|
||||||
|
compactMode: root.compactMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Launch transition: brief fade to black, then dismiss
|
// Launch transition: brief fade to black, then dismiss
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,19 @@ Item {
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property bool compactMode: false
|
||||||
property bool opened: false
|
property bool opened: false
|
||||||
readonly property string acceptButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonA)
|
readonly property string acceptButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonA)
|
||||||
readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB)
|
readonly property string closeButtonLabel: GamingShell.GamepadManager.buttonLabel(GamingShell.GamepadManager.ButtonB)
|
||||||
|
|
||||||
|
function controlLegendText() {
|
||||||
|
if (GamingShell.GamepadManager.hasGamepad) {
|
||||||
|
return i18n("↕: Navigate ↔: Adjust %1: Toggle %2: Close", acceptButtonLabel, closeButtonLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return i18n("↑↓: Navigate ←→: Adjust Enter: Toggle Esc: Close")
|
||||||
|
}
|
||||||
|
|
||||||
function pulsePrimaryGamepad(lowIntensity, highIntensity, durationMs) {
|
function pulsePrimaryGamepad(lowIntensity, highIntensity, durationMs) {
|
||||||
var pad = GamingShell.GamepadManager.primaryGamepad
|
var pad = GamingShell.GamepadManager.primaryGamepad
|
||||||
if (!pad || !pad.hasRumble) {
|
if (!pad || !pad.hasRumble) {
|
||||||
|
|
@ -142,7 +151,9 @@ Item {
|
||||||
// Panel sliding in from the right
|
// Panel sliding in from the right
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: panel
|
id: panel
|
||||||
width: Math.min(root.width * 0.35, Kirigami.Units.gridUnit * 22)
|
width: root.compactMode
|
||||||
|
? Math.min(root.width * 0.92, Kirigami.Units.gridUnit * 26)
|
||||||
|
: Math.min(root.width * 0.35, Kirigami.Units.gridUnit * 24)
|
||||||
height: root.height
|
height: root.height
|
||||||
anchors.top: root.top
|
anchors.top: root.top
|
||||||
anchors.bottom: root.bottom
|
anchors.bottom: root.bottom
|
||||||
|
|
@ -155,9 +166,7 @@ Item {
|
||||||
|
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
color: Qt.rgba(Kirigami.Theme.backgroundColor.r,
|
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), 0.96)
|
||||||
Kirigami.Theme.backgroundColor.g,
|
|
||||||
Kirigami.Theme.backgroundColor.b, 0.96)
|
|
||||||
|
|
||||||
// Subtle left border
|
// Subtle left border
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -183,7 +192,7 @@ Item {
|
||||||
|
|
||||||
Flickable {
|
Flickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Kirigami.Units.largeSpacing * 2
|
anchors.margins: root.compactMode ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 2
|
||||||
contentHeight: settingsColumn.implicitHeight
|
contentHeight: settingsColumn.implicitHeight
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
|
|
@ -690,11 +699,11 @@ Item {
|
||||||
// ---- Gamepad legend ----
|
// ---- Gamepad legend ----
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: i18n("↕: Navigate ↔: Adjust %1: Toggle %2: Close",
|
text: root.controlLegendText()
|
||||||
acceptButtonLabel, closeButtonLabel)
|
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
|
||||||
opacity: 0.5
|
opacity: 0.5
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillHeight: true }
|
Item { Layout.fillHeight: true }
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool compactMode: false
|
||||||
|
|
||||||
implicitHeight: taskList.count > 0 ? column.implicitHeight : 0
|
implicitHeight: taskList.count > 0 ? column.implicitHeight : 0
|
||||||
readonly property bool hasTasks: taskList.count > 0
|
readonly property bool hasTasks: taskList.count > 0
|
||||||
readonly property int taskCount: taskList.count
|
readonly property int taskCount: taskList.count
|
||||||
|
|
@ -73,7 +75,7 @@ Item {
|
||||||
visible: taskList.count > 0
|
visible: taskList.count > 0
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
level: 2
|
level: root.compactMode ? 3 : 2
|
||||||
text: i18n("Running")
|
text: i18n("Running")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,8 +122,8 @@ Item {
|
||||||
required property var decoration
|
required property var decoration
|
||||||
required property var winIdList
|
required property var winIdList
|
||||||
|
|
||||||
width: Kirigami.Units.gridUnit * 8
|
width: root.compactMode ? Kirigami.Units.gridUnit * 6.4 : Kirigami.Units.gridUnit * 8
|
||||||
height: Kirigami.Units.gridUnit * 5
|
height: root.compactMode ? Kirigami.Units.gridUnit * 4.2 : Kirigami.Units.gridUnit * 5
|
||||||
|
|
||||||
readonly property var modelIndex: tasks.makeModelIndex(index)
|
readonly property var modelIndex: tasks.makeModelIndex(index)
|
||||||
readonly property bool isCurrent: ListView.isCurrentItem && taskList.activeFocus
|
readonly property bool isCurrent: ListView.isCurrentItem && taskList.activeFocus
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ ContainmentItem {
|
||||||
// State saved when gaming mode activates, restored when it deactivates
|
// State saved when gaming mode activates, restored when it deactivates
|
||||||
property string _savedPowerProfile: ""
|
property string _savedPowerProfile: ""
|
||||||
property bool _savedDnd: false
|
property bool _savedDnd: false
|
||||||
|
property bool _savedDynamicTiling: false
|
||||||
property bool _gamingSessionActive: false
|
property bool _gamingSessionActive: false
|
||||||
|
|
||||||
function _applyGamingModeState(enabled) {
|
function _applyGamingModeState(enabled) {
|
||||||
|
|
@ -57,7 +58,20 @@ ContainmentItem {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
// Save current state and apply gaming optimizations
|
// Save current state and apply gaming optimizations
|
||||||
root._savedDnd = MobileShellState.ShellDBusClient.doNotDisturb
|
root._savedDnd = MobileShellState.ShellDBusClient.doNotDisturb
|
||||||
|
root._savedDynamicTiling = ShellSettings.Settings.dynamicTilingEnabled
|
||||||
|
root._gamingSessionActive = true
|
||||||
MobileShellState.ShellDBusClient.doNotDisturb = true
|
MobileShellState.ShellDBusClient.doNotDisturb = true
|
||||||
|
ShellSettings.Settings.dynamicTilingEnabled = false
|
||||||
|
|
||||||
|
if (MobileShellState.ShellDBusClient.isActionDrawerOpen) {
|
||||||
|
MobileShellState.ShellDBusClient.closeActionDrawer()
|
||||||
|
}
|
||||||
|
folio.HomeScreenState.closeFolder()
|
||||||
|
folio.HomeScreenState.closeSearchWidget()
|
||||||
|
folio.HomeScreenState.closeAppDrawer()
|
||||||
|
if (folio.HomeScreenState.viewState === Folio.HomeScreenState.SettingsView) {
|
||||||
|
folio.HomeScreenState.closeSettingsView()
|
||||||
|
}
|
||||||
|
|
||||||
if (GamingShell.PowerProfileControl.available) {
|
if (GamingShell.PowerProfileControl.available) {
|
||||||
root._savedPowerProfile = GamingShell.PowerProfileControl.activeProfile
|
root._savedPowerProfile = GamingShell.PowerProfileControl.activeProfile
|
||||||
|
|
@ -65,17 +79,17 @@ ContainmentItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
GamingShell.GameModeControl.requestStart()
|
GamingShell.GameModeControl.requestStart()
|
||||||
root._gamingSessionActive = true
|
|
||||||
} else {
|
} else {
|
||||||
// Restore previous state
|
// Restore previous state
|
||||||
|
root._gamingSessionActive = false
|
||||||
MobileShellState.ShellDBusClient.doNotDisturb = root._savedDnd
|
MobileShellState.ShellDBusClient.doNotDisturb = root._savedDnd
|
||||||
|
ShellSettings.Settings.dynamicTilingEnabled = root._savedDynamicTiling
|
||||||
|
|
||||||
if (GamingShell.PowerProfileControl.available && root._savedPowerProfile.length > 0) {
|
if (GamingShell.PowerProfileControl.available && root._savedPowerProfile.length > 0) {
|
||||||
GamingShell.PowerProfileControl.activeProfile = root._savedPowerProfile
|
GamingShell.PowerProfileControl.activeProfile = root._savedPowerProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
GamingShell.GameModeControl.requestEnd()
|
GamingShell.GameModeControl.requestEnd()
|
||||||
root._gamingSessionActive = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +265,7 @@ ContainmentItem {
|
||||||
// Convergence: no scrim (popup has own background); mobile: dark scrim
|
// Convergence: no scrim (popup has own background); mobile: dark scrim
|
||||||
color: ShellSettings.Settings.convergenceModeEnabled
|
color: ShellSettings.Settings.convergenceModeEnabled
|
||||||
? "transparent"
|
? "transparent"
|
||||||
: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.46)
|
: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.46)
|
||||||
|
|
||||||
opacity: folio.HomeScreenState.appDrawerOpenProgress
|
opacity: folio.HomeScreenState.appDrawerOpenProgress
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +275,7 @@ ContainmentItem {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.30)
|
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.30)
|
||||||
|
|
||||||
opacity: folio.HomeScreenState.searchWidgetOpenProgress
|
opacity: folio.HomeScreenState.searchWidgetOpenProgress
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +285,7 @@ ContainmentItem {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||||
color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.30)
|
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.18, 0.10), 0.30)
|
||||||
|
|
||||||
opacity: folio.HomeScreenState.settingsOpenProgress
|
opacity: folio.HomeScreenState.settingsOpenProgress
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +362,7 @@ ContainmentItem {
|
||||||
readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength
|
readonly property real rightFrameBulgeEdgeTopY: rightFrameBulgeApexY - rightFrameBulgeHalfLength
|
||||||
readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength
|
readonly property real rightFrameBulgeEdgeBottomY: rightFrameBulgeApexY + rightFrameBulgeHalfLength
|
||||||
readonly property real rightFrameBulgeTangent: rightFrameBulgeHalfLength * 0.55
|
readonly property real rightFrameBulgeTangent: rightFrameBulgeHalfLength * 0.55
|
||||||
readonly property color chromeColor: Kirigami.Theme.backgroundColor
|
readonly property color chromeColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
|
||||||
readonly property color edgeColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.1)
|
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)
|
readonly property int dockAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
|
||||||
|
|
||||||
|
|
@ -1430,7 +1444,7 @@ ContainmentItem {
|
||||||
id: drawerSurfacePath
|
id: drawerSurfacePath
|
||||||
readonly property real cornerRadius: drawerSurface.cornerRadius
|
readonly property real cornerRadius: drawerSurface.cornerRadius
|
||||||
|
|
||||||
fillColor: Kirigami.Theme.backgroundColor
|
fillColor: MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.32, 0.18)
|
||||||
strokeWidth: 0
|
strokeWidth: 0
|
||||||
startX: 0
|
startX: 0
|
||||||
startY: 0
|
startY: 0
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Shapes
|
import QtQuick.Shapes
|
||||||
import org.kde.kwin.decoration
|
import org.kde.kwin.decoration
|
||||||
import org.kde.plasma.private.mobileshell as MobileShell
|
|
||||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
|
|
||||||
Decoration {
|
Decoration {
|
||||||
|
|
@ -24,7 +23,7 @@ Decoration {
|
||||||
readonly property int normalCornerRadius: 8
|
readonly property int normalCornerRadius: 8
|
||||||
readonly property int cornerRadius: decoration.client.maximized ? 0 : normalCornerRadius
|
readonly property int cornerRadius: decoration.client.maximized ? 0 : normalCornerRadius
|
||||||
readonly property int frameThickness: decoration.client.maximized ? 0 : normalCornerRadius
|
readonly property int frameThickness: decoration.client.maximized ? 0 : normalCornerRadius
|
||||||
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
readonly property int shortAnimationDuration: 120
|
||||||
readonly property bool windowMenuAllowed: !ShellSettings.Settings.convergenceModeEnabled
|
readonly property bool windowMenuAllowed: !ShellSettings.Settings.convergenceModeEnabled
|
||||||
|| ShellSettings.Settings.gamingModeEnabled
|
|| ShellSettings.Settings.gamingModeEnabled
|
||||||
|| !ShellSettings.Settings.dynamicTilingEnabled
|
|| !ShellSettings.Settings.dynamicTilingEnabled
|
||||||
|
|
@ -94,7 +93,7 @@ Decoration {
|
||||||
height: root.barHeight
|
height: root.barHeight
|
||||||
radius: root.cornerRadius
|
radius: root.cornerRadius
|
||||||
color: decoration.client.active ? root.activeBar : root.inactiveBar
|
color: decoration.client.active ? root.activeBar : root.inactiveBar
|
||||||
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
|
Behavior on color { ColorAnimation { duration: root.shortAnimationDuration; easing.type: Easing.OutCubic } }
|
||||||
|
|
||||||
// Square off bottom half — only top corners are rounded
|
// Square off bottom half — only top corners are rounded
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -135,7 +134,7 @@ Decoration {
|
||||||
elide: Text.ElideMiddle
|
elide: Text.ElideMiddle
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
|
Behavior on color { ColorAnimation { duration: root.shortAnimationDuration; easing.type: Easing.OutCubic } }
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
|
@ -243,7 +242,7 @@ Decoration {
|
||||||
color: parent.pressed ? Qt.darker(parent.hoverColor, 1.3)
|
color: parent.pressed ? Qt.darker(parent.hoverColor, 1.3)
|
||||||
: parent.hovered ? parent.hoverColor
|
: parent.hovered ? parent.hoverColor
|
||||||
: parent.normalColor
|
: parent.normalColor
|
||||||
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
|
Behavior on color { ColorAnimation { duration: root.shortAnimationDuration; easing.type: Easing.OutCubic } }
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
@ -252,7 +251,7 @@ Decoration {
|
||||||
font.pixelSize: Math.round(parent.width * 0.66)
|
font.pixelSize: Math.round(parent.width * 0.66)
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
opacity: 1.0
|
opacity: 1.0
|
||||||
Behavior on opacity { MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration } }
|
Behavior on opacity { NumberAnimation { duration: root.shortAnimationDuration; easing.type: Easing.OutCubic } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import org.kde.kwin as KWinComponents
|
import org.kde.kwin as KWinComponents
|
||||||
import org.kde.plasma.private.mobileshell as MobileShell
|
|
||||||
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
||||||
|
|
||||||
KWinComponents.SceneEffect {
|
KWinComponents.SceneEffect {
|
||||||
|
|
@ -39,7 +38,7 @@ KWinComponents.SceneEffect {
|
||||||
readonly property int panelScreenMargin: 8
|
readonly property int panelScreenMargin: 8
|
||||||
readonly property int panelCursorGap: 12
|
readonly property int panelCursorGap: 12
|
||||||
readonly property int panelCursorRightBias: 34
|
readonly property int panelCursorRightBias: 34
|
||||||
readonly property int hoverAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Press)
|
readonly property int hoverAnimationDuration: 100
|
||||||
property var hoverWindowId: null
|
property var hoverWindowId: null
|
||||||
property int hoverTicks: 0
|
property int hoverTicks: 0
|
||||||
property string hoverWindowStateKey: ""
|
property string hoverWindowStateKey: ""
|
||||||
|
|
@ -651,7 +650,7 @@ KWinComponents.SceneEffect {
|
||||||
|
|
||||||
property bool hovered: false
|
property bool hovered: false
|
||||||
|
|
||||||
Behavior on color { MobileShell.MotionColorAnimation { type: MobileShell.Motion.Press; duration: effect.hoverAnimationDuration } }
|
Behavior on color { ColorAnimation { duration: effect.hoverAnimationDuration; easing.type: Easing.OutCubic } }
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: previewFrame
|
id: previewFrame
|
||||||
|
|
|
||||||
BIN
screenshots/SHIFT-Gaming_Shell.png
Normal file
BIN
screenshots/SHIFT-Gaming_Shell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
2
screenshots/SHIFT-Gaming_Shell.png.license
Normal file
2
screenshots/SHIFT-Gaming_Shell.png.license
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
SPDX-License-Identifier: EUPL-1.2
|
||||||
BIN
screenshots/SHIFT-dynamic_tiling.png
Normal file
BIN
screenshots/SHIFT-dynamic_tiling.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
2
screenshots/SHIFT-dynamic_tiling.png.license
Normal file
2
screenshots/SHIFT-dynamic_tiling.png.license
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||||
|
SPDX-License-Identifier: EUPL-1.2
|
||||||
|
|
@ -27,6 +27,12 @@ kcm_main=kcms/mobileshell/ui/main.qml
|
||||||
kcm_appearance=kcms/mobileshell/ui/AppearanceForm.qml
|
kcm_appearance=kcms/mobileshell/ui/AppearanceForm.qml
|
||||||
desktop_view=shell/contents/views/Desktop.qml
|
desktop_view=shell/contents/views/Desktop.qml
|
||||||
kcm_metadata=kcms/mobileshell/kcm_mobileshell.json
|
kcm_metadata=kcms/mobileshell/kcm_mobileshell.json
|
||||||
|
panel_background=components/mobileshell/qml/components/PanelBackground.qml
|
||||||
|
motion_state_layer=components/mobileshell/qml/components/MotionStateLayer.qml
|
||||||
|
surface_colors=components/mobileshell/qml/components/SurfaceColors.qml
|
||||||
|
folio_main=containments/homescreens/folio/qml/main.qml
|
||||||
|
favourites_bar=containments/homescreens/folio/qml/FavouritesBar.qml
|
||||||
|
landscape_drawer=components/mobileshell/qml/actiondrawer/private/LandscapeContentContainer.qml
|
||||||
|
|
||||||
require_line "$settings_header" 'Q_PROPERTY\(QString colorScheme READ colorScheme NOTIFY colorSchemeChanged\)' \
|
require_line "$settings_header" 'Q_PROPERTY\(QString colorScheme READ colorScheme NOTIFY colorSchemeChanged\)' \
|
||||||
"mobile shell settings must expose the active KDE color scheme"
|
"mobile shell settings must expose the active KDE color scheme"
|
||||||
|
|
@ -148,6 +154,33 @@ require_line "$desktop_view" 'ShellSettings\.Settings\.applyWallpaperThemeColor\
|
||||||
"desktop wallpaper color extraction must feed Shift's wallpaper theme mode"
|
"desktop wallpaper color extraction must feed Shift's wallpaper theme mode"
|
||||||
require_line "$desktop_view" 'onWallpaperThemeEnabledChanged' \
|
require_line "$desktop_view" 'onWallpaperThemeEnabledChanged' \
|
||||||
"desktop wallpaper color extraction must react when wallpaper theme mode is toggled"
|
"desktop wallpaper color extraction must react when wallpaper theme mode is toggled"
|
||||||
|
require_line "$surface_colors" '!ShellSettings\.Settings\.wallpaperThemeEnabled && !ShellSettings\.Settings\.wallpaperAccentEnabled' \
|
||||||
|
"shared shell surface color helper must react to wallpaper/accent theming"
|
||||||
|
require_line "$surface_colors" 'ShellSettings\.Settings\.accentColor' \
|
||||||
|
"shared shell surface color helper must prefer the applied KDE accent color"
|
||||||
|
require_line "$surface_colors" 'ShellSettings\.Settings\.wallpaperThemeColor' \
|
||||||
|
"shared shell surface color helper must fall back to the extracted wallpaper color"
|
||||||
|
require_line "$panel_background" 'SurfaceColors\.accentSurface\(baseColor, accentTintDark, accentTintLight\)' \
|
||||||
|
"shared shell panel backgrounds must react to wallpaper/accent theming"
|
||||||
|
require_line "$surface_colors" 'mix\(base, accent\(\), darkSurface \? darkRatio : lightRatio\)' \
|
||||||
|
"shared shell panel backgrounds must tint surfaces from the active accent color"
|
||||||
|
require_line "$motion_state_layer" 'property color activeColor: SurfaceColors\.accent\(\)' \
|
||||||
|
"shared active state layers must use the same accent source as shell surfaces"
|
||||||
|
require_line "$folio_main" 'readonly property color chromeColor: MobileShell\.SurfaceColors\.accentSurface\(Kirigami\.Theme\.backgroundColor, 0\.32, 0\.18\)' \
|
||||||
|
"convergence top bar, frame, and dock chrome must tint from the active accent color"
|
||||||
|
require_line "$surface_colors" '!ShellSettings\.Settings\.wallpaperThemeEnabled && !ShellSettings\.Settings\.wallpaperAccentEnabled' \
|
||||||
|
"convergence chrome must react to wallpaper/accent theming mode"
|
||||||
|
require_line "$folio_main" 'fillColor: MobileShell\.SurfaceColors\.accentSurface\(Kirigami\.Theme\.backgroundColor, 0\.32, 0\.18\)' \
|
||||||
|
"convergence app drawer surface must tint from the active accent color"
|
||||||
|
require_line "$landscape_drawer" 'fillColor: MobileShell\.SurfaceColors\.accentSurface\(Kirigami\.Theme\.backgroundColor, 0\.32, 0\.18\)' \
|
||||||
|
"convergence action drawer surface must tint from the active accent color"
|
||||||
|
require_line "$favourites_bar" 'color: MobileShell\.SurfaceColors\.accentSurface\(Kirigami\.Theme\.backgroundColor, 0\.24, 0\.12\)' \
|
||||||
|
"dock thumbnail popup surfaces must tint from the active accent color"
|
||||||
|
require_line "$favourites_bar" 'color: leftDesktopBtn\.isCurrent \? MobileShell\.SurfaceColors\.accent\(\) : Kirigami\.Theme\.textColor' \
|
||||||
|
"dock workspace pager labels must use the same accent source as shell surfaces"
|
||||||
|
if grep -Eq -- 'Kirigami\.Theme\.highlightColor' "$favourites_bar"; then
|
||||||
|
fail "convergence dock must not use Kirigami.Theme.highlightColor directly; use MobileShell.SurfaceColors.accent()"
|
||||||
|
fi
|
||||||
require_line tests/CMakeLists.txt 'NAME shift-dynamic-theming' \
|
require_line tests/CMakeLists.txt 'NAME shift-dynamic-theming' \
|
||||||
"dynamic theming regression test must be registered with CTest"
|
"dynamic theming regression test must be registered with CTest"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue