Open app drawer over windows in convergence mode

Drawer is inside the homescreen, which sits behind windows.
Home button minimized everything to reach it. Render it in
a LayerTop window instead and skip the blanket minimizeAll
in the D-Bus handler.
This commit is contained in:
Marco Allegretti 2026-04-15 10:18:34 +02:00
parent c61642265b
commit 7fa2c20ab4
3 changed files with 128 additions and 41 deletions

View file

@ -80,6 +80,13 @@ Item {
target: MobileShellState.ShellDBusClient target: MobileShellState.ShellDBusClient
function onOpenHomeScreenRequested() { function onOpenHomeScreenRequested() {
if (ShellSettings.Settings.convergenceModeEnabled) {
// In convergence mode let the containment handle everything
// via homeTriggered homeAction() without touching windows.
root.homeTriggered();
return;
}
if (windowMaximizedTracker.showingWindow) { if (windowMaximizedTracker.showingWindow) {
itemContainer.zoomIn(); itemContainer.zoomIn();
} }

View file

@ -515,26 +515,22 @@ Item {
transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height } transform: Translate { y: folderView.opacity > 0 ? 0 : folderView.height }
} }
// Click-to-dismiss overlay for popup drawer in convergence mode // Click-to-dismiss overlay (mobile only; convergence uses layer-shell overlay)
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
anchors.bottomMargin: ShellSettings.Settings.convergenceModeEnabled ? favouritesBar.height : 0 visible: !ShellSettings.Settings.convergenceModeEnabled
visible: ShellSettings.Settings.convergenceModeEnabled && homeScreenState.appDrawerOpenProgress > 0 && homeScreenState.appDrawerOpenProgress > 0
onClicked: folio.HomeScreenState.closeAppDrawer() onClicked: folio.HomeScreenState.closeAppDrawer()
} }
// bottom app drawer // bottom app drawer (mobile only; convergence uses layer-shell overlay)
AppDrawer { AppDrawer {
id: appDrawer id: appDrawer
folio: root.folio folio: root.folio
visible: !ShellSettings.Settings.convergenceModeEnabled
// Convergence: popup above dock; mobile: full-screen width: parent.width
property bool isPopup: ShellSettings.Settings.convergenceModeEnabled height: parent.height
property real popupWidth: Math.min(Kirigami.Units.gridUnit * 28, parent.width * 0.5)
property real popupHeight: Math.min(Kirigami.Units.gridUnit * 32, parent.height * 0.7)
width: isPopup ? popupWidth : parent.width
height: isPopup ? popupHeight : parent.height
homeScreen: root homeScreen: root
@ -544,20 +540,11 @@ Item {
// position for animation // position for animation
property real animationY: (1 - homeScreenState.appDrawerOpenProgress) * (Kirigami.Units.gridUnit * 2) property real animationY: (1 - homeScreenState.appDrawerOpenProgress) * (Kirigami.Units.gridUnit * 2)
// Convergence popup position: above dock, left-aligned
property real popupX: root.leftMargin + Kirigami.Units.smallSpacing
property real popupY: (opacity > 0)
? parent.height - favouritesBar.height - popupHeight - Kirigami.Units.smallSpacing + animationY
: parent.height
// Full-screen position // Full-screen position
property real fullX: 0 x: 0
property real fullY: (opacity > 0) ? animationY : parent.height y: (opacity > 0) ? animationY : parent.height
x: isPopup ? popupX : fullX headerHeight: Math.round(Kirigami.Units.gridUnit * 4)
y: isPopup ? popupY : fullY
headerHeight: Math.round(Kirigami.Units.gridUnit * (appDrawer.isPopup ? 3 : 4))
headerItem: AppDrawerHeader { headerItem: AppDrawerHeader {
id: appDrawerHeader id: appDrawerHeader
folio: root.folio folio: root.folio
@ -565,11 +552,11 @@ Item {
onReleaseFocusRequested: appDrawer.forceActiveFocus() onReleaseFocusRequested: appDrawer.forceActiveFocus()
} }
// Account for panels (popup handles its own margins) // Account for panels
topPadding: appDrawer.isPopup ? 0 : root.topMargin topPadding: root.topMargin
bottomPadding: appDrawer.isPopup ? 0 : root.bottomMargin bottomPadding: root.bottomMargin
leftPadding: appDrawer.isPopup ? 0 : root.leftMargin leftPadding: root.leftMargin
rightPadding: appDrawer.isPopup ? 0 : root.rightMargin rightPadding: root.rightMargin
// Forward keyboard text to the search bar // Forward keyboard text to the search bar
Keys.onPressed: (event) => { Keys.onPressed: (event) => {

View file

@ -85,19 +85,36 @@ ContainmentItem {
MobileShellState.ShellDBusClient.closeActionDrawer(); MobileShellState.ShellDBusClient.closeActionDrawer();
} }
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) { if (isInWindow) {
// Only minimize windows and go to homescreen when not in docked mode
if (!ShellSettings.Settings.convergenceModeEnabled) {
folio.HomeScreenState.closeFolder(); folio.HomeScreenState.closeFolder();
folio.HomeScreenState.closeSearchWidget(); folio.HomeScreenState.closeSearchWidget();
folio.HomeScreenState.closeAppDrawer(); folio.HomeScreenState.closeAppDrawer();
folio.HomeScreenState.goToPage(0, false); folio.HomeScreenState.goToPage(0, false);
WindowPlugin.WindowUtil.minimizeAll(); WindowPlugin.WindowUtil.minimizeAll();
} else {
// In convergence mode, toggle "show desktop" (minimize all to reveal homescreen)
WindowPlugin.WindowUtil.minimizeAll();
}
// Always ensure settings view is closed // Always ensure settings view is closed
if (folio.HomeScreenState.viewState == Folio.HomeScreenState.SettingsView) { if (folio.HomeScreenState.viewState == Folio.HomeScreenState.SettingsView) {
@ -107,7 +124,7 @@ ContainmentItem {
} else { // If we are already on the homescreen } else { // If we are already on the homescreen
switch (folio.HomeScreenState.viewState) { switch (folio.HomeScreenState.viewState) {
case Folio.HomeScreenState.PageView: case Folio.HomeScreenState.PageView:
if (ShellSettings.Settings.convergenceModeEnabled || folio.HomeScreenState.currentPage === 0) { if (folio.HomeScreenState.currentPage === 0) {
folio.HomeScreenState.openAppDrawer(); folio.HomeScreenState.openAppDrawer();
} else { } else {
folio.HomeScreenState.goToPage(0, false); folio.HomeScreenState.goToPage(0, false);
@ -221,6 +238,82 @@ ContainmentItem {
} }
} }
// 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
&& 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 popupHeight: Math.min(Kirigami.Units.gridUnit * 32, parent.height * 0.7)
readonly property real dockHeight: Kirigami.Units.gridUnit * 3
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: Kirigami.Units.smallSpacing
y: (opacity > 0)
? parent.height - dockHeight - popupHeight - Kirigami.Units.smallSpacing + 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() {
overlayDrawer.forceActiveFocus();
}
}
}
}
MobileShell.HomeScreen { MobileShell.HomeScreen {
id: homeScreen id: homeScreen
anchors.fill: parent anchors.fill: parent