mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-04 04:42:05 -04:00
Initial framework
This commit is contained in:
@@ -37,6 +37,8 @@ Item {
|
||||
}
|
||||
|
||||
property real rt: {
|
||||
if (SettingsData.frameEnabled)
|
||||
return Math.max(0, SettingsData.frameRounding - SettingsData.frameThickness);
|
||||
if (barConfig?.squareCorners ?? false)
|
||||
return 0;
|
||||
if (barWindow.hasMaximizedToplevel)
|
||||
@@ -255,11 +257,12 @@ Item {
|
||||
h = h - wing;
|
||||
const r = wing;
|
||||
const cr = rt;
|
||||
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||
|
||||
let d = `M ${cr} 0`;
|
||||
d += ` L ${w - cr} 0`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 ${w} ${cr}`;
|
||||
let d = `M ${crE} 0`;
|
||||
d += ` L ${w - crE} 0`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 1 ${w} ${crE}`;
|
||||
if (r > 0) {
|
||||
d += ` L ${w} ${h + r}`;
|
||||
d += ` A ${r} ${r} 0 0 0 ${w - r} ${h}`;
|
||||
@@ -273,9 +276,9 @@ Item {
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 0 ${h - cr}`;
|
||||
}
|
||||
d += ` L 0 ${cr}`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
||||
d += ` L 0 ${crE}`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 1 ${crE} 0`;
|
||||
d += " Z";
|
||||
return d;
|
||||
}
|
||||
@@ -285,11 +288,12 @@ Item {
|
||||
h = h - wing;
|
||||
const r = wing;
|
||||
const cr = rt;
|
||||
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||
|
||||
let d = `M ${cr} ${fullH}`;
|
||||
d += ` L ${w - cr} ${fullH}`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 ${w} ${fullH - cr}`;
|
||||
let d = `M ${crE} ${fullH}`;
|
||||
d += ` L ${w - crE} ${fullH}`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 0 ${w} ${fullH - crE}`;
|
||||
if (r > 0) {
|
||||
d += ` L ${w} 0`;
|
||||
d += ` A ${r} ${r} 0 0 1 ${w - r} ${r}`;
|
||||
@@ -303,9 +307,9 @@ Item {
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
||||
}
|
||||
d += ` L 0 ${fullH - cr}`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${fullH}`;
|
||||
d += ` L 0 ${fullH - crE}`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${fullH}`;
|
||||
d += " Z";
|
||||
return d;
|
||||
}
|
||||
@@ -314,11 +318,12 @@ Item {
|
||||
w = w - wing;
|
||||
const r = wing;
|
||||
const cr = rt;
|
||||
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||
|
||||
let d = `M 0 ${cr}`;
|
||||
d += ` L 0 ${h - cr}`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 ${cr} ${h}`;
|
||||
let d = `M 0 ${crE}`;
|
||||
d += ` L 0 ${h - crE}`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 0 ${crE} ${h}`;
|
||||
if (r > 0) {
|
||||
d += ` L ${w + r} ${h}`;
|
||||
d += ` A ${r} ${r} 0 0 1 ${w} ${h - r}`;
|
||||
@@ -332,9 +337,9 @@ Item {
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 ${w - cr} 0`;
|
||||
}
|
||||
d += ` L ${cr} 0`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 0 0 ${cr}`;
|
||||
d += ` L ${crE} 0`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 0 0 ${crE}`;
|
||||
d += " Z";
|
||||
return d;
|
||||
}
|
||||
@@ -344,11 +349,12 @@ Item {
|
||||
w = w - wing;
|
||||
const r = wing;
|
||||
const cr = rt;
|
||||
const crE = SettingsData.frameEnabled ? 0 : cr;
|
||||
|
||||
let d = `M ${fullW} ${cr}`;
|
||||
d += ` L ${fullW} ${h - cr}`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW - cr} ${h}`;
|
||||
let d = `M ${fullW} ${crE}`;
|
||||
d += ` L ${fullW} ${h - crE}`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 1 ${fullW - crE} ${h}`;
|
||||
if (r > 0) {
|
||||
d += ` L 0 ${h}`;
|
||||
d += ` A ${r} ${r} 0 0 0 ${r} ${h - r}`;
|
||||
@@ -362,9 +368,9 @@ Item {
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 ${cr} 0`;
|
||||
}
|
||||
d += ` L ${fullW - cr} 0`;
|
||||
if (cr > 0)
|
||||
d += ` A ${cr} ${cr} 0 0 1 ${fullW} ${cr}`;
|
||||
d += ` L ${fullW - crE} 0`;
|
||||
if (crE > 0)
|
||||
d += ` A ${crE} ${crE} 0 0 1 ${fullW} ${crE}`;
|
||||
d += " Z";
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -238,7 +238,9 @@ PanelWindow {
|
||||
readonly property color _surfaceContainer: Theme.surfaceContainer
|
||||
readonly property string _barId: barConfig?.id ?? "default"
|
||||
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() {
|
||||
const live = SettingsData.barConfigs.find(c => c.id === _barId);
|
||||
@@ -384,7 +386,7 @@ PanelWindow {
|
||||
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 {
|
||||
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