mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-03 20:32:07 -04:00
Initial framework
This commit is contained in:
@@ -14,7 +14,7 @@ import "settings/SettingsStore.js" as Store
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property int settingsConfigVersion: 5
|
readonly property int settingsConfigVersion: 7
|
||||||
|
|
||||||
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
readonly property bool isGreeterMode: Quickshell.env("DMS_RUN_GREETER") === "1" || Quickshell.env("DMS_RUN_GREETER") === "true"
|
||||||
|
|
||||||
@@ -186,6 +186,7 @@ Singleton {
|
|||||||
onPopoutElevationEnabledChanged: saveSettings()
|
onPopoutElevationEnabledChanged: saveSettings()
|
||||||
property bool barElevationEnabled: true
|
property bool barElevationEnabled: true
|
||||||
onBarElevationEnabledChanged: saveSettings()
|
onBarElevationEnabledChanged: saveSettings()
|
||||||
|
|
||||||
property bool blurEnabled: false
|
property bool blurEnabled: false
|
||||||
onBlurEnabledChanged: saveSettings()
|
onBlurEnabledChanged: saveSettings()
|
||||||
property string blurBorderColor: "outline"
|
property string blurBorderColor: "outline"
|
||||||
@@ -198,6 +199,21 @@ Singleton {
|
|||||||
property bool blurredWallpaperLayer: false
|
property bool blurredWallpaperLayer: false
|
||||||
property bool blurWallpaperOnOverview: false
|
property bool blurWallpaperOnOverview: false
|
||||||
|
|
||||||
|
property bool frameEnabled: false
|
||||||
|
onFrameEnabledChanged: saveSettings()
|
||||||
|
property real frameThickness: 15
|
||||||
|
onFrameThicknessChanged: saveSettings()
|
||||||
|
property real frameRounding: 24
|
||||||
|
onFrameRoundingChanged: saveSettings()
|
||||||
|
property string frameColor: "#2a2a2a"
|
||||||
|
onFrameColorChanged: saveSettings()
|
||||||
|
property real frameOpacity: 1.0
|
||||||
|
onFrameOpacityChanged: saveSettings()
|
||||||
|
property bool frameSyncBarColor: true
|
||||||
|
onFrameSyncBarColorChanged: saveSettings()
|
||||||
|
property var frameScreenPreferences: ["all"]
|
||||||
|
onFrameScreenPreferencesChanged: saveSettings()
|
||||||
|
|
||||||
property bool showLauncherButton: true
|
property bool showLauncherButton: true
|
||||||
property bool showWorkspaceSwitcher: true
|
property bool showWorkspaceSwitcher: true
|
||||||
property bool showFocusedWindow: true
|
property bool showFocusedWindow: true
|
||||||
@@ -1938,6 +1954,47 @@ Singleton {
|
|||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFrameFilteredScreens() {
|
||||||
|
var prefs = frameScreenPreferences || ["all"];
|
||||||
|
if (!prefs || prefs.length === 0 || prefs.includes("all")) {
|
||||||
|
return Quickshell.screens;
|
||||||
|
}
|
||||||
|
return Quickshell.screens.filter(screen => isScreenInPreferences(screen, prefs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveBarEdgeForScreen(screen) {
|
||||||
|
if (!screen) return "";
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled) continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs)) continue;
|
||||||
|
switch (bc.position ?? 0) {
|
||||||
|
case SettingsData.Position.Top: return "top";
|
||||||
|
case SettingsData.Position.Bottom: return "bottom";
|
||||||
|
case SettingsData.Position.Left: return "left";
|
||||||
|
case SettingsData.Position.Right: return "right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveBarThicknessForScreen(screen) {
|
||||||
|
if (!screen) return frameThickness;
|
||||||
|
for (var i = 0; i < barConfigs.length; i++) {
|
||||||
|
var bc = barConfigs[i];
|
||||||
|
if (!bc.enabled) continue;
|
||||||
|
var prefs = bc.screenPreferences || ["all"];
|
||||||
|
if (!prefs.includes("all") && !isScreenInPreferences(screen, prefs)) continue;
|
||||||
|
const innerPadding = bc.innerPadding ?? 4;
|
||||||
|
const barT = Math.max(26 + innerPadding * 0.6, Theme.barHeight - 4 - (8 - innerPadding));
|
||||||
|
const spacing = bc.spacing ?? 4;
|
||||||
|
const bottomGap = bc.bottomGap ?? 0;
|
||||||
|
return barT + spacing + bottomGap;
|
||||||
|
}
|
||||||
|
return frameThickness;
|
||||||
|
}
|
||||||
|
|
||||||
function sendTestNotifications() {
|
function sendTestNotifications() {
|
||||||
NotificationService.dismissAllPopups();
|
NotificationService.dismissAllPopups();
|
||||||
sendTestNotification(0);
|
sendTestNotification(0);
|
||||||
|
|||||||
@@ -547,7 +547,15 @@ var SPEC = {
|
|||||||
clipboardEnterToPaste: { def: false },
|
clipboardEnterToPaste: { def: false },
|
||||||
|
|
||||||
launcherPluginVisibility: { def: {} },
|
launcherPluginVisibility: { def: {} },
|
||||||
launcherPluginOrder: { def: [] }
|
launcherPluginOrder: { def: [] },
|
||||||
|
|
||||||
|
frameEnabled: { def: false },
|
||||||
|
frameThickness: { def: 15 },
|
||||||
|
frameRounding: { def: 24 },
|
||||||
|
frameColor: { def: "#2a2a2a" },
|
||||||
|
frameOpacity: { def: 1.0 },
|
||||||
|
frameSyncBarColor: { def: true },
|
||||||
|
frameScreenPreferences: { def: ["all"] }
|
||||||
};
|
};
|
||||||
|
|
||||||
function getValidKeys() {
|
function getValidKeys() {
|
||||||
|
|||||||
@@ -248,6 +248,20 @@ function migrateToVersion(obj, targetVersion) {
|
|||||||
settings.configVersion = 6;
|
settings.configVersion = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentVersion < 7) {
|
||||||
|
console.info("Migrating settings from version", currentVersion, "to version 7");
|
||||||
|
|
||||||
|
if (settings.frameEnabled === undefined) settings.frameEnabled = false;
|
||||||
|
if (settings.frameThickness === undefined) settings.frameThickness = 15;
|
||||||
|
if (settings.frameRounding === undefined) settings.frameRounding = 24;
|
||||||
|
if (settings.frameColor === undefined) settings.frameColor = "#2a2a2a";
|
||||||
|
if (settings.frameOpacity === undefined) settings.frameOpacity = 1.0;
|
||||||
|
if (settings.frameSyncBarColor === undefined) settings.frameSyncBarColor = true;
|
||||||
|
if (settings.frameScreenPreferences === undefined) settings.frameScreenPreferences = ["all"];
|
||||||
|
|
||||||
|
settings.configVersion = 7;
|
||||||
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import qs.Modules.OSD
|
|||||||
import qs.Modules.ProcessList
|
import qs.Modules.ProcessList
|
||||||
import qs.Modules.DankBar
|
import qs.Modules.DankBar
|
||||||
import qs.Modules.DankBar.Popouts
|
import qs.Modules.DankBar.Popouts
|
||||||
|
import qs.Modules.Frame
|
||||||
import qs.Modules.WorkspaceOverlays
|
import qs.Modules.WorkspaceOverlays
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
@@ -207,6 +208,8 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Frame {}
|
||||||
|
|
||||||
property bool dockEnabled: false
|
property bool dockEnabled: false
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
|||||||
@@ -518,5 +518,20 @@ FocusScope {
|
|||||||
Qt.callLater(() => item.forceActiveFocus());
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: frameLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 33
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: FrameTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ Rectangle {
|
|||||||
"text": I18n.tr("Widgets"),
|
"text": I18n.tr("Widgets"),
|
||||||
"icon": "widgets",
|
"icon": "widgets",
|
||||||
"tabIndex": 22
|
"tabIndex": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "frame",
|
||||||
|
"text": I18n.tr("Frame"),
|
||||||
|
"icon": "frame_source",
|
||||||
|
"tabIndex": 33
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property real rt: {
|
property real rt: {
|
||||||
|
if (SettingsData.frameEnabled)
|
||||||
|
return Math.max(0, SettingsData.frameRounding - SettingsData.frameThickness);
|
||||||
if (barConfig?.squareCorners ?? false)
|
if (barConfig?.squareCorners ?? false)
|
||||||
return 0;
|
return 0;
|
||||||
if (barWindow.hasMaximizedToplevel)
|
if (barWindow.hasMaximizedToplevel)
|
||||||
@@ -255,11 +257,12 @@ Item {
|
|||||||
h = h - wing;
|
h = h - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${cr} 0`;
|
let d = `M ${crE} 0`;
|
||||||
d += ` L ${w - cr} 0`;
|
d += ` L ${w - crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${w} ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${w} ${crE}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w} ${h + r}`;
|
d += ` L ${w} ${h + r}`;
|
||||||
d += ` A ${r} ${r} 0 0 0 ${w - r} ${h}`;
|
d += ` A ${r} ${r} 0 0 0 ${w - r} ${h}`;
|
||||||
@@ -273,9 +276,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 0 ${h - cr}`;
|
d += ` A ${cr} ${cr} 0 0 1 0 ${h - cr}`;
|
||||||
}
|
}
|
||||||
d += ` L 0 ${cr}`;
|
d += ` L 0 ${crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
d += ` A ${crE} ${crE} 0 0 1 ${crE} 0`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -285,11 +288,12 @@ Item {
|
|||||||
h = h - wing;
|
h = h - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${cr} ${fullH}`;
|
let d = `M ${crE} ${fullH}`;
|
||||||
d += ` L ${w - cr} ${fullH}`;
|
d += ` L ${w - crE} ${fullH}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${w} ${fullH - cr}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${w} ${fullH - crE}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w} 0`;
|
d += ` L ${w} 0`;
|
||||||
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
|
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
|
||||||
@@ -303,9 +307,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
||||||
}
|
}
|
||||||
d += ` L 0 ${fullH - cr}`;
|
d += ` L 0 ${fullH - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${fullH}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${fullH}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -314,11 +318,12 @@ Item {
|
|||||||
w = w - wing;
|
w = w - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M 0 ${cr}`;
|
let d = `M 0 ${crE}`;
|
||||||
d += ` L 0 ${h - cr}`;
|
d += ` L 0 ${h - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${h}`;
|
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${h}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L ${w + r} ${h}`;
|
d += ` L ${w + r} ${h}`;
|
||||||
d += ` A ${r} ${r} 0 0 1 ${w} ${h - r}`;
|
d += ` A ${r} ${r} 0 0 1 ${w} ${h - r}`;
|
||||||
@@ -332,9 +337,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 ${w - cr} 0`;
|
d += ` A ${cr} ${cr} 0 0 0 ${w - cr} 0`;
|
||||||
}
|
}
|
||||||
d += ` L ${cr} 0`;
|
d += ` L ${crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 0 0 ${crE}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
@@ -344,11 +349,12 @@ Item {
|
|||||||
w = w - wing;
|
w = w - wing;
|
||||||
const r = wing;
|
const r = wing;
|
||||||
const cr = rt;
|
const cr = rt;
|
||||||
|
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||||
|
|
||||||
let d = `M ${fullW} ${cr}`;
|
let d = `M ${fullW} ${crE}`;
|
||||||
d += ` L ${fullW} ${h - cr}`;
|
d += ` L ${fullW} ${h - crE}`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW - cr} ${h}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${fullW - crE} ${h}`;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
d += ` L 0 ${h}`;
|
d += ` L 0 ${h}`;
|
||||||
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
|
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
|
||||||
@@ -362,9 +368,9 @@ Item {
|
|||||||
if (cr > 0)
|
if (cr > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
||||||
}
|
}
|
||||||
d += ` L ${fullW - cr} 0`;
|
d += ` L ${fullW - crE} 0`;
|
||||||
if (cr > 0)
|
if (crE > 0)
|
||||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW} ${cr}`;
|
d += ` A ${crE} ${crE} 0 0 1 ${fullW} ${crE}`;
|
||||||
d += " Z";
|
d += " Z";
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,9 @@ PanelWindow {
|
|||||||
readonly property color _surfaceContainer: Theme.surfaceContainer
|
readonly property color _surfaceContainer: Theme.surfaceContainer
|
||||||
readonly property string _barId: barConfig?.id ?? "default"
|
readonly property string _barId: barConfig?.id ?? "default"
|
||||||
property real _backgroundAlpha: barConfig?.transparency ?? 1.0
|
property real _backgroundAlpha: barConfig?.transparency ?? 1.0
|
||||||
readonly property color _bgColor: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
|
readonly property color _bgColor: (SettingsData.frameEnabled && SettingsData.frameSyncBarColor)
|
||||||
|
? SettingsData.frameColor
|
||||||
|
: Theme.withAlpha(_surfaceContainer, _backgroundAlpha)
|
||||||
|
|
||||||
function _updateBackgroundAlpha() {
|
function _updateBackgroundAlpha() {
|
||||||
const live = SettingsData.barConfigs.find(c => c.id === _barId);
|
const live = SettingsData.barConfigs.find(c => c.id === _barId);
|
||||||
@@ -384,7 +386,7 @@ PanelWindow {
|
|||||||
shouldHideForWindows = filtered.length > 0;
|
shouldHideForWindows = filtered.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
property real effectiveSpacing: hasMaximizedToplevel ? 0 : (barConfig?.spacing ?? 4)
|
property real effectiveSpacing: SettingsData.frameEnabled ? 0 : (hasMaximizedToplevel ? 0 : (barConfig?.spacing ?? 4))
|
||||||
|
|
||||||
Behavior on effectiveSpacing {
|
Behavior on effectiveSpacing {
|
||||||
enabled: barWindow.visible
|
enabled: barWindow.visible
|
||||||
|
|||||||
17
quickshell/Modules/Frame/Frame.qml
Normal file
17
quickshell/Modules/Frame/Frame.qml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
model: SettingsData.frameEnabled ? SettingsData.getFrameFilteredScreens() : []
|
||||||
|
|
||||||
|
FrameInstance {
|
||||||
|
required property ShellScreen modelData
|
||||||
|
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
53
quickshell/Modules/Frame/FrameBorder.qml
Normal file
53
quickshell/Modules/Frame/FrameBorder.qml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string barEdge // "top" | "bottom" | "left" | "right" | ""
|
||||||
|
required property real barThickness
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
readonly property real _thickness: SettingsData.frameThickness
|
||||||
|
readonly property real _rounding: SettingsData.frameRounding
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: borderRect
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
color: SettingsData.frameColor
|
||||||
|
opacity: SettingsData.frameOpacity
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
maskSource: cutoutMask
|
||||||
|
maskEnabled: true
|
||||||
|
maskInverted: true
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: cutoutMask
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
layer.enabled: true
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: root.barEdge === "top" ? root.barThickness : root._thickness
|
||||||
|
bottomMargin: root.barEdge === "bottom" ? root.barThickness : root._thickness
|
||||||
|
leftMargin: root.barEdge === "left" ? root.barThickness : root._thickness
|
||||||
|
rightMargin: root.barEdge === "right" ? root.barThickness : root._thickness
|
||||||
|
}
|
||||||
|
radius: root._rounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
quickshell/Modules/Frame/FrameExclusions.qml
Normal file
80
quickshell/Modules/Frame/FrameExclusions.qml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property ShellScreen screen
|
||||||
|
|
||||||
|
readonly property string barEdge: SettingsData.getActiveBarEdgeForScreen(screen)
|
||||||
|
|
||||||
|
// One thin invisible PanelWindow per edge.
|
||||||
|
// Skips the edge where the bar already provides its own exclusiveZone.
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.barEdge !== "top"
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
screen: root.screen
|
||||||
|
anchorTop: true
|
||||||
|
anchorLeft: true
|
||||||
|
anchorRight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.barEdge !== "bottom"
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
screen: root.screen
|
||||||
|
anchorBottom: true
|
||||||
|
anchorLeft: true
|
||||||
|
anchorRight: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.barEdge !== "left"
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
screen: root.screen
|
||||||
|
anchorLeft: true
|
||||||
|
anchorTop: true
|
||||||
|
anchorBottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.barEdge !== "right"
|
||||||
|
sourceComponent: EdgeExclusion {
|
||||||
|
screen: root.screen
|
||||||
|
anchorRight: true
|
||||||
|
anchorTop: true
|
||||||
|
anchorBottom: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component EdgeExclusion: PanelWindow {
|
||||||
|
required property ShellScreen screen
|
||||||
|
property bool anchorTop: false
|
||||||
|
property bool anchorBottom: false
|
||||||
|
property bool anchorLeft: false
|
||||||
|
property bool anchorRight: false
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:frame-exclusion"
|
||||||
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
exclusiveZone: SettingsData.frameThickness
|
||||||
|
color: "transparent"
|
||||||
|
mask: Region {}
|
||||||
|
implicitWidth: 1
|
||||||
|
implicitHeight: 1
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: anchorTop
|
||||||
|
bottom: anchorBottom
|
||||||
|
left: anchorLeft
|
||||||
|
right: anchorRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
quickshell/Modules/Frame/FrameInstance.qml
Normal file
18
quickshell/Modules/Frame/FrameInstance.qml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property ShellScreen screen
|
||||||
|
|
||||||
|
FrameWindow {
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameExclusions {
|
||||||
|
screen: root.screen
|
||||||
|
}
|
||||||
|
}
|
||||||
34
quickshell/Modules/Frame/FrameWindow.qml
Normal file
34
quickshell/Modules/Frame/FrameWindow.qml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: win
|
||||||
|
|
||||||
|
required property ShellScreen screen
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:frame"
|
||||||
|
WlrLayershell.layer: WlrLayer.Bottom
|
||||||
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
// No input — pass everything through to apps and bar
|
||||||
|
mask: Region {}
|
||||||
|
|
||||||
|
FrameBorder {
|
||||||
|
anchors.fill: parent
|
||||||
|
barEdge: SettingsData.getActiveBarEdgeForScreen(win.screen)
|
||||||
|
barThickness: SettingsData.getActiveBarThicknessForScreen(win.screen)
|
||||||
|
}
|
||||||
|
}
|
||||||
195
quickshell/Modules/Settings/FrameTab.qml
Normal file
195
quickshell/Modules/Settings/FrameTab.qml
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(550, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
|
// ── Enable Frame ──────────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "frame_source"
|
||||||
|
title: I18n.tr("Frame")
|
||||||
|
settingKey: "frameEnabled"
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "frameEnable"
|
||||||
|
tags: ["frame", "border", "outline", "display"]
|
||||||
|
text: I18n.tr("Enable Frame")
|
||||||
|
description: I18n.tr("Draw a connected picture-frame border around the entire display")
|
||||||
|
checked: SettingsData.frameEnabled
|
||||||
|
onToggled: checked => SettingsData.set("frameEnabled", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Border ────────────────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "border_outer"
|
||||||
|
title: I18n.tr("Border")
|
||||||
|
settingKey: "frameBorder"
|
||||||
|
collapsible: true
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: roundingSlider
|
||||||
|
settingKey: "frameRounding"
|
||||||
|
tags: ["frame", "border", "rounding", "radius", "corner"]
|
||||||
|
text: I18n.tr("Border rounding")
|
||||||
|
unit: "px"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
step: 1
|
||||||
|
defaultValue: 24
|
||||||
|
value: SettingsData.frameRounding
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameRounding", v)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: roundingSlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameRounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: thicknessSlider
|
||||||
|
settingKey: "frameThickness"
|
||||||
|
tags: ["frame", "border", "thickness", "size", "width"]
|
||||||
|
text: I18n.tr("Border thickness")
|
||||||
|
unit: "px"
|
||||||
|
minimum: 2
|
||||||
|
maximum: 100
|
||||||
|
step: 1
|
||||||
|
defaultValue: 15
|
||||||
|
value: SettingsData.frameThickness
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameThickness", v)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: thicknessSlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameThickness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsSliderRow {
|
||||||
|
id: opacitySlider
|
||||||
|
settingKey: "frameOpacity"
|
||||||
|
tags: ["frame", "border", "opacity", "transparency"]
|
||||||
|
text: I18n.tr("Border opacity")
|
||||||
|
unit: "%"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
defaultValue: 100
|
||||||
|
value: SettingsData.frameOpacity * 100
|
||||||
|
onSliderDragFinished: v => SettingsData.set("frameOpacity", v / 100)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: opacitySlider
|
||||||
|
property: "value"
|
||||||
|
value: SettingsData.frameOpacity * 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color row
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: colorRow.height + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: colorRow
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
x: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Border color")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: colorSwatch
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
radius: 16
|
||||||
|
color: SettingsData.frameColor
|
||||||
|
border.color: Theme.outline
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.colorPickerModal.selectedColor = SettingsData.frameColor;
|
||||||
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Frame Border Color");
|
||||||
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (color) {
|
||||||
|
SettingsData.set("frameColor", color.toString());
|
||||||
|
};
|
||||||
|
PopoutService.colorPickerModal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bar Integration ───────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "toolbar"
|
||||||
|
title: I18n.tr("Bar Integration")
|
||||||
|
settingKey: "frameBarIntegration"
|
||||||
|
collapsible: true
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsToggleRow {
|
||||||
|
settingKey: "frameSyncBarColor"
|
||||||
|
tags: ["frame", "bar", "sync", "color", "background"]
|
||||||
|
text: I18n.tr("Sync bar background to frame")
|
||||||
|
description: I18n.tr("Sets the bar background color to match the frame border color for a seamless look")
|
||||||
|
checked: SettingsData.frameSyncBarColor
|
||||||
|
onToggled: checked => SettingsData.set("frameSyncBarColor", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Display Assignment ────────────────────────────────────────────
|
||||||
|
SettingsCard {
|
||||||
|
width: parent.width
|
||||||
|
iconName: "monitor"
|
||||||
|
title: I18n.tr("Display Assignment")
|
||||||
|
settingKey: "frameDisplays"
|
||||||
|
collapsible: true
|
||||||
|
visible: SettingsData.frameEnabled
|
||||||
|
|
||||||
|
SettingsDisplayPicker {
|
||||||
|
displayPreferences: SettingsData.frameScreenPreferences
|
||||||
|
onPreferencesChanged: prefs => SettingsData.set("frameScreenPreferences", prefs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user