mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 21:45:38 -05:00
settings: complete re-organize and breakout
This commit is contained in:
17
Modules/Settings/ClockTab.qml
Normal file
17
Modules/Settings/ClockTab.qml
Normal file
@@ -0,0 +1,17 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
text: "24-Hour Format"
|
||||
description: "Use 24-hour time format instead of 12-hour AM/PM"
|
||||
checked: Prefs.use24HourClock
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setClockFormat(checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
Modules/Settings/DisplayTab.qml
Normal file
161
Modules/Settings/DisplayTab.qml
Normal file
@@ -0,0 +1,161 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
width: parent.width
|
||||
spacing: Theme.spacingL
|
||||
|
||||
DankToggle {
|
||||
text: "Night Mode"
|
||||
description: "Apply warm color temperature to reduce eye strain"
|
||||
checked: Prefs.nightModeEnabled
|
||||
onToggled: (checked) => {
|
||||
Prefs.setNightModeEnabled(checked);
|
||||
if (checked)
|
||||
nightModeEnableProcess.running = true;
|
||||
else
|
||||
nightModeDisableProcess.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Light Mode"
|
||||
description: "Use light theme instead of dark theme"
|
||||
checked: Prefs.isLightMode
|
||||
onToggled: (checked) => {
|
||||
Prefs.setLightMode(checked);
|
||||
Theme.isLightMode = checked;
|
||||
}
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
text: "Icon Theme"
|
||||
description: "Select icon theme (requires restart)"
|
||||
currentValue: Prefs.iconTheme
|
||||
options: Prefs.availableIconThemes
|
||||
onValueChanged: (value) => {
|
||||
Prefs.setIconTheme(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Top Bar Transparency
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "Top Bar Transparency"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
width: parent.width
|
||||
value: Math.round(Prefs.topBarTransparency * 100)
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
leftIcon: "opacity"
|
||||
rightIcon: "circle"
|
||||
unit: "%"
|
||||
showValue: true
|
||||
onSliderDragFinished: (finalValue) => {
|
||||
let transparencyValue = finalValue / 100;
|
||||
Prefs.setTopBarTransparency(transparencyValue);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Adjust the transparency of the top bar background"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Popup Transparency
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "Popup Transparency"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
width: parent.width
|
||||
value: Math.round(Prefs.popupTransparency * 100)
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
leftIcon: "blur_on"
|
||||
rightIcon: "circle"
|
||||
unit: "%"
|
||||
showValue: true
|
||||
onSliderDragFinished: (finalValue) => {
|
||||
let transparencyValue = finalValue / 100;
|
||||
Prefs.setPopupTransparency(transparencyValue);
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Adjust transparency for dialogs, menus, and popups"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Theme Picker
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "Theme Color"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
ThemePicker {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Night mode processes
|
||||
Process {
|
||||
id: nightModeEnableProcess
|
||||
|
||||
command: ["bash", "-c", "if command -v wlsunset > /dev/null; then pkill wlsunset; wlsunset -t 3000 & elif command -v redshift > /dev/null; then pkill redshift; redshift -P -O 3000 & else echo 'No night mode tool available'; fi"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("Failed to enable night mode");
|
||||
Prefs.setNightModeEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: nightModeDisableProcess
|
||||
|
||||
command: ["bash", "-c", "pkill wlsunset; pkill redshift; if command -v wlsunset > /dev/null; then wlsunset -t 6500 -T 6500 & sleep 1; pkill wlsunset; elif command -v redshift > /dev/null; then redshift -P -O 6500; redshift -x; fi"]
|
||||
running: false
|
||||
onExited: (exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
console.warn("Failed to disable night mode");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
167
Modules/Settings/ProfileTab.qml
Normal file
167
Modules/Settings/ProfileTab.qml
Normal file
@@ -0,0 +1,167 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Profile Image Preview and Input
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "Profile Image"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
// Profile Image Preview with circular crop
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Circular profile image preview
|
||||
Item {
|
||||
id: avatarContainer
|
||||
|
||||
property bool hasImage: avatarImageSource.status === Image.Ready
|
||||
|
||||
width: 54
|
||||
height: 54
|
||||
|
||||
// This rectangle provides the themed ring via its border.
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: Theme.primary
|
||||
border.width: 1 // The ring is 1px thick.
|
||||
visible: parent.hasImage
|
||||
}
|
||||
|
||||
// Hidden Image loader. Its only purpose is to load the texture.
|
||||
Image {
|
||||
id: avatarImageSource
|
||||
|
||||
source: {
|
||||
if (profileImageInput.text === "")
|
||||
return "";
|
||||
|
||||
if (profileImageInput.text.startsWith("/"))
|
||||
return "file://" + profileImageInput.text;
|
||||
|
||||
return profileImageInput.text;
|
||||
}
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
mipmap: true
|
||||
cache: true
|
||||
visible: false // This item is never shown directly.
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
source: avatarImageSource
|
||||
maskEnabled: true
|
||||
maskSource: settingsCircularMask
|
||||
visible: avatarContainer.hasImage
|
||||
maskThresholdMin: 0.5
|
||||
maskSpreadAtMin: 1
|
||||
}
|
||||
|
||||
Item {
|
||||
id: settingsCircularMask
|
||||
|
||||
width: 54 - 10
|
||||
height: 54 - 10
|
||||
layer.enabled: true
|
||||
layer.smooth: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: "black"
|
||||
antialiasing: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fallback for when there is no image.
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: width / 2
|
||||
color: Theme.primary
|
||||
visible: !parent.hasImage
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "person"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Error icon for when the image fails to load.
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: "warning"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
visible: profileImageInput.text !== "" && avatarImageSource.status === Image.Error
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Input field
|
||||
Column {
|
||||
width: parent.width - 80 - Theme.spacingM
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 48
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.surfaceVariant
|
||||
border.color: profileImageInput.activeFocus ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||
border.width: profileImageInput.activeFocus ? 2 : 1
|
||||
|
||||
DankTextField {
|
||||
id: profileImageInput
|
||||
|
||||
anchors.fill: parent
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
text: Prefs.profileImage
|
||||
placeholderText: "Enter image path or URL..."
|
||||
backgroundColor: "transparent"
|
||||
normalBorderColor: "transparent"
|
||||
focusedBorderColor: "transparent"
|
||||
onEditingFinished: {
|
||||
Prefs.setProfileImage(text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Local filesystem path or URL to an image file."
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
231
Modules/Settings/SettingsPopup.qml
Normal file
231
Modules/Settings/SettingsPopup.qml
Normal file
@@ -0,0 +1,231 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings
|
||||
|
||||
PanelWindow {
|
||||
id: settingsPopup
|
||||
|
||||
property bool settingsVisible: false
|
||||
|
||||
signal closingPopup()
|
||||
|
||||
onSettingsVisibleChanged: {
|
||||
if (!settingsVisible) {
|
||||
closingPopup();
|
||||
// Hide any open dropdown when settings close
|
||||
if (typeof globalDropdownWindow !== 'undefined') {
|
||||
globalDropdownWindow.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
visible: settingsVisible
|
||||
implicitWidth: 600
|
||||
implicitHeight: 700
|
||||
WlrLayershell.layer: WlrLayershell.Overlay
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// Darkened background
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
opacity: 0.5
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: settingsPopup.settingsVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Main settings panel - spotlight-like centered appearance
|
||||
Rectangle {
|
||||
id: mainPanel
|
||||
|
||||
width: Math.min(600, parent.width - Theme.spacingXL * 2)
|
||||
height: Math.min(700, parent.height - Theme.spacingXL * 2)
|
||||
anchors.centerIn: parent
|
||||
color: Theme.popupBackground()
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
// Simple opacity and scale control tied directly to settingsVisible
|
||||
opacity: settingsPopup.settingsVisible ? 1 : 0
|
||||
scale: settingsPopup.settingsVisible ? 1 : 0.95
|
||||
// Add shadow effect
|
||||
layer.enabled: true
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Header
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "settings"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Settings"
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width - 175 // Spacer to push close button to the right
|
||||
height: 1
|
||||
}
|
||||
|
||||
// Close button
|
||||
DankActionButton {
|
||||
circular: false
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||
onClicked: settingsPopup.settingsVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Settings sections
|
||||
Flickable {
|
||||
id: settingsScrollView
|
||||
width: parent.width
|
||||
height: parent.height - 80
|
||||
clip: true
|
||||
contentHeight: settingsColumn.height
|
||||
boundsBehavior: Flickable.DragAndOvershootBounds
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
|
||||
property real wheelStepSize: 60
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y
|
||||
var steps = delta / 120
|
||||
settingsScrollView.contentY -= steps * settingsScrollView.wheelStepSize
|
||||
|
||||
// Keep within bounds
|
||||
if (settingsScrollView.contentY < 0)
|
||||
settingsScrollView.contentY = 0
|
||||
else if (settingsScrollView.contentY > settingsScrollView.contentHeight - settingsScrollView.height)
|
||||
settingsScrollView.contentY = Math.max(0, settingsScrollView.contentHeight - settingsScrollView.height)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: settingsColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Profile Settings
|
||||
SettingsSection {
|
||||
title: "Profile"
|
||||
iconName: "person"
|
||||
content: ProfileTab {}
|
||||
}
|
||||
|
||||
// Clock Settings
|
||||
SettingsSection {
|
||||
title: "Clock & Time"
|
||||
iconName: "schedule"
|
||||
content: ClockTab {}
|
||||
}
|
||||
|
||||
// Weather Settings
|
||||
SettingsSection {
|
||||
title: "Weather"
|
||||
iconName: "wb_sunny"
|
||||
content: WeatherTab {}
|
||||
}
|
||||
|
||||
// Widget Visibility Settings
|
||||
SettingsSection {
|
||||
title: "Top Bar Widgets"
|
||||
iconName: "widgets"
|
||||
content: WidgetsTab {}
|
||||
}
|
||||
|
||||
// Workspace Settings
|
||||
SettingsSection {
|
||||
title: "Workspaces"
|
||||
iconName: "tab"
|
||||
content: WorkspaceTab {}
|
||||
}
|
||||
|
||||
// Display Settings
|
||||
SettingsSection {
|
||||
title: "Display & Appearance"
|
||||
iconName: "palette"
|
||||
content: DisplayTab {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 8
|
||||
shadowBlur: 1
|
||||
shadowColor: Qt.rgba(0, 0, 0, 0.3)
|
||||
shadowOpacity: 0.3
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Keyboard focus and shortcuts
|
||||
FocusScope {
|
||||
anchors.fill: parent
|
||||
focus: settingsPopup.settingsVisible
|
||||
Keys.onEscapePressed: settingsPopup.settingsVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
51
Modules/Settings/SettingsSection.qml
Normal file
51
Modules/Settings/SettingsSection.qml
Normal file
@@ -0,0 +1,51 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property string title: ""
|
||||
property string iconName: ""
|
||||
property alias content: contentLoader.sourceComponent
|
||||
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Section header
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: iconName
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: title
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Divider
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||
}
|
||||
|
||||
// Content
|
||||
Loader {
|
||||
id: contentLoader
|
||||
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
}
|
||||
339
Modules/Settings/ThemePicker.qml
Normal file
339
Modules/Settings/ThemePicker.qml
Normal file
@@ -0,0 +1,339 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
id: themePicker
|
||||
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "Current Theme: " + (Theme.isDynamicTheme ? "Auto" : (Theme.currentThemeIndex < Theme.themes.length ? Theme.themes[Theme.currentThemeIndex].name : "Blue"))
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
// Theme description
|
||||
Text {
|
||||
text: {
|
||||
if (Theme.isDynamicTheme)
|
||||
return "Wallpaper-based dynamic colors";
|
||||
|
||||
var descriptions = ["Material blue inspired by modern interfaces", "Deep blue inspired by material 3", "Rich purple tones for BB elegance", "Natural green for productivity", "Energetic orange for creativity", "Bold red for impact", "Cool cyan for tranquility", "Vibrant pink for expression", "Warm amber for comfort", "Soft coral for gentle warmth"];
|
||||
return descriptions[Theme.currentThemeIndex] || "Select a theme";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(parent.width, 200)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
// Grid layout for 10 themes (2 rows of 5)
|
||||
Column {
|
||||
spacing: Theme.spacingS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// First row - Blue, Deep Blue, Purple, Green, Orange
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Repeater {
|
||||
model: 5
|
||||
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Theme.themes[index].primary
|
||||
border.color: Theme.outline
|
||||
border.width: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 2 : 1
|
||||
scale: (Theme.currentThemeIndex === index && !Theme.isDynamicTheme) ? 1.1 : 1
|
||||
|
||||
// Theme name tooltip
|
||||
Rectangle {
|
||||
width: nameText.contentWidth + Theme.spacingS * 2
|
||||
height: nameText.contentHeight + Theme.spacingXS * 2
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: Theme.cornerRadiusSmall
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: Theme.spacingXS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: mouseArea.containsMouse
|
||||
|
||||
Text {
|
||||
id: nameText
|
||||
|
||||
text: Theme.themes[index].name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Theme.switchTheme(index, false);
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Second row - Red, Cyan, Pink, Amber, Coral
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Repeater {
|
||||
model: 5
|
||||
|
||||
Rectangle {
|
||||
property int themeIndex: index + 5
|
||||
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: themeIndex < Theme.themes.length ? Theme.themes[themeIndex].primary : "transparent"
|
||||
border.color: Theme.outline
|
||||
border.width: Theme.currentThemeIndex === themeIndex ? 2 : 1
|
||||
visible: themeIndex < Theme.themes.length
|
||||
scale: Theme.currentThemeIndex === themeIndex ? 1.1 : 1
|
||||
|
||||
// Theme name tooltip
|
||||
Rectangle {
|
||||
width: nameText2.contentWidth + Theme.spacingS * 2
|
||||
height: nameText2.contentHeight + Theme.spacingXS * 2
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: Theme.cornerRadiusSmall
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: Theme.spacingXS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: mouseArea2.containsMouse && themeIndex < Theme.themes.length
|
||||
|
||||
Text {
|
||||
id: nameText2
|
||||
|
||||
text: themeIndex < Theme.themes.length ? Theme.themes[themeIndex].name : ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea2
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (themeIndex < Theme.themes.length)
|
||||
Theme.switchTheme(themeIndex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on border.width {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Spacer for better visual separation
|
||||
Item {
|
||||
width: 1
|
||||
height: Theme.spacingM
|
||||
}
|
||||
|
||||
// Auto theme button - prominent oval below the grid
|
||||
Rectangle {
|
||||
width: 120
|
||||
height: 40
|
||||
radius: 20
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: {
|
||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
||||
else
|
||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3);
|
||||
}
|
||||
border.color: {
|
||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.5);
|
||||
else if (Theme.isDynamicTheme)
|
||||
return Theme.primary;
|
||||
else
|
||||
return Theme.outline;
|
||||
}
|
||||
border.width: Theme.isDynamicTheme ? 2 : 1
|
||||
scale: Theme.isDynamicTheme ? 1.1 : (autoMouseArea.containsMouse ? 1.02 : 1)
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: {
|
||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return "error";
|
||||
else
|
||||
return "palette";
|
||||
}
|
||||
size: 16
|
||||
color: {
|
||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return Theme.error;
|
||||
else
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: {
|
||||
if (ToastService.wallpaperErrorStatus === "error")
|
||||
return "Error";
|
||||
else if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return "No matugen";
|
||||
else
|
||||
return "Auto";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: {
|
||||
if (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return Theme.error;
|
||||
else
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: autoMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Theme.switchTheme(10, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip for Auto button
|
||||
Rectangle {
|
||||
width: autoTooltipText.contentWidth + Theme.spacingM * 2
|
||||
height: autoTooltipText.contentHeight + Theme.spacingS * 2
|
||||
color: Theme.surfaceContainer
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: Theme.cornerRadiusSmall
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: autoMouseArea.containsMouse && (!Theme.isDynamicTheme || ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
|
||||
Text {
|
||||
id: autoTooltipText
|
||||
|
||||
text: {
|
||||
if (ToastService.wallpaperErrorStatus === "error")
|
||||
return "Wallpaper symlink missing at ~/quickshell/current_wallpaper";
|
||||
else if (ToastService.wallpaperErrorStatus === "matugen_missing")
|
||||
return "Install matugen package for dynamic themes";
|
||||
else
|
||||
return "Dynamic wallpaper-based colors";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: (ToastService.wallpaperErrorStatus === "error" || ToastService.wallpaperErrorStatus === "matugen_missing") ? Theme.error : Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(implicitWidth, 250)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
69
Modules/Settings/WeatherTab.qml
Normal file
69
Modules/Settings/WeatherTab.qml
Normal file
@@ -0,0 +1,69 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
text: "Fahrenheit"
|
||||
description: "Use Fahrenheit instead of Celsius for temperature"
|
||||
checked: Prefs.useFahrenheit
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setTemperatureUnit(checked);
|
||||
}
|
||||
}
|
||||
|
||||
// Weather Location Override
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
text: "Override Location"
|
||||
description: "Use a specific location instead of auto-detection"
|
||||
checked: Prefs.weatherLocationOverrideEnabled
|
||||
onToggled: (checked) => Prefs.setWeatherLocationOverrideEnabled(checked)
|
||||
}
|
||||
|
||||
// Location input - only visible when override is enabled
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: Prefs.weatherLocationOverrideEnabled
|
||||
opacity: visible ? 1.0 : 0.0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Location"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankLocationSearch {
|
||||
width: parent.width
|
||||
currentLocation: Prefs.weatherLocationOverride
|
||||
placeholderText: "Search for a location..."
|
||||
onLocationSelected: (displayName, coordinates) => {
|
||||
Prefs.setWeatherLocationOverride(coordinates)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Examples: \"New York\", \"Tokyo\", \"44511\""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Modules/Settings/WidgetsTab.qml
Normal file
62
Modules/Settings/WidgetsTab.qml
Normal file
@@ -0,0 +1,62 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
text: "Focused Window"
|
||||
description: "Show the currently focused application in the top bar"
|
||||
checked: Prefs.showFocusedWindow
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowFocusedWindow(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Weather Widget"
|
||||
description: "Display weather information in the top bar"
|
||||
checked: Prefs.showWeather
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowWeather(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Media Controls"
|
||||
description: "Show currently playing media in the top bar"
|
||||
checked: Prefs.showMusic
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowMusic(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Clipboard Button"
|
||||
description: "Show clipboard access button in the top bar"
|
||||
checked: Prefs.showClipboard
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowClipboard(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "System Resources"
|
||||
description: "Display CPU and RAM usage indicators"
|
||||
checked: Prefs.showSystemResources
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowSystemResources(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "System Tray"
|
||||
description: "Show system tray icons in the top bar"
|
||||
checked: Prefs.showSystemTray
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowSystemTray(checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Modules/Settings/WorkspaceTab.qml
Normal file
26
Modules/Settings/WorkspaceTab.qml
Normal file
@@ -0,0 +1,26 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankToggle {
|
||||
text: "Workspace Index Numbers"
|
||||
description: "Show workspace index numbers in the top bar workspace switcher"
|
||||
checked: Prefs.showWorkspaceIndex
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowWorkspaceIndex(checked);
|
||||
}
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
text: "Workspace Padding"
|
||||
description: "Always show a minimum of 3 workspaces, even if fewer are available"
|
||||
checked: Prefs.showWorkspacePadding
|
||||
onToggled: (checked) => {
|
||||
return Prefs.setShowWorkspacePadding(checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user