mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 00:47:22 +00:00
Add dynamic tiling layout menu
Expose layout requests and state through shell settings so Folio can offer alternatives for the current tiled workspace. Keep KWin as the owner of layout changes, snapshot request serials so requests are not dropped, and hide the edge menu when fewer than two windows are tiled.
This commit is contained in:
parent
1702027f7e
commit
97abf33597
7 changed files with 662 additions and 2 deletions
|
|
@ -49,6 +49,8 @@ MobileShellSettings::MobileShellSettings(QObject *parent)
|
|||
Q_EMIT dynamicTilingEnabledChanged();
|
||||
Q_EMIT dynamicTilingWindowRequestChanged();
|
||||
Q_EMIT dynamicTilingWindowStateChanged();
|
||||
Q_EMIT dynamicTilingLayoutRequestChanged();
|
||||
Q_EMIT dynamicTilingLayoutStateChanged();
|
||||
Q_EMIT snapLayoutsEnabledChanged();
|
||||
Q_EMIT allowLogoutChanged();
|
||||
}
|
||||
|
|
@ -371,6 +373,69 @@ void MobileShellSettings::reportDynamicTilingWindowState(const QStringList &maxi
|
|||
Q_EMIT dynamicTilingWindowStateChanged();
|
||||
}
|
||||
|
||||
QString MobileShellSettings::dynamicTilingLayoutRequestMode() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
return group.readEntry("dynamicTilingLayoutRequestMode", QString{});
|
||||
}
|
||||
|
||||
int MobileShellSettings::dynamicTilingLayoutRequestSerial() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
return group.readEntry("dynamicTilingLayoutRequestSerial", 0);
|
||||
}
|
||||
|
||||
void MobileShellSettings::requestDynamicTilingLayoutMode(const QString &mode)
|
||||
{
|
||||
if (mode.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
const int serial = group.readEntry("dynamicTilingLayoutRequestSerial", 0) + 1;
|
||||
group.writeEntry("dynamicTilingLayoutRequestMode", mode, KConfigGroup::Notify);
|
||||
group.writeEntry("dynamicTilingLayoutRequestSerial", serial, KConfigGroup::Notify);
|
||||
m_config->sync();
|
||||
|
||||
Q_EMIT dynamicTilingLayoutRequestChanged();
|
||||
}
|
||||
|
||||
QString MobileShellSettings::dynamicTilingLayoutMode() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
return group.readEntry("dynamicTilingLayoutMode", QString{});
|
||||
}
|
||||
|
||||
int MobileShellSettings::dynamicTilingLayoutWindowCount() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
return group.readEntry("dynamicTilingLayoutWindowCount", 0);
|
||||
}
|
||||
|
||||
int MobileShellSettings::dynamicTilingLayoutStateSerial() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
return group.readEntry("dynamicTilingLayoutStateSerial", 0);
|
||||
}
|
||||
|
||||
void MobileShellSettings::reportDynamicTilingLayoutState(const QString &mode, int windowCount)
|
||||
{
|
||||
const int normalizedWindowCount = windowCount < 0 ? 0 : windowCount;
|
||||
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
if (group.readEntry("dynamicTilingLayoutMode", QString{}) == mode && group.readEntry("dynamicTilingLayoutWindowCount", 0) == normalizedWindowCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int serial = group.readEntry("dynamicTilingLayoutStateSerial", 0) + 1;
|
||||
group.writeEntry("dynamicTilingLayoutMode", mode, KConfigGroup::Notify);
|
||||
group.writeEntry("dynamicTilingLayoutWindowCount", normalizedWindowCount, KConfigGroup::Notify);
|
||||
group.writeEntry("dynamicTilingLayoutStateSerial", serial, KConfigGroup::Notify);
|
||||
m_config->sync();
|
||||
|
||||
Q_EMIT dynamicTilingLayoutStateChanged();
|
||||
}
|
||||
|
||||
bool MobileShellSettings::snapLayoutsEnabled() const
|
||||
{
|
||||
auto group = KConfigGroup{m_config, GENERAL_CONFIG_GROUP};
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ class MobileShellSettings : public QObject
|
|||
Q_PROPERTY(int dynamicTilingWindowRequestSerial READ dynamicTilingWindowRequestSerial NOTIFY dynamicTilingWindowRequestChanged)
|
||||
Q_PROPERTY(QStringList dynamicTilingMaximizedWindowIds READ dynamicTilingMaximizedWindowIds NOTIFY dynamicTilingWindowStateChanged)
|
||||
Q_PROPERTY(int dynamicTilingWindowStateSerial READ dynamicTilingWindowStateSerial NOTIFY dynamicTilingWindowStateChanged)
|
||||
Q_PROPERTY(QString dynamicTilingLayoutRequestMode READ dynamicTilingLayoutRequestMode NOTIFY dynamicTilingLayoutRequestChanged)
|
||||
Q_PROPERTY(int dynamicTilingLayoutRequestSerial READ dynamicTilingLayoutRequestSerial NOTIFY dynamicTilingLayoutRequestChanged)
|
||||
Q_PROPERTY(QString dynamicTilingLayoutMode READ dynamicTilingLayoutMode NOTIFY dynamicTilingLayoutStateChanged)
|
||||
Q_PROPERTY(int dynamicTilingLayoutWindowCount READ dynamicTilingLayoutWindowCount NOTIFY dynamicTilingLayoutStateChanged)
|
||||
Q_PROPERTY(int dynamicTilingLayoutStateSerial READ dynamicTilingLayoutStateSerial NOTIFY dynamicTilingLayoutStateChanged)
|
||||
|
||||
// Snap layout picker — only meaningful in convergence mode when dynamic tiling is off.
|
||||
Q_PROPERTY(bool snapLayoutsEnabled READ snapLayoutsEnabled WRITE setSnapLayoutsEnabled NOTIFY snapLayoutsEnabledChanged)
|
||||
|
|
@ -302,6 +307,13 @@ public:
|
|||
int dynamicTilingWindowStateSerial() const;
|
||||
Q_INVOKABLE bool isDynamicTilingWindowMaximized(const QString &windowId) const;
|
||||
Q_INVOKABLE void reportDynamicTilingWindowState(const QStringList &maximizedWindowIds);
|
||||
QString dynamicTilingLayoutRequestMode() const;
|
||||
int dynamicTilingLayoutRequestSerial() const;
|
||||
Q_INVOKABLE void requestDynamicTilingLayoutMode(const QString &mode);
|
||||
QString dynamicTilingLayoutMode() const;
|
||||
int dynamicTilingLayoutWindowCount() const;
|
||||
int dynamicTilingLayoutStateSerial() const;
|
||||
Q_INVOKABLE void reportDynamicTilingLayoutState(const QString &mode, int windowCount);
|
||||
|
||||
/**
|
||||
* Whether the SHIFT snap layout picker is enabled.
|
||||
|
|
@ -362,6 +374,8 @@ Q_SIGNALS:
|
|||
void dynamicTilingEnabledChanged();
|
||||
void dynamicTilingWindowRequestChanged();
|
||||
void dynamicTilingWindowStateChanged();
|
||||
void dynamicTilingLayoutRequestChanged();
|
||||
void dynamicTilingLayoutStateChanged();
|
||||
void snapLayoutsEnabledChanged();
|
||||
void allowLogoutChanged();
|
||||
void lockscreenLeftButtonActionChanged();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ plasma_add_applet(org.kde.plasma.mobile.homescreen.folio
|
|||
qml/CategoryPanel.qml
|
||||
qml/DelegateDragItem.qml
|
||||
qml/DelegateDropArea.qml
|
||||
qml/DynamicTilingLayoutMenu.qml
|
||||
qml/FavouritesBar.qml
|
||||
qml/FolderView.qml
|
||||
qml/FolderViewTitle.qml
|
||||
|
|
|
|||
298
containments/homescreens/folio/qml/DynamicTilingLayoutMenu.qml
Normal file
298
containments/homescreens/folio/qml/DynamicTilingLayoutMenu.qml
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Shapes 1.8
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||
import org.kde.plasma.private.mobileshell as MobileShell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int windowCount: 0
|
||||
property string currentMode: ""
|
||||
property color surfaceColor: Kirigami.Theme.backgroundColor
|
||||
property int animationDuration: Kirigami.Units.shortDuration
|
||||
property real maxHeight: 0
|
||||
|
||||
signal layoutModeRequested(string mode)
|
||||
signal dismissRequested()
|
||||
|
||||
readonly property int clampedWindowCount: clampedLayoutWindowCount(windowCount)
|
||||
readonly property var layoutOptions: layoutOptionsForWindowCount(clampedWindowCount)
|
||||
readonly property int optionCount: layoutOptions.length
|
||||
readonly property real rowHeight: Math.max(Kirigami.Units.gridUnit * 2.4,
|
||||
Kirigami.Units.iconSizes.medium + Kirigami.Units.smallSpacing * 2)
|
||||
readonly property real naturalHeight: Kirigami.Units.gridUnit * 2.2
|
||||
+ Math.max(1, optionCount) * rowHeight
|
||||
+ Kirigami.Units.smallSpacing * 3
|
||||
readonly property real preferredHeight: maxHeight > 0 ? Math.min(naturalHeight, maxHeight) : naturalHeight
|
||||
readonly property real cornerRadius: Math.min(MobileShell.Constants.convergenceWorkspaceFrameRadius, height * 0.24)
|
||||
|
||||
clip: true
|
||||
|
||||
function clampedLayoutWindowCount(windowCount) {
|
||||
const count = Math.round(Number(windowCount) || 0)
|
||||
return Math.max(0, Math.min(4, count))
|
||||
}
|
||||
|
||||
function linearLayoutZones(windowCount, orientation) {
|
||||
const count = Math.max(1, clampedLayoutWindowCount(windowCount))
|
||||
let zones = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (orientation === "horizontal") {
|
||||
zones.push({ x: 0, y: i / count, w: 1, h: 1 / count })
|
||||
} else {
|
||||
zones.push({ x: i / count, y: 0, w: 1 / count, h: 1 })
|
||||
}
|
||||
}
|
||||
return zones
|
||||
}
|
||||
|
||||
function masterLayoutZones(windowCount) {
|
||||
const count = clampedLayoutWindowCount(windowCount)
|
||||
if (count <= 2) {
|
||||
return linearLayoutZones(Math.max(1, count), "vertical")
|
||||
}
|
||||
|
||||
let zones = [{ x: 0, y: 0, w: 0.58, h: 1 }]
|
||||
const stackCount = count - 1
|
||||
for (let i = 0; i < stackCount; i++) {
|
||||
zones.push({ x: 0.58, y: i / stackCount, w: 0.42, h: 1 / stackCount })
|
||||
}
|
||||
return zones
|
||||
}
|
||||
|
||||
function layoutOptionsForWindowCount(windowCount) {
|
||||
const count = clampedLayoutWindowCount(windowCount)
|
||||
if (count < 2) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (count === 2) {
|
||||
return [
|
||||
{
|
||||
mode: "columns",
|
||||
selectedModes: ["master", "columns"],
|
||||
name: i18n("Side by Side"),
|
||||
summary: i18n("2 columns"),
|
||||
zones: linearLayoutZones(count, "vertical")
|
||||
},
|
||||
{
|
||||
mode: "rows",
|
||||
selectedModes: ["rows"],
|
||||
name: i18n("Stacked"),
|
||||
summary: i18n("2 rows"),
|
||||
zones: linearLayoutZones(count, "horizontal")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
mode: "master",
|
||||
selectedModes: ["master"],
|
||||
name: i18n("Master Stack"),
|
||||
summary: i18n("1 + %1 stack", count - 1),
|
||||
zones: masterLayoutZones(count)
|
||||
},
|
||||
{
|
||||
mode: "columns",
|
||||
selectedModes: ["columns"],
|
||||
name: i18n("Columns"),
|
||||
summary: i18n("%1 columns", count),
|
||||
zones: linearLayoutZones(count, "vertical")
|
||||
},
|
||||
{
|
||||
mode: "rows",
|
||||
selectedModes: ["rows"],
|
||||
name: i18n("Rows"),
|
||||
summary: i18n("%1 rows", count),
|
||||
zones: linearLayoutZones(count, "horizontal")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function emptyLayoutSummary() {
|
||||
return clampedWindowCount === 1 ? i18n("1 window") : i18n("0 windows")
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
MobileShell.MotionNumberAnimation {
|
||||
type: MobileShell.Motion.EffectsFast
|
||||
duration: root.animationDuration
|
||||
}
|
||||
}
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
|
||||
ShapePath {
|
||||
fillColor: root.surfaceColor
|
||||
strokeWidth: 0
|
||||
|
||||
startX: root.width
|
||||
startY: 0
|
||||
PathLine { x: root.cornerRadius; y: 0 }
|
||||
PathArc {
|
||||
x: 0
|
||||
y: root.cornerRadius
|
||||
radiusX: root.cornerRadius
|
||||
radiusY: root.cornerRadius
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine { x: 0; y: root.height - root.cornerRadius }
|
||||
PathArc {
|
||||
x: root.cornerRadius
|
||||
y: root.height
|
||||
radiusX: root.cornerRadius
|
||||
radiusY: root.cornerRadius
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine { x: root.width; y: root.height }
|
||||
PathLine { x: root.width; y: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Tiling Layout")
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.layoutOptions
|
||||
|
||||
delegate: MouseArea {
|
||||
id: optionButton
|
||||
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.rowHeight
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
readonly property bool selected: modelData.selectedModes.indexOf(root.currentMode) >= 0
|
||||
|
||||
onClicked: {
|
||||
if (!selected) {
|
||||
root.layoutModeRequested(modelData.mode)
|
||||
}
|
||||
root.dismissRequested()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: optionButton.selected
|
||||
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.24)
|
||||
: optionButton.containsMouse
|
||||
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
|
||||
: "transparent"
|
||||
border.width: optionButton.selected ? 1 : 0
|
||||
border.color: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.5)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Rectangle {
|
||||
id: layoutPreviewFrame
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 2.5
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 1.65
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.06)
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.18)
|
||||
|
||||
Repeater {
|
||||
model: optionButton.modelData.zones
|
||||
|
||||
delegate: Rectangle {
|
||||
required property var modelData
|
||||
|
||||
x: Math.round(layoutPreviewFrame.width * modelData.x) + Kirigami.Units.smallSpacing / 2
|
||||
y: Math.round(layoutPreviewFrame.height * modelData.y) + Kirigami.Units.smallSpacing / 2
|
||||
width: Math.max(1, Math.round(layoutPreviewFrame.width * modelData.w) - Kirigami.Units.smallSpacing)
|
||||
height: Math.max(1, Math.round(layoutPreviewFrame.height * modelData.h) - Kirigami.Units.smallSpacing)
|
||||
radius: Math.max(1, Kirigami.Units.cornerRadius - 1)
|
||||
color: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.58)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
text: optionButton.modelData.name
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
text: optionButton.modelData.summary
|
||||
opacity: 0.62
|
||||
font: Kirigami.Theme.smallFont
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: root.optionCount === 0
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: i18n("No Alternatives")
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
PlasmaComponents.Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.emptyLayoutSummary()
|
||||
opacity: 0.62
|
||||
font: Kirigami.Theme.smallFont
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -306,14 +306,23 @@ ContainmentItem {
|
|||
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 leftFrameBulgeIdleDepth: Math.max(frameThickness * 1.2, Kirigami.Units.gridUnit * 0.35)
|
||||
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.
|
||||
|
|
@ -326,6 +335,13 @@ ContainmentItem {
|
|||
// 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)
|
||||
|
|
@ -339,6 +355,9 @@ ContainmentItem {
|
|||
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
|
||||
|
|
@ -349,12 +368,21 @@ ContainmentItem {
|
|||
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 {
|
||||
|
|
@ -365,6 +393,10 @@ ContainmentItem {
|
|||
regions.push(leftLauncherRegion)
|
||||
}
|
||||
|
||||
if (layoutMenuEnabled && layoutMenuOpen) {
|
||||
regions.push(layoutMenuRegion)
|
||||
}
|
||||
|
||||
MobileShell.ShellUtil.setInputRegions(convergenceChrome, regions)
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +429,25 @@ ContainmentItem {
|
|||
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()
|
||||
}
|
||||
|
||||
function updateLeftLauncherHoverState(pointerX, pointerY, withinWindow) {
|
||||
const insideEdge = withinWindow
|
||||
&& pointerX >= 0
|
||||
|
|
@ -421,6 +472,30 @@ ContainmentItem {
|
|||
refreshLeftLauncherVisibility()
|
||||
}
|
||||
|
||||
function updateLayoutMenuHoverState(pointerX, pointerY, withinWindow) {
|
||||
const insideEdge = layoutMenuEnabled
|
||||
&& withinWindow
|
||||
&& pointerX >= (width - rightEdgeHotzoneWidth)
|
||||
&& pointerX <= width
|
||||
&& pointerY >= topBarHitHeight
|
||||
&& pointerY <= (height - dockHeight)
|
||||
|
||||
const insideMenu = withinWindow
|
||||
&& layoutMenuOpen
|
||||
&& pointerX >= rightLayoutMenu.x
|
||||
&& pointerX <= width
|
||||
&& pointerY >= rightLayoutMenu.y
|
||||
&& pointerY <= (rightLayoutMenu.y + rightLayoutMenu.height)
|
||||
|
||||
if (rightEdgeHovered !== insideEdge) {
|
||||
rightEdgeHovered = insideEdge
|
||||
}
|
||||
if (layoutMenuHovered !== insideMenu) {
|
||||
layoutMenuHovered = insideMenu
|
||||
}
|
||||
refreshLayoutMenuVisibility()
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
hoverRevealTimer.stop()
|
||||
hoverRevealing = false
|
||||
|
|
@ -438,6 +513,7 @@ ContainmentItem {
|
|||
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
|
||||
|
|
@ -484,6 +560,20 @@ ContainmentItem {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -498,6 +588,13 @@ ContainmentItem {
|
|||
}
|
||||
}
|
||||
|
||||
Behavior on rightFrameBulgeDepth {
|
||||
MobileShell.MotionNumberAnimation {
|
||||
type: MobileShell.Motion.SpatialDefault
|
||||
duration: root.shortAnimationDuration
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: topBarSurface
|
||||
anchors.top: parent.top
|
||||
|
|
@ -534,6 +631,23 @@ ContainmentItem {
|
|||
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 }
|
||||
|
|
@ -580,6 +694,23 @@ ContainmentItem {
|
|||
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
|
||||
|
|
@ -672,6 +803,15 @@ ContainmentItem {
|
|||
width: convergenceChrome.leftEdgeHotzoneWidth
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rightEdgeStrip
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.top: topBarSurface.bottom
|
||||
anchors.bottom: dockSurface.top
|
||||
width: convergenceChrome.layoutMenuEnabled ? convergenceChrome.rightEdgeHotzoneWidth : 0
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: leftLauncherPointerTracker
|
||||
|
||||
|
|
@ -682,9 +822,43 @@ ContainmentItem {
|
|||
|
||||
onPositionChanged: (mouse) => {
|
||||
convergenceChrome.updateLeftLauncherHoverState(mouse.x, mouse.y, true)
|
||||
convergenceChrome.updateLayoutMenuHoverState(mouse.x, mouse.y, true)
|
||||
}
|
||||
onExited: {
|
||||
convergenceChrome.updateLeftLauncherHoverState(-1, -1, false)
|
||||
convergenceChrome.updateLayoutMenuHoverState(-1, -1, false)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,10 @@ Item {
|
|||
// Values keep callback references so KWin script reloads can disconnect them.
|
||||
property var dragConnectedWindows: ({})
|
||||
|
||||
property int lastWindowRequestSerial: ShellSettings.Settings.dynamicTilingWindowRequestSerial
|
||||
property int lastWindowRequestSerial: -1
|
||||
property string lastPublishedMaximizedWindowIds: "__unpublished__"
|
||||
property int lastLayoutRequestSerial: -1
|
||||
property string lastPublishedLayoutState: "__unpublished__"
|
||||
|
||||
// Drag state.
|
||||
//
|
||||
|
|
@ -504,6 +506,7 @@ Item {
|
|||
const focused = Object.assign({}, lastFocusedWindowKeys);
|
||||
delete focused[outputName];
|
||||
lastFocusedWindowKeys = focused;
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function clearDisplacedWindowOwnersForLayout(outputName) {
|
||||
|
|
@ -654,6 +657,8 @@ Item {
|
|||
for (let i = 0; i < cleanupLayouts.length; i++) {
|
||||
cleanupEmptyLayout(cleanupLayouts[i]);
|
||||
}
|
||||
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function makeLeaf(win) {
|
||||
|
|
@ -842,6 +847,15 @@ Item {
|
|||
const win = KWinComponents.Workspace.activeWindow;
|
||||
const activeName = layoutKeyForWindow(win);
|
||||
if (activeName !== "") return activeName;
|
||||
const desktop = KWinComponents.Workspace.currentDesktop;
|
||||
const screens = KWinComponents.Workspace.screens;
|
||||
if (desktop && screens && screens.length > 0) {
|
||||
for (let i = 0; i < screens.length; i++) {
|
||||
const currentDesktopName = layoutKeyFor(screens[i].name, desktop);
|
||||
if (screenLayouts[currentDesktopName]) return currentDesktopName;
|
||||
}
|
||||
return layoutKeyFor(screens[0].name, desktop);
|
||||
}
|
||||
for (const name in screenLayouts) {
|
||||
return name;
|
||||
}
|
||||
|
|
@ -863,6 +877,21 @@ Item {
|
|||
markLayoutChanged(transaction, outputName);
|
||||
applyLayoutTransaction(transaction);
|
||||
}
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function setLayoutMode(outputName, mode) {
|
||||
if (!outputName || layoutModes.indexOf(mode) < 0) return;
|
||||
setLayoutModeForScreen(outputName, mode);
|
||||
|
||||
const windows = orderedWindowsForScreen(outputName);
|
||||
if (windows.length > 0) {
|
||||
setStableLayout(outputName, windows);
|
||||
const transaction = createLayoutTransaction();
|
||||
markLayoutChanged(transaction, outputName);
|
||||
applyLayoutTransaction(transaction);
|
||||
}
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function containsLeaf(node, key) {
|
||||
|
|
@ -1281,6 +1310,37 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function publishDynamicTilingLayoutState() {
|
||||
if (!isConvergence()) {
|
||||
const disabledState = "|0";
|
||||
if (disabledState === lastPublishedLayoutState) return;
|
||||
|
||||
lastPublishedLayoutState = disabledState;
|
||||
if (ShellSettings.Settings.reportDynamicTilingLayoutState !== undefined) {
|
||||
ShellSettings.Settings.reportDynamicTilingLayoutState("", 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const outputName = outputNameForActiveWindow();
|
||||
const mode = outputName !== "" ? layoutModeForScreen(outputName) : "";
|
||||
const windowCount = outputName !== "" ? windowCountForLayout(outputName) : 0;
|
||||
const serialized = mode + "|" + windowCount;
|
||||
if (serialized === lastPublishedLayoutState) return;
|
||||
|
||||
lastPublishedLayoutState = serialized;
|
||||
if (ShellSettings.Settings.reportDynamicTilingLayoutState !== undefined) {
|
||||
ShellSettings.Settings.reportDynamicTilingLayoutState(mode, windowCount);
|
||||
}
|
||||
}
|
||||
|
||||
function clearDynamicTilingLayoutState() {
|
||||
lastPublishedLayoutState = "__unpublished__";
|
||||
if (ShellSettings.Settings.reportDynamicTilingLayoutState !== undefined) {
|
||||
ShellSettings.Settings.reportDynamicTilingLayoutState("", 0);
|
||||
}
|
||||
}
|
||||
|
||||
function maximizedLayoutNameForWindow(win) {
|
||||
const key = windowKey(win);
|
||||
if (!key) return "";
|
||||
|
|
@ -1682,6 +1742,19 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
function handleLayoutModeRequest() {
|
||||
const serial = ShellSettings.Settings.dynamicTilingLayoutRequestSerial;
|
||||
if (serial === lastLayoutRequestSerial) return;
|
||||
lastLayoutRequestSerial = serial;
|
||||
|
||||
if (!isConvergence()) return;
|
||||
|
||||
const mode = ShellSettings.Settings.dynamicTilingLayoutRequestMode;
|
||||
if (layoutModes.indexOf(mode) < 0) return;
|
||||
|
||||
setLayoutMode(outputNameForActiveWindow(), mode);
|
||||
}
|
||||
|
||||
function promoteWindow(win) {
|
||||
if (!isTileable(win)) return;
|
||||
|
||||
|
|
@ -1771,10 +1844,12 @@ Item {
|
|||
|
||||
function onActiveWindowChanged() {
|
||||
root.rememberFocusedWindow(KWinComponents.Workspace.activeWindow);
|
||||
root.publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function onCurrentDesktopChanged() {
|
||||
root.retileCurrentDesktopLayouts();
|
||||
root.publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
function onScreensChanged() {
|
||||
|
|
@ -1788,6 +1863,7 @@ Item {
|
|||
}
|
||||
|
||||
root.scheduleRetileAll();
|
||||
root.publishDynamicTilingLayoutState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1801,6 +1877,7 @@ Item {
|
|||
for (let i = 0; i < wins.length; i++) {
|
||||
adoptWindow(wins[i]);
|
||||
}
|
||||
publishDynamicTilingLayoutState();
|
||||
} else {
|
||||
// Clear all tiles — the convergentwindows script will re-maximize
|
||||
restoreAllMaximizedLayouts();
|
||||
|
|
@ -1809,6 +1886,7 @@ Item {
|
|||
screenLayoutModes = {};
|
||||
lastFocusedWindowKeys = {};
|
||||
maximizedLayouts = {};
|
||||
clearDynamicTilingLayoutState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1820,11 +1898,13 @@ Item {
|
|||
screenLayoutModes = {};
|
||||
lastFocusedWindowKeys = {};
|
||||
maximizedLayouts = {};
|
||||
clearDynamicTilingLayoutState();
|
||||
} else if (isConvergence()) {
|
||||
const wins = KWinComponents.Workspace.windows;
|
||||
for (let i = 0; i < wins.length; i++) {
|
||||
adoptWindow(wins[i]);
|
||||
}
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1834,6 +1914,7 @@ Item {
|
|||
for (let i = 0; i < wins.length; i++) {
|
||||
adoptWindow(wins[i]);
|
||||
}
|
||||
publishDynamicTilingLayoutState();
|
||||
} else {
|
||||
// Tiling turned off — leave windows where they are.
|
||||
restoreAllMaximizedLayouts();
|
||||
|
|
@ -1842,12 +1923,17 @@ Item {
|
|||
screenLayoutModes = {};
|
||||
lastFocusedWindowKeys = {};
|
||||
maximizedLayouts = {};
|
||||
clearDynamicTilingLayoutState();
|
||||
}
|
||||
}
|
||||
|
||||
function onDynamicTilingWindowRequestChanged() {
|
||||
root.handleWindowTilingRequest();
|
||||
}
|
||||
|
||||
function onDynamicTilingLayoutRequestChanged() {
|
||||
root.handleLayoutModeRequest();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Drag handlers ─────────────────────────────────────────────────────
|
||||
|
|
@ -2165,6 +2251,9 @@ Item {
|
|||
// ── Component setup ───────────────────────────────────────────────────
|
||||
|
||||
Component.onCompleted: {
|
||||
lastWindowRequestSerial = ShellSettings.Settings.dynamicTilingWindowRequestSerial;
|
||||
lastLayoutRequestSerial = ShellSettings.Settings.dynamicTilingLayoutRequestSerial;
|
||||
|
||||
// Connect to existing windows
|
||||
const wins = KWinComponents.Workspace.windows;
|
||||
for (let i = 0; i < wins.length; i++) {
|
||||
|
|
@ -2172,10 +2261,12 @@ Item {
|
|||
}
|
||||
rememberFocusedWindow(KWinComponents.Workspace.activeWindow);
|
||||
publishDynamicTilingWindowState();
|
||||
publishDynamicTilingLayoutState();
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
disconnectDragHandlers();
|
||||
clearDynamicTilingWindowState();
|
||||
clearDynamicTilingLayoutState();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ effects_cmake="$repo_root/kwin/effects/CMakeLists.txt"
|
|||
tiling_script="$repo_root/kwin/scripts/shift-tiling/contents/ui/main.qml"
|
||||
decoration_qml="$repo_root/kwin/decorations/org.shift.decoration/contents/ui/main.qml"
|
||||
running_apps_panel="$repo_root/containments/homescreens/folio/qml/RunningAppsPanel.qml"
|
||||
folio_main="$repo_root/containments/homescreens/folio/qml/main.qml"
|
||||
env_config="$repo_root/envmanager/config.h"
|
||||
|
||||
require_line() {
|
||||
|
|
@ -60,6 +61,10 @@ require_line "$effect_qml" "showPreview(\"restore\""
|
|||
require_line "$tiling_script" "readonly property int maxWindowsPerPage: 4"
|
||||
require_line "$tiling_script" "readonly property real stablePrimaryRatio: 0.58"
|
||||
require_line "$tiling_script" "readonly property var layoutModes: [\"master\", \"columns\", \"rows\"]"
|
||||
require_line "$tiling_script" "property int lastWindowRequestSerial: -1"
|
||||
require_line "$tiling_script" "property int lastLayoutRequestSerial: -1"
|
||||
require_line "$tiling_script" "lastWindowRequestSerial = ShellSettings.Settings.dynamicTilingWindowRequestSerial"
|
||||
require_line "$tiling_script" "lastLayoutRequestSerial = ShellSettings.Settings.dynamicTilingLayoutRequestSerial"
|
||||
require_line "$tiling_script" "function desktopKey(desktop)"
|
||||
require_line "$tiling_script" "function desktopForWindow(win)"
|
||||
require_line "$tiling_script" "function normalizeWindowDesktopScope(win)"
|
||||
|
|
@ -126,6 +131,16 @@ require_line "$decoration_qml" "borders.bottom = normalCornerRadius;"
|
|||
require_line "$decoration_qml" "PathArc { x: root.width - root.cornerRadius; y: root.height; radiusX: root.cornerRadius; radiusY: root.cornerRadius }"
|
||||
require_line "$decoration_qml" "PathArc { x: 0; y: root.height - root.cornerRadius; radiusX: root.cornerRadius; radiusY: root.cornerRadius }"
|
||||
|
||||
require_line "$folio_main" "readonly property int layoutMenuWindowCount: Math.max(0, ShellSettings.Settings.dynamicTilingLayoutWindowCount)"
|
||||
require_line "$folio_main" "&& layoutMenuWindowCount >= 2"
|
||||
require_line "$folio_main" "if (layoutMenuEnabled) {"
|
||||
require_line "$folio_main" "regions.push(rightEdgeRegion)"
|
||||
require_line "$folio_main" "if (layoutMenuEnabled && layoutMenuOpen)"
|
||||
require_line "$folio_main" "rightEdgeHovered = false"
|
||||
require_line "$folio_main" "layoutMenuHovered = false"
|
||||
require_line "$folio_main" "const insideEdge = layoutMenuEnabled"
|
||||
require_line "$folio_main" "width: convergenceChrome.layoutMenuEnabled ? convergenceChrome.rightEdgeHotzoneWidth : 0"
|
||||
|
||||
running_panel_group_disabled_count="$(grep -F "groupMode: TaskManager.TasksModel.GroupDisabled" "$running_apps_panel" | wc -l)"
|
||||
if [[ "$running_panel_group_disabled_count" -ne 2 ]]; then
|
||||
echo "Expected the Folio Running panel to disable grouping for both task models; found $running_panel_group_disabled_count" >&2
|
||||
|
|
@ -155,6 +170,8 @@ reject_line "$tiling_script" "function tileInsertDirection(cursor, rect)"
|
|||
reject_line "$tiling_script" "showDragOutline(\"insert\""
|
||||
reject_line "$tiling_script" "KWinComponents.Workspace.showOutline(dragOutlineRect)"
|
||||
reject_line "$tiling_script" "KWinComponents.Workspace.hideOutline()"
|
||||
reject_line "$tiling_script" "property int lastWindowRequestSerial: ShellSettings.Settings.dynamicTilingWindowRequestSerial"
|
||||
reject_line "$tiling_script" "property int lastLayoutRequestSerial: ShellSettings.Settings.dynamicTilingLayoutRequestSerial"
|
||||
reject_line "$effect_qml" "effect.visible = true"
|
||||
|
||||
printf '%s\n' 'dynamic-tiles-motion-ok'
|
||||
Loading…
Reference in a new issue