greeting: redesign welcome page & changelog page

+ enable changelog
This commit is contained in:
bbedward
2026-10-19 12:05:38 -04:00
parent 317d9399c7
commit fcb74f5884
26 changed files with 998 additions and 710 deletions
+1 -1
View File
@@ -877,7 +877,7 @@ func checkWindowManagers() []checkResult {
results = append(results, checkAqueousConfigHelper())
}
if utils.CommandExists("mango") {
if os.Getenv("MANGO_INSTANCE_SIGNATURE") != "" {
results = append(results, checkMangoConfig(mangoconf.Dir(), mangoconf.Detect())...)
}
+6
View File
@@ -293,6 +293,12 @@ func (r *Registry) resetLegacyCache() {
// declaration order (first occurrence of an ID wins). A failing registry is
// reported in the joined error but does not block the others.
func (r *Registry) Update() error {
unlock, err := registries.LockCache()
if err != nil {
return err
}
defer unlock()
r.resetLegacyCache()
r.plugins = []Plugin{}
seen := make(map[string]struct{})
+31
View File
@@ -0,0 +1,31 @@
package registries
import (
"fmt"
"os"
"path/filepath"
"syscall"
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
)
// flock is per open file, so concurrent goroutines contend like separate processes.
func LockCache() (unlock func(), err error) {
dir := filepath.Join(utils.RuntimeDir(), "dms")
if err := os.MkdirAll(dir, 0o700); err != nil {
return nil, fmt.Errorf("create registry lock dir: %w", err)
}
f, err := os.OpenFile(filepath.Join(dir, "registry.lock"), os.O_CREATE|os.O_RDWR, 0o600)
if err != nil {
return nil, fmt.Errorf("open registry lock: %w", err)
}
if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
f.Close()
return nil, fmt.Errorf("acquire registry lock: %w", err)
}
return func() {
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
f.Close()
}, nil
}
+6
View File
@@ -318,6 +318,12 @@ func (r *Registry) resetLegacyCache() {
// declaration order (first occurrence of an ID wins). A failing registry is
// reported in the joined error but does not block the others.
func (r *Registry) Update() error {
unlock, err := registries.LockCache()
if err != nil {
return err
}
defer unlock()
r.resetLegacyCache()
r.themes = []Theme{}
seen := make(map[string]struct{})
Generated
+3 -3
View File
@@ -3,11 +3,11 @@
"dank-qml-common": {
"flake": false,
"locked": {
"lastModified": 1792374447,
"narHash": "sha256-FlzldJJuGPxyb6IHnzYLi8HQE1Yg222UnY0KcKnKGik=",
"lastModified": 1792424555,
"narHash": "sha256-V1RZ9krNkeXzvyAC8OoGthhq5zsxWKUWq7Qwnpl7awo=",
"owner": "AvengeMedia",
"repo": "dank-qml-common",
"rev": "51978d3ba6be3138ee39bb0c7f07e156a82805b6",
"rev": "0166a116f7c9739008b202eee3dfef5bf76d4f0f",
"type": "github"
},
"original": {
+5
View File
@@ -1379,6 +1379,11 @@ Item {
Component.onCompleted: show()
}
Component.onCompleted: {
if (ChangelogService.shouldShowChangelog)
active = true;
}
Connections {
target: ChangelogService
function onChangelogRequested() {
+281 -261
View File
@@ -1,5 +1,6 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.DCommon.Widgets
@@ -9,296 +10,315 @@ import qs.Modules.Settings.Widgets
Column {
id: root
readonly property real logoSize: Math.round(Theme.iconSize * 2.8)
readonly property real badgeHeight: Math.round(Theme.fontSizeSmall * 1.7)
readonly property string releaseNotesUrl: Site.web + "/blog/v1-6-release"
readonly property string screenshotDocsUrl: Site.docs + "/dankmaterialshell/cli-screenshot"
readonly property string pluginDocsUrl: Site.docs + "/dms/plugin-development"
readonly property string compositorDocsUrl: Site.docs + "/dms/compositors"
topPadding: Theme.spacingL
readonly property var features: [
{
"icon": "palette",
"shape": "softBurst",
"hoverShape": "flower",
"accent": "purple",
"heading": "A new design",
"description": "Material 3 Expressive across Settings, Control Center, Dash, and every control",
"target": "theme_surfaces"
},
{
"icon": "space_dashboard",
"shape": "cookie9",
"hoverShape": "sunny",
"accent": "blue",
"heading": "Dash plugins",
"description": "Plugins add full tabs or overview cards, sized and ordered your way",
"target": "dank_dash"
},
{
"icon": "lock",
"shape": "clover4",
"hoverShape": "cookie4",
"accent": "green",
"heading": "Build your lock screen",
"description": "Place the clock, media, status, and any desktop widget where you want",
"target": "lock_screen"
},
{
"icon": "lyrics",
"shape": "sunny",
"hoverShape": "softBurst",
"accent": "pink",
"heading": "Lyrical Engine",
"description": "Word synced lyrics from six providers, right in the media player",
"target": "media_player"
},
{
"icon": "wallpaper",
"shape": "gem",
"hoverShape": "cookie6",
"accent": "orange",
"heading": "Material wallpapers",
"description": "Presets from your seed color, plus layouts from wallpaper.danklinux.com",
"target": "personalization"
},
{
"icon": "digital_wellbeing",
"shape": "puffy",
"hoverShape": "clover8",
"accent": "teal",
"heading": "Digital wellbeing",
"description": "Screen time, daily and app limits, with its own Dash tab",
"target": "wellbeing"
}
]
readonly property var additions: [
{
"icon": "view_in_ar",
"accent": "purple",
"title": "Island",
"subtitle": "Notch mode, workspace switcher, tray slot, clipboard, and shadows",
"target": "dankbar_settings"
},
{
"icon": "grid_view",
"accent": "green",
"title": "Launcher",
"subtitle": "Custom keybindings, dmenu mode, Open with, and app editing",
"target": "launcher"
},
{
"icon": "dock_to_bottom",
"accent": "orange",
"title": "Dock",
"subtitle": "Proximity magnification and a focused app pill",
"target": "dock_appearance"
},
{
"icon": "notifications",
"accent": "pink",
"title": "Notifications",
"subtitle": "Rule conditions, timed app mutes, and two finger swipe to dismiss",
"target": "notification_rules"
},
{
"icon": "extension",
"accent": "teal",
"title": "Plugin browser",
"subtitle": "Discover shelves, quick filters, upvotes, and typo tolerant search",
"target": "plugins"
},
{
"icon": "format_paint",
"accent": "yellow",
"title": "Themes",
"subtitle": "Bold palettes, injected palettes, fixed radius, and terminal opacity",
"target": "theme"
},
{
"icon": "monitor",
"accent": "red",
"title": "Displays",
"subtitle": "Per-output ICC profiles and color temperature",
"target": "display_config"
},
{
"icon": "system_update_alt",
"accent": "blue",
"title": "Software updates",
"subtitle": "Update dms from Settings and switch between stable and beta",
"target": "updater"
},
{
"icon": "devices",
"accent": "green",
"title": "More platforms",
"subtitle": "FreeBSD, Enterprise Linux 10, Mango, Hyprland 0.56, Umbriel, and Aqueous",
"target": root.compositorDocsUrl
}
]
readonly property var featurePairs: {
const pairs = [];
for (let i = 0; i < features.length; i += 2)
pairs.push(features.slice(i, i + 2));
return pairs;
}
function open(target) {
if (target.startsWith("https://")) {
Qt.openUrlExternally(target);
return;
}
PopoutService.openSettingsWithTab(target);
}
topPadding: Theme.spacingS
spacing: Theme.spacingL
ChangelogHero {
width: parent.width
symbol: "He"
atomicNumber: 2
atomicMass: "4.0026"
remainder: "isenberg"
}
Column {
width: parent.width
spacing: Theme.spacingM
spacing: Theme.spacingS
Repeater {
model: root.featurePairs
delegate: Row {
id: pair
required property var modelData
required property int index
readonly property real cardWidth: (width - spacing) / 2
readonly property real rowHeight: Math.max(first.implicitHeight, second.implicitHeight)
width: parent.width
spacing: Theme.spacingS
LayoutMirroring.enabled: I18n.isRtl
FeatureCard {
id: first
width: pair.cardWidth
height: pair.rowHeight
iconName: pair.modelData[0].icon
shape: pair.modelData[0].shape
hoverShape: pair.modelData[0].hoverShape
accent: pair.modelData[0].accent
heading: pair.modelData[0].heading
description: pair.modelData[0].description
onClicked: root.open(pair.modelData[0].target)
}
FeatureCard {
id: second
width: pair.cardWidth
height: pair.rowHeight
iconName: pair.modelData[1].icon
shape: pair.modelData[1].shape
hoverShape: pair.modelData[1].hoverShape
accent: pair.modelData[1].accent
heading: pair.modelData[1].heading
description: pair.modelData[1].description
onClicked: root.open(pair.modelData[1].target)
}
}
}
}
SettingsCard {
title: "More in " + ChangelogService.currentVersion
Repeater {
model: root.additions
delegate: SettingsRow {
id: addition
required property var modelData
readonly property var accentPair: Theme.accent(modelData.accent)
title: modelData.title
subtitle: modelData.subtitle
resetKeys: []
clickable: modelData.target !== ""
showChevron: clickable
onClicked: root.open(modelData.target)
leading: Rectangle {
width: SettingsMetrics.navIconSize
height: width
radius: Theme.fullRadius(width, height)
color: addition.accentPair?.container ?? Theme.primaryContainer
DIcon {
anchors.centerIn: parent
name: addition.modelData.icon
size: Theme.iconSizeMedium
color: addition.accentPair?.onContainer ?? Theme.onPrimaryContainer
}
}
}
}
}
DCard {
width: parent.width
height: pluginNote.implicitHeight + pad * 2
tone: "tertiary"
pad: Theme.spacingL
restRadius: Theme.groupedListOuterRadius
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingM
id: pluginNote
width: parent.width
spacing: Theme.spacingL
LayoutMirroring.enabled: I18n.isRtl
Image {
width: root.logoSize
height: width * (569.94629 / 506.50931)
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: true
asynchronous: true
source: "file://" + Theme.shellDir + "/assets/danklogonormal.svg"
layer.enabled: true
layer.smooth: true
layer.mipmap: true
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: Theme.primary
DMaterialShape {
width: Theme.buttonHeightM
height: width
shape: "cookie6"
color: Theme.onTertiaryContainer
DIcon {
anchors.centerIn: parent
name: "code"
size: Theme.iconSize
color: Theme.tertiaryContainer
}
}
Column {
width: parent.width - Theme.buttonHeightM - docsButton.width - parent.spacing * 2
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
spacing: Theme.spacingXXS
Row {
spacing: Theme.spacingS
StyledText {
text: "DMS " + ChangelogService.currentVersion
font.pixelSize: Theme.fontSizeXLarge + 2
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
width: codenameText.implicitWidth + Theme.spacingM * 2
height: root.badgeHeight
radius: Theme.fullRadius(width, height)
color: Theme.primaryContainer
anchors.verticalCenter: parent.verticalCenter
StyledText {
id: codenameText
anchors.centerIn: parent
text: "Marble Tabby"
font.pixelSize: Theme.fontSizeSmall
font.weight: Theme.fontWeightMedium
color: Theme.accentOnPrimaryContainer
}
}
StyledText {
width: parent.width
text: "Plugin authors"
font.pixelSize: Theme.fontSizeLarge
font.weight: Theme.fontWeightBold
color: Theme.onTertiaryContainer
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: "Dank Island, spring motion, performance & resource optimizations"
width: parent.width
text: "The Dank* widget wrappers are deprecated. Import qs.DCommon.Widgets and use the D names, DankButton becomes DButton. Each old type logs its replacement."
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outlineMedium
opacity: 0.3
}
Column {
width: parent.width
spacing: Theme.spacingM
StyledText {
text: "What's New"
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
Grid {
width: parent.width
columns: 2
rowSpacing: Theme.spacingS
columnSpacing: Theme.spacingS
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "view_in_ar"
title: "Dank Island"
description: "A bar that reacts to you"
onClicked: {
SettingsSearchService.navigateToSection("barLayout");
PopoutService.openSettingsWithTab("dankbar");
color: Theme.onTertiaryContainer
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "animation"
title: "Spring Motion"
description: "New motion physics"
onClicked: {
SettingsSearchService.navigateToSection("springBounce");
PopoutService.openSettingsWithTab("typography");
}
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "speed"
title: "Performance"
description: "Big speedups, less memory"
onClicked: Qt.openUrlExternally(root.releaseNotesUrl)
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "wifi_tethering"
title: "Network"
description: "Hotspot, cellular, and OWE"
onClicked: {
SettingsSearchService.navigateToSection("networkHotspot");
PopoutService.openSettingsWithTab("network_wifi");
}
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "extension"
title: "Plugin Registries"
description: "Add your own sources"
onClicked: PopoutService.openSettingsWithTab("plugins")
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "photo_camera"
title: "Capture"
description: "Grab regions across outputs"
onClicked: Qt.openUrlExternally(root.screenshotDocsUrl)
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "login"
title: "Standalone Greeter"
description: "Now its own app"
onClicked: {
SettingsSearchService.navigateToSection("greeterStatus");
PopoutService.openSettingsWithTab("greeter");
}
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "dock_to_bottom"
title: "Dock"
description: "Pinned apps, running apps"
onClicked: {
SettingsSearchService.navigateToSection("dockSeparatePinnedAndRunningApps");
PopoutService.openSettingsWithTab("dock_general");
}
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "content_paste"
title: "Clipboard"
description: "Drop in anything to store it"
onClicked: PopoutService.openSettingsWithTab("clipboard")
}
ChangelogFeatureCard {
width: (parent.width - Theme.spacingS) / 2
iconName: "language"
title: "Translations"
description: "22 languages, plugin translations"
onClicked: {
SettingsSearchService.navigateToSection("locale");
PopoutService.openSettingsWithTab("locale");
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outlineMedium
opacity: 0.3
}
Column {
width: parent.width
spacing: Theme.spacingS
Row {
spacing: Theme.spacingS
DIcon {
name: "warning"
size: Theme.iconSizeSmall
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: "Upgrade Notes"
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
DButton {
id: docsButton
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Docs")
iconName: "open_in_new"
backgroundColor: Theme.onTertiaryContainer
textColor: Theme.tertiaryContainer
onClicked: Qt.openUrlExternally(root.pluginDocsUrl)
}
}
Rectangle {
width: parent.width
height: upgradeNotesColumn.height + Theme.spacingM * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.warning, 0.08)
border.width: 1
border.color: Theme.withAlpha(Theme.warning, 0.2)
Column {
id: upgradeNotesColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
ChangelogUpgradeNote {
width: parent.width
text: "The shell is embedded in the dms binary. ~/.config/quickshell/dms is no longer auto-discovered, pass -c or set DMS_SHELL_DIR to override it"
}
ChangelogUpgradeNote {
width: parent.width
text: "The greeter is now a standalone application. dms greeter commands become dms-greeter"
}
ChangelogUpgradeNote {
width: parent.width
text: "settings.json only stores what differs from the defaults, and machine-specific state moved to session.json"
}
}
}
}
SettingsDivider {
visible: ChangelogService.release !== null
}
Column {
width: parent.width
visible: ChangelogService.release !== null
spacing: Theme.spacingS
spacing: 0
Row {
spacing: Theme.spacingS
DIcon {
name: "auto_awesome"
size: Theme.iconSizeSmall
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("What changed", "changelog section heading listing the changes in a release")
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
SettingsSectionLabel {
text: I18n.tr("What changed", "changelog section heading listing the changes in a release")
}
ReleaseNotesCard {
@@ -1,80 +0,0 @@
import QtQuick
import qs.Common
import qs.DCommon.Widgets
Rectangle {
id: root
property string iconName: ""
property string title: ""
property string description: ""
signal clicked
readonly property real iconContainerSize: Math.round(Theme.iconSize * 1.3)
height: Math.round(Theme.fontSizeMedium * 4.2)
radius: Theme.cornerRadius
color: Theme.floatingWindowNestedSurface
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
Rectangle {
anchors.fill: parent
radius: parent.radius
color: Theme.primary
opacity: mouseArea.containsMouse ? 0.12 : 0
}
Row {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
Rectangle {
width: root.iconContainerSize
height: root.iconContainerSize
radius: Math.round(root.iconContainerSize * 0.28)
color: Theme.primaryContainer
anchors.verticalCenter: parent.verticalCenter
DIcon {
anchors.centerIn: parent
name: root.iconName
size: Theme.iconSize - 6
color: Theme.accentOnPrimaryContainer
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
width: parent.width - root.iconContainerSize - Theme.spacingS
StyledText {
text: root.title
font.pixelSize: Theme.fontSizeSmall
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
StyledText {
text: root.description
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
width: parent.width
elide: Text.ElideRight
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
}
}
@@ -0,0 +1,96 @@
import QtQuick
import qs.Common
import qs.Services
import qs.DCommon.Widgets
import qs.Widgets
ShapeHero {
id: root
property string symbol: ""
property int atomicNumber: 0
property string atomicMass: ""
property string remainder: ""
readonly property real lockupBaseSize: Theme.fontSizeDisplayLarge * 0.8
readonly property real lockupScale: Math.min(1, availableWidth / Math.max(1, lockupBaseSize * 1.6 + Theme.spacingS + remainderMetrics.width))
readonly property real lockupSize: lockupBaseSize * lockupScale
readonly property real tileSize: lockupSize * 1.6
accentSize: tileSize
paddingV: Theme.spacingXL
StyledTextMetrics {
id: remainderMetrics
text: root.remainder
font.pixelSize: root.lockupBaseSize
font.weight: Theme.fontWeightBold
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingS
Rectangle {
id: tile
width: root.tileSize
height: width
radius: Theme.cornerRadiusS
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
StyledText {
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: tile.width * 0.07
text: root.atomicNumber
font.pixelSize: tile.width * 0.15
font.weight: Theme.fontWeightMedium
color: Theme.onPrimary
}
StyledText {
anchors.centerIn: parent
anchors.verticalCenterOffset: tile.width * 0.03
text: root.symbol
font.pixelSize: root.lockupSize * 0.9
font.weight: Theme.fontWeightBold
color: Theme.onPrimary
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: tile.width * 0.05
text: root.atomicMass
font.pixelSize: tile.width * 0.11
color: Theme.onPrimary
}
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: root.remainder
font.pixelSize: root.lockupSize
font.weight: Theme.fontWeightBold
color: Theme.onPrimaryContainer
}
}
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
width: versionText.implicitWidth + Theme.spacingL * 2
height: Theme.buttonHeightXXS
radius: Theme.fullRadius(width, height)
color: Theme.primary
StyledText {
id: versionText
anchors.centerIn: parent
text: "dms " + ChangelogService.currentVersion
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightBold
color: Theme.onPrimary
}
}
}
+42 -45
View File
@@ -7,8 +7,8 @@ import qs.Widgets
DFloatingWindow {
id: root
readonly property int modalWidth: 680
readonly property int modalHeight: screen ? Math.min(720, screen.height - 80) : 720
readonly property int modalWidth: 720
readonly property int modalHeight: screen ? Math.min(800, screen.height - 80) : 800
signal changelogDismissed
@@ -16,6 +16,12 @@ DFloatingWindow {
visible = true;
}
function dismiss() {
ChangelogService.dismissChangelog();
changelogDismissed();
visible = false;
}
objectName: "changelogModal"
title: "What's New"
minimumSize: Qt.size(modalWidth, modalHeight)
@@ -57,58 +63,55 @@ DFloatingWindow {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: headerRow.bottom
anchors.bottom: footerRow.top
anchors.bottom: parent.bottom
clip: true
contentHeight: mainColumn.height + Theme.spacingL * 2
contentHeight: mainColumn.height + fabBar.height + Theme.spacingL * 3
contentWidth: width
ChangelogContent {
id: mainColumn
anchors.horizontalCenter: parent.horizontalCenter
width: Math.min(600, parent.width - Theme.spacingXL * 2)
width: Math.min(640, parent.width - Theme.spacingXL * 2)
}
}
Rectangle {
id: footerRow
anchors.left: parent.left
anchors.right: parent.right
Row {
id: fabBar
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
height: Math.round(Theme.fontSizeMedium * 4.5)
color: Theme.floatingWindowNestedSurface
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
anchors.bottomMargin: Theme.spacingL
z: 2
spacing: Theme.spacingM
LayoutMirroring.enabled: I18n.isRtl
Rectangle {
anchors.top: parent.top
width: parent.width
height: 1
color: Theme.outlineMedium
opacity: 0.5
}
DButton {
id: notesButton
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Release notes", "settings page and card title, notes for a DMS release")
iconName: "auto_awesome"
buttonHeight: Theme.buttonHeightM
backgroundColor: Theme.secondaryContainer
textColor: Theme.onSecondaryContainer
onClicked: PopoutService.openSettingsWithTab("updater_changelog")
Row {
anchors.centerIn: parent
spacing: Theme.spacingM
DButton {
text: I18n.tr("Release notes", "settings page and card title, notes for a DMS release")
iconName: "auto_awesome"
backgroundColor: Theme.chipSurface
textColor: Theme.surfaceText
onClicked: {
root.dismiss();
PopoutService.openSettingsWithTab("updater_changelog");
Loader {
anchors.fill: parent
z: -1
active: Theme.elevationEnabled
sourceComponent: ElevationShadow {
level: Theme.elevationLevel3
targetColor: notesButton.color
targetRadius: notesButton.radius
}
}
}
DButton {
text: I18n.tr("OK")
iconName: "check"
backgroundColor: Theme.primary
textColor: Theme.primaryText
onClicked: root.dismiss()
}
DFab {
anchors.verticalCenter: parent.verticalCenter
text: "Let's go"
iconName: "rocket_launch"
colorRole: "primary"
onClicked: root.dismiss()
}
}
}
@@ -117,10 +120,4 @@ DFloatingWindow {
id: windowControls
targetWindow: root
}
function dismiss() {
ChangelogService.dismissChangelog();
changelogDismissed();
visible = false;
}
}
@@ -1,27 +0,0 @@
import QtQuick
import qs.Common
import qs.DCommon.Widgets
Row {
id: root
property alias text: noteText.text
spacing: Theme.spacingS
DIcon {
name: "arrow_right"
size: Theme.iconSizeSmall - 2
color: Theme.surfaceVariantText
anchors.top: parent.top
anchors.topMargin: 2
}
StyledText {
id: noteText
width: root.width - Theme.iconSizeSmall - Theme.spacingS
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.WordWrap
}
}
@@ -1,76 +0,0 @@
import QtQuick
import qs.Common
import qs.DCommon.Widgets
Rectangle {
id: root
property string iconName: ""
property string title: ""
property string description: ""
signal clicked
readonly property real iconContainerSize: Math.round(Theme.iconSize * 1.5)
height: Math.round(Theme.fontSizeMedium * 6.4)
radius: Theme.cornerRadius
color: Theme.floatingWindowNestedSurface
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
Rectangle {
anchors.fill: parent
radius: parent.radius
color: Theme.primary
opacity: mouseArea.containsMouse ? 0.12 : 0
}
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Rectangle {
width: root.iconContainerSize
height: root.iconContainerSize
radius: Math.round(root.iconContainerSize * 0.28)
color: Theme.primaryContainer
anchors.horizontalCenter: parent.horizontalCenter
DIcon {
anchors.centerIn: parent
name: root.iconName
size: Theme.iconSize - 4
color: Theme.accentOnPrimaryContainer
}
}
Column {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXXS
StyledText {
text: root.title
font.pixelSize: Theme.fontSizeSmall
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: root.description
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
}
}
+66 -87
View File
@@ -154,54 +154,6 @@ DFloatingWindow {
title: root.title
closeTooltipText: I18n.tr("Skip setup", "greeter skip button tooltip")
onCloseRequested: root.skip()
Rectangle {
id: pageIndicatorContainer
readonly property real indicatorHeight: Math.round(Theme.fontSizeMedium * 2)
anchors.verticalCenter: parent.verticalCenter
width: pageIndicatorRow.width + Theme.spacingM * 2
height: indicatorHeight
radius: Theme.fullRadius(width, height)
color: Theme.floatingWindowNestedSurface
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
Row {
id: pageIndicatorRow
anchors.centerIn: parent
spacing: Theme.spacingS
Repeater {
model: root.totalPages
Rectangle {
required property int index
property bool isActive: index === root.currentPage
readonly property real dotSize: Math.round(Theme.spacingS * 1.3)
width: isActive ? dotSize * 3 : dotSize
height: dotSize
radius: dotSize / 2
color: dotColor.value
anchors.verticalCenter: parent.verticalCenter
DColorAnimation {
id: dotColor
to: isActive ? Theme.primary : Theme.surfaceTextAlpha
duration: Theme.shortDuration
}
Behavior on width {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
}
}
}
Item {
@@ -219,66 +171,93 @@ DFloatingWindow {
}
}
Rectangle {
Item {
id: footerRow
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: Math.round(Theme.fontSizeMedium * 4.5)
color: Theme.floatingWindowNestedSurface
border.width: Theme.layerOutlineWidth
border.color: Theme.outlineMedium
Rectangle {
anchors.top: parent.top
width: parent.width
height: 1
color: Theme.outlineMedium
opacity: 0.5
}
height: Theme.buttonHeightM + Theme.spacingL * 2
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
Row {
anchors.right: parent.right
anchors.rightMargin: Theme.windowInset
anchors.left: parent.left
anchors.leftMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DButton {
visible: root.currentPage < root.totalPages - 1
text: I18n.tr("Skip", "greeter skip button")
backgroundColor: "transparent"
textColor: Theme.surfaceVariantText
onClicked: root.currentPage === 1 ? root.nextPage() : root.skip()
}
spacing: Theme.spacingS
DButton {
visible: root.currentPage > 0
text: I18n.tr("Back", "greeter back button")
iconName: "arrow_back"
backgroundColor: Theme.chipSurface
textColor: Theme.surfaceText
buttonHeight: Theme.buttonHeightM
backgroundColor: Theme.secondaryContainer
textColor: Theme.onSecondaryContainer
onClicked: root.prevPage()
}
DButton {
visible: root.currentPage < root.totalPages - 1
enabled: !(root.currentPage === 1 && pageLoader.item && pageLoader.item.isRunning)
text: root.currentPage === 0 ? I18n.tr("Get Started", "greeter first page button") : I18n.tr("Next", "greeter next button")
iconName: "arrow_forward"
backgroundColor: Theme.primary
textColor: Theme.primaryText
onClicked: root.nextPage()
text: I18n.tr("Skip", "greeter skip button")
buttonHeight: Theme.buttonHeightM
backgroundColor: "transparent"
textColor: Theme.primary
onClicked: root.currentPage === 1 ? root.nextPage() : root.skip()
}
}
DButton {
visible: root.currentPage === root.totalPages - 1
text: I18n.tr("Finish", "greeter finish button")
iconName: "check"
backgroundColor: Theme.primary
textColor: Theme.primaryText
onClicked: root.finish()
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
Repeater {
model: root.totalPages
Rectangle {
required property int index
readonly property bool isActive: index === root.currentPage
width: isActive ? Theme.spacingXL : Theme.spacingS
height: Theme.spacingS
radius: Theme.fullRadius(width, height)
color: isActive ? Theme.primary : Theme.outlineVariant
anchors.verticalCenter: parent.verticalCenter
Behavior on width {
enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
NumberAnimation {
duration: Theme.expressiveDurations.expressiveFastSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
}
}
Behavior on color {
enabled: Theme.currentAnimationSpeed !== SettingsData.AnimationSpeed.None
ColorAnimation {
duration: Theme.expressiveDurations.expressiveEffects
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
}
}
}
}
}
DButton {
readonly property bool lastPage: root.currentPage === root.totalPages - 1
anchors.right: parent.right
anchors.rightMargin: Theme.spacingL
anchors.verticalCenter: parent.verticalCenter
enabled: !(root.currentPage === 1 && pageLoader.item && pageLoader.item.isRunning)
text: lastPage ? I18n.tr("Finish", "greeter finish button") : root.currentPage === 0 ? I18n.tr("Get Started", "greeter first page button") : I18n.tr("Next", "greeter next button")
iconName: lastPage ? "check" : "arrow_forward"
buttonHeight: Theme.buttonHeightM
backgroundColor: Theme.primary
textColor: Theme.onPrimary
onClicked: lastPage ? root.finish() : root.nextPage()
}
}
}
@@ -9,7 +9,7 @@ Item {
property var greeterRoot: parent ? parent.greeterRoot : null
readonly property var curatedPluginIds: ["quickCapture", "dankKDEConnect", "wallpaperCarousel", "calculator", "emojiLauncher", "dockerManager", "amdGpuMonitor", "bongoCat"]
readonly property var curatedPluginIds: ["quickCapture", "dankKDEConnect", "wallpaperCarousel", "calculator", "emojiLauncher", "dockerManager", "dankSoftwareDepot", "homeAssistantMonitor"]
readonly property real headerIconContainerSize: Math.round(Theme.iconSize * 2)
+205 -111
View File
@@ -1,135 +1,229 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Effects
import qs.Common
import qs.Services
import qs.DCommon.Widgets
import qs.Widgets
Item {
id: root
readonly property real logoSize: Math.round(Theme.iconSize * 5.3)
readonly property int columns: 3
readonly property real cardWidth: (content.width - Theme.spacingS * (columns - 1)) / columns
readonly property var features: [
{
"icon": "auto_awesome",
"shape": "softBurst",
"hoverShape": "flower",
"accent": "purple",
"heading": I18n.tr("Dynamic Theming", "greeter feature card title"),
"description": I18n.tr("Colors from wallpaper", "greeter feature card description"),
"target": "theme"
},
{
"icon": "toolbar",
"shape": "pill",
"hoverShape": "cookie4",
"accent": "blue",
"heading": I18n.tr("Bar"),
"description": I18n.tr("Modular widget bar", "greeter feature card description"),
"target": "dankbar_settings"
},
{
"icon": "extension",
"shape": "puffy",
"hoverShape": "clover8",
"accent": "green",
"heading": I18n.tr("Plugins", "greeter feature card title"),
"description": I18n.tr("Extensible architecture", "greeter feature card description"),
"target": "plugins"
},
{
"icon": "monitor",
"shape": "gem",
"hoverShape": "cookie6",
"accent": "orange",
"heading": I18n.tr("Multi-Monitor", "greeter feature card title"),
"description": I18n.tr("Per-screen config", "greeter feature card description"),
"target": CompositorService.supportsDisplayConfig ? "display_config" : "display_widgets"
},
{
"icon": "nightlight",
"shape": "sunny",
"hoverShape": "softBurst",
"accent": "pink",
"heading": I18n.tr("Display Control", "greeter feature card title"),
"description": I18n.tr("Night mode & gamma", "greeter feature card description"),
"target": "display_gamma"
},
{
"icon": "tune",
"shape": "cookie9",
"hoverShape": "sunny",
"accent": "teal",
"heading": I18n.tr("Control Center", "greeter feature card title"),
"description": I18n.tr("Quick system toggles", "greeter feature card description"),
"target": "control_center"
}
]
Column {
id: mainColumn
anchors.centerIn: parent
width: Math.min(Math.round(Theme.fontSizeMedium * 43), parent.width - Theme.spacingXL * 2)
spacing: Theme.spacingXL
function open(target) {
switch (target) {
case "control_center":
BarWidgetService.getBarWindowOnFocusedScreen()?.triggerControlCenter();
return;
}
PopoutService.openSettingsWithTab(target);
}
DFlickable {
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: Math.max(height, content.height + Theme.spacingL * 2)
Column {
width: parent.width
spacing: Theme.spacingM
id: content
anchors.horizontalCenter: parent.horizontalCenter
y: Math.max(Theme.spacingS, (parent.height - height) / 2)
width: Math.min(640, parent.width - Theme.spacingXL * 2)
spacing: Theme.spacingL
Image {
width: root.logoSize
height: width * (569.94629 / 506.50931)
anchors.horizontalCenter: parent.horizontalCenter
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: true
asynchronous: true
source: "file://" + Theme.shellDir + "/assets/danklogonormal.svg"
layer.enabled: true
layer.smooth: true
layer.mipmap: true
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: Theme.primary
}
}
Column {
ShapeHero {
width: parent.width
spacing: Theme.spacingXS
paddingV: Theme.spacingXL
backdrop: Item {
DMaterialShape {
width: parent.height * 1.6
height: width
x: parent.width - width * 0.72
y: -height * 0.38
shape: "cookie12"
respectThemeShape: false
color: Theme.withAlpha(Theme.primary, Theme.tonalTintAlpha)
rotation: 10
}
StyledText {
text: I18n.tr("Welcome to DankMaterialShell", "greeter welcome page title")
font.pixelSize: Theme.fontSizeXLarge + 4
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
DMaterialShape {
width: parent.height * 1.1
height: width
x: parent.width - width * 1.35
y: parent.height - height * 0.55
shape: "softBurst"
respectThemeShape: false
color: Theme.withAlpha(Theme.tertiary, Theme.tonalTintAlpha)
rotation: -12
}
StyledText {
text: I18n.tr("A modern desktop shell for Wayland compositors", "greeter welcome page tagline")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outlineMedium
opacity: 0.3
}
Column {
width: parent.width
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Features", "greeter welcome page section header")
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
Grid {
width: parent.width
columns: 3
rowSpacing: Theme.spacingS
columnSpacing: Theme.spacingS
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "auto_awesome"
title: I18n.tr("Dynamic Theming", "greeter feature card title")
description: I18n.tr("Colors from wallpaper", "greeter feature card description")
onClicked: PopoutService.openSettingsWithTab("theme")
}
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "view_carousel"
title: I18n.tr("DankBar", "greeter feature card title")
description: I18n.tr("Modular widget bar", "greeter feature card description")
onClicked: PopoutService.openSettingsWithTab("dankbar_settings")
}
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "extension"
title: I18n.tr("Plugins", "greeter feature card title")
description: I18n.tr("Extensible architecture", "greeter feature card description")
onClicked: PopoutService.openSettingsWithTab("plugins")
}
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "layers"
title: I18n.tr("Multi-Monitor", "greeter feature card title")
description: I18n.tr("Per-screen config", "greeter feature card description")
onClicked: {
PopoutService.openSettingsWithTab(CompositorService.supportsDisplayConfig ? "display_config" : "display_widgets");
DMaterialShape {
width: parent.height * 0.75
height: width
x: parent.width * 0.6
y: -height * 0.2
shape: "pill"
respectThemeShape: false
color: Theme.withAlpha(Theme.secondary, Theme.tonalTintAlpha)
rotation: -35
}
}
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "nightlight"
title: I18n.tr("Display Control", "greeter feature card title")
description: I18n.tr("Night mode & gamma", "greeter feature card description")
onClicked: PopoutService.openSettingsWithTab("display_gamma")
Row {
width: parent.width
spacing: Theme.spacingXL
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
Item {
id: logo
width: Theme.buttonHeightM * 1.6
height: width
anchors.verticalCenter: parent.verticalCenter
Image {
readonly property real dpr: Screen.devicePixelRatio
anchors.centerIn: parent
height: parent.height
width: height
// 2x raster plus mipmap keeps the thin rings smooth at fractional scales
sourceSize.height: Math.ceil(height * dpr * 2)
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: true
asynchronous: true
source: "file://" + Theme.shellDir + "/assets/danklogo.svg"
layer.enabled: true
layer.smooth: true
layer.textureSize: Qt.size(Math.ceil(width * dpr), Math.ceil(height * dpr))
layer.effect: MultiEffect {
saturation: 0
colorization: 1
colorizationColor: Theme.primary
}
}
}
Column {
width: parent.width - logo.width - parent.spacing
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
StyledText {
width: parent.width
text: I18n.tr("Welcome to DankMaterialShell", "greeter welcome page title")
font.pixelSize: Theme.fontSizeXXLarge
font.weight: Theme.fontWeightBold
color: Theme.onPrimaryContainer
horizontalAlignment: Text.AlignLeft
wrapMode: Text.WordWrap
}
StyledText {
width: parent.width
text: I18n.tr("A modern desktop shell for Wayland compositors", "greeter welcome page tagline")
font.pixelSize: Theme.fontSizeLarge
color: Theme.onPrimaryContainer
horizontalAlignment: Text.AlignLeft
wrapMode: Text.WordWrap
}
}
}
}
Grid {
id: grid
readonly property real cellHeight: {
let tallest = 0;
for (const card of children)
tallest = Math.max(tallest, card.implicitHeight ?? 0);
return tallest;
}
GreeterFeatureCard {
width: (parent.width - Theme.spacingS * 2) / 3
iconName: "tune"
title: I18n.tr("Control Center", "greeter feature card title")
description: I18n.tr("Quick system toggles", "greeter feature card description")
onClicked: BarWidgetService.getBarWindowOnFocusedScreen()?.triggerControlCenter()
width: parent.width
columns: root.columns
rowSpacing: Theme.spacingS
columnSpacing: Theme.spacingS
layoutDirection: I18n.isRtl ? Qt.RightToLeft : Qt.LeftToRight
Repeater {
model: root.features
delegate: FeatureCard {
required property var modelData
width: root.cardWidth
height: grid.cellHeight
iconName: modelData.icon
shape: modelData.shape
hoverShape: modelData.hoverShape
accent: modelData.accent
heading: modelData.heading
description: modelData.description
onClicked: root.open(modelData.target)
}
}
}
}
@@ -323,7 +323,7 @@ Item {
settingKey: "matugenSpec"
text: I18n.tr("Material palette", "theme settings card title, matugen palette options")
enabled: Theme.matugenAvailable
model: ["2021", "2025", "DMS"]
model: ["2021", "2025", "dms"]
currentIndex: SettingsData.matugenSpec === "dms" ? 2 : SettingsData.matugenSpec === "2025" ? 1 : 0
onSelectionChanged: (index, selected) => {
if (!selected)
@@ -4,12 +4,14 @@ import qs.Services
import qs.DCommon.Widgets
DHeroCard {
brand: "DMS"
brand: "dms"
accent: {
const semver = ShellVersionService.semverVersion.replace(/^v/, "");
const base = semver.match(/^\d+\.\d+/);
return base ? base[0] : semver || SystemUpdateService.shellRunning.replace(/^v/, "");
}
caption: ShellVersionService.shellCodename.toUpperCase()
color: SettingsMetrics.rowColor
caption: ShellVersionService.shellCodename
symbol: "He"
atomicNumber: 2
atomicMass: "4.0026"
}
+8 -8
View File
@@ -195,7 +195,7 @@ Singleton {
name: I18n.tr("Settings", "settings window title"),
icon: "svg+corner:" + dmsLogoPath + "|settings",
cornerIcon: "settings",
comment: "DMS",
comment: "dms",
action: "ipc:settings",
categories: ["Settings", "System"],
defaultTrigger: "",
@@ -206,7 +206,7 @@ Singleton {
name: I18n.tr("Notepad", "Notepad"),
icon: "svg+corner:" + dmsLogoPath + "|description",
cornerIcon: "description",
comment: "DMS",
comment: "dms",
action: "ipc:notepad",
categories: ["Office", "Utility"],
defaultTrigger: "",
@@ -217,7 +217,7 @@ Singleton {
name: I18n.tr("System Monitor", "sysmon window title"),
icon: "svg+corner:" + dmsLogoPath + "|monitor_heart",
cornerIcon: "monitor_heart",
comment: "DMS",
comment: "dms",
action: "ipc:processlist",
categories: ["System", "Monitor"],
defaultTrigger: "",
@@ -228,7 +228,7 @@ Singleton {
name: I18n.tr("Color Picker"),
icon: "svg+corner:" + dmsLogoPath + "|palette",
cornerIcon: "palette",
comment: "DMS",
comment: "dms",
action: "ipc:color-picker",
categories: ["Graphics", "Utility"],
defaultTrigger: "",
@@ -238,7 +238,7 @@ Singleton {
id: "dms_power",
name: I18n.tr("Power"),
cornerIcon: "power_settings_new",
comment: "DMS",
comment: "dms",
defaultTrigger: "pw",
isLauncher: true,
viewMode: "list",
@@ -249,7 +249,7 @@ Singleton {
id: "dms_vpn",
name: I18n.tr("VPN", "virtual private network, widget and page title"),
cornerIcon: "vpn_key",
comment: "DMS",
comment: "dms",
defaultTrigger: "",
isLauncher: true,
viewMode: "list",
@@ -261,7 +261,7 @@ Singleton {
name: I18n.tr("QR Generator"),
icon: "svg+corner:" + dmsLogoPath + "|qr_code",
cornerIcon: "qr_code",
comment: "DMS",
comment: "dms",
action: "ipc:qr-generator",
categories: ["Utility"],
defaultTrigger: "qrg",
@@ -281,7 +281,7 @@ Singleton {
id: "dms_clipboard_search",
name: I18n.tr("Clipboard"),
cornerIcon: "content_paste",
comment: "DMS",
comment: "dms",
defaultTrigger: "cb",
isLauncher: true,
viewMode: "list",
+1 -1
View File
@@ -17,7 +17,7 @@ Singleton {
const m = v.match(/^\d+\.\d+/);
return m ? m[0] : "";
}
readonly property bool changelogEnabled: false
readonly property bool changelogEnabled: true
readonly property bool ready: SessionData._hasLoaded && !SessionData.isGreeterMode && FirstLaunchService.checkComplete && currentVersion !== ""
property bool changelogDismissed: false
+2 -2
View File
@@ -615,7 +615,7 @@ Singleton {
function gamma(value: string): string {
if (!root.gammaAdjustAvailable)
return "Gamma adjustment not available (requires DMS API v34+)";
return "Gamma adjustment not available (requires dms API v34+)";
if (!value)
return SessionData.displayGamma.toString();
@@ -629,7 +629,7 @@ Singleton {
function contrast(value: string): string {
if (!root.gammaAdjustAvailable)
return "Contrast adjustment not available (requires DMS API v34+)";
return "Contrast adjustment not available (requires dms API v34+)";
if (!value)
return SessionData.displayContrast.toString();
+1 -1
View File
@@ -1088,7 +1088,7 @@ Singleton {
}
function sendTestNotification(index) {
const notifications = [["Notification Position Test", "DMS test notification 1 of 3 ~ Hi there!", "preferences-system"], ["Second Test", "DMS Notification 2 of 3 ~ Check it out!", "applications-graphics"], ["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "face-smile"]];
const notifications = [["Notification Position Test", "dms test notification 1 of 3 ~ Hi there!", "preferences-system"], ["Second Test", "dms notification 2 of 3 ~ Check it out!", "applications-graphics"], ["Third Test", "DMS notification 3 of 3 ~ Enjoy!", "face-smile"]];
if (index < 0 || index >= notifications.length) {
return;
+1 -1
View File
@@ -225,7 +225,7 @@ Singleton {
function status(): string {
if (!root.available)
return "Screen time tracking: unavailable (DMS server without wellbeing support)";
return "Screen time tracking: unavailable (dms server without wellbeing support)";
const lines = ["Screen time tracking: " + (root.enabled ? "enabled" : "disabled")];
if (root.tracking)
lines.push("Today: " + root.formatDuration(root.today.active ?? 0));
+95
View File
@@ -0,0 +1,95 @@
import QtQuick
import qs.Common
import qs.DCommon.Widgets
DCard {
id: root
property string iconName: ""
property string shape: "circle"
property string hoverShape: "circle"
property string accent: ""
property string heading: ""
property string description: ""
readonly property var accentPair: Theme.accent(accent)
readonly property bool engaged: hover.hovered || focusTarget.visualFocus
readonly property var morphSpring: Theme.springPreset("default", Theme.expressiveDurations.expressiveDefaultSpatial)
onEngagedChanged: morphMotion.retarget(engaged ? 1 : 0)
clickable: true
pad: Theme.spacingL
restRadius: Theme.groupedListOuterRadius
bodyRadius: focusTarget.pressed ? Theme.cornerRadiusM : restRadius
implicitHeight: body.implicitHeight + pad * 2
Accessible.name: heading
Accessible.description: description
HoverHandler {
id: hover
}
SpringMotion {
id: morphMotion
enabled: !Theme.springMotionDisabled
stiffness: root.morphSpring.stiffness
damping: root.morphSpring.damping
}
Column {
id: body
width: parent.width
spacing: Theme.spacingM
Item {
width: parent.width
height: Theme.buttonHeightM
MorphShape {
id: badge
width: parent.height
height: width
fromShape: root.shape
toShape: root.hoverShape
progress: morphMotion.value
color: root.accentPair?.container ?? Theme.primaryContainer
}
DIcon {
anchors.centerIn: badge
name: root.iconName
size: Theme.iconSize
color: root.accentPair?.onContainer ?? Theme.onPrimaryContainer
}
DIcon {
anchors.right: parent.right
anchors.top: parent.top
name: "arrow_outward"
size: Theme.iconSizeMedium
color: root.engaged ? Theme.primary : Theme.surfaceVariantText
rotation: I18n.isRtl ? -90 : 0
}
}
StyledText {
width: parent.width
text: root.heading
font.pixelSize: Theme.fontSizeLarge
font.weight: Theme.fontWeightBold
color: Theme.surfaceText
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
StyledText {
width: parent.width
text: root.description
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
}
}
+56
View File
@@ -0,0 +1,56 @@
import "../DCommon/Widgets/MaterialShapes.js" as Shapes
import QtQuick
import QtQuick.Shapes
import qs.Common
import qs.DCommon.Widgets
Item {
id: root
property string fromShape: "circle"
property string toShape: "circle"
property color color: Theme.primary
property real progress: 0
readonly property int samples: 128
readonly property real clampedProgress: Math.max(0, Math.min(1, progress))
readonly property bool morphing: clampedProgress > 0 && clampedProgress < 1 && Theme.shapeScale !== 0
readonly property var fromProfile: Shapes.polarProfile(fromShape, samples)
readonly property var toProfile: Shapes.polarProfile(toShape, samples)
readonly property var outline: {
if (!morphing)
return [];
const size = Math.min(width, height);
const points = [];
for (let i = 0; i <= samples; i++) {
const index = i % samples;
const angle = -Math.PI + 2 * Math.PI * index / samples;
const radius = (fromProfile[index] + (toProfile[index] - fromProfile[index]) * clampedProgress) * size;
points.push(Qt.point(width / 2 + Math.cos(angle) * radius, height / 2 + Math.sin(angle) * radius));
}
return points;
}
DMaterialShape {
anchors.fill: parent
shape: root.clampedProgress >= 1 ? root.toShape : root.fromShape
color: root.color
visible: !root.morphing
}
Shape {
anchors.fill: parent
visible: root.morphing
preferredRendererType: Shape.CurveRenderer
ShapePath {
strokeColor: "transparent"
strokeWidth: 0
fillColor: root.color
PathPolyline {
path: root.outline
}
}
}
}
+84
View File
@@ -0,0 +1,84 @@
import QtQuick
import Quickshell.Widgets
import qs.Common
import qs.DCommon.Widgets
Item {
id: root
default property alias content: contentColumn.data
property real paddingV: Theme.spacingXL + Theme.spacingS
property real accentSize: Theme.buttonHeightM * 1.6
property Component backdrop: defaultBackdrop
readonly property real availableWidth: width - Theme.spacingXL * 2
height: contentColumn.implicitHeight + paddingV * 2
component Decoration: DMaterialShape {
respectThemeShape: false
}
Component {
id: defaultBackdrop
Item {
Decoration {
width: root.height * 1.15
height: width
x: parent.width - width * 0.62
y: -height * 0.42
shape: "cookie12"
color: Theme.withAlpha(Theme.primary, Theme.tonalTintAlpha)
rotation: 12
}
Decoration {
width: root.height * 0.8
height: width
x: -width * 0.38
y: parent.height - height * 0.58
shape: "softBurst"
color: Theme.withAlpha(Theme.tertiary, Theme.tonalTintAlpha)
rotation: -8
}
Decoration {
width: root.accentSize * 0.6
height: width
x: parent.width * 0.12
y: root.paddingV * 0.6
shape: "flower"
color: Theme.withAlpha(Theme.onPrimaryContainer, Theme.stateLayerHover)
}
Decoration {
width: root.accentSize * 0.75
height: width
x: parent.width * 0.8
y: parent.height - height - root.paddingV * 0.5
shape: "pill"
color: Theme.withAlpha(Theme.secondary, Theme.tonalTintAlpha)
rotation: -40
}
}
}
ClippingRectangle {
anchors.fill: parent
radius: Theme.cornerRadiusXL
color: Theme.primaryContainer
Loader {
anchors.fill: parent
sourceComponent: root.backdrop
}
}
Column {
id: contentColumn
anchors.centerIn: parent
width: root.availableWidth
spacing: Theme.spacingM
}
}