Cleaned up ColorSchemeTab, added program checks, added firefox template
Matugen: added firefox (pywalfox) template SidePanelToggle: use ProgramCheckerService for gpu-screen-recorder ColorSchemeTab: use NCollapsible for matugen templates, use ProgramCheckerService to detect available programs (for matugen templates) NCollapsible: create collapsible category
This commit is contained in:
parent
c0d6780c3d
commit
fa838ecdb1
8 changed files with 514 additions and 148 deletions
|
|
@ -70,6 +70,12 @@ Singleton {
|
|||
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/vesktop.css"')
|
||||
lines.push('output_path = "~/.config/vesktop/themes/noctalia.theme.css"')
|
||||
}
|
||||
if (Settings.data.matugen.pywalfox) {
|
||||
lines.push("\n[templates.pywalfox]")
|
||||
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/pywalfox.json"')
|
||||
lines.push('output_path = "~/.cache/wal/colors.json"')
|
||||
lines.push('post_hook = "pywalfox update"')
|
||||
}
|
||||
|
||||
return lines.join("\n") + "\n"
|
||||
}
|
||||
|
|
|
|||
22
Assets/Matugen/templates/pywalfox.json
Normal file
22
Assets/Matugen/templates/pywalfox.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"wallpaper": "{{image}}",
|
||||
"alpha": "100",
|
||||
"colors": {
|
||||
"color0": "{{colors.background.default.hex}}",
|
||||
"color1": "",
|
||||
"color2": "",
|
||||
"color3": "",
|
||||
"color4": "",
|
||||
"color5": "",
|
||||
"color6": "",
|
||||
"color7": "",
|
||||
"color8": "",
|
||||
"color9": "",
|
||||
"color10": "{{colors.primary.default.hex}}",
|
||||
"color11": "",
|
||||
"color12": "",
|
||||
"color13": "{{colors.surface_bright.default.hex}}",
|
||||
"color14": "",
|
||||
"color15": "{{colors.on_surface.default.hex}}"
|
||||
}
|
||||
}
|
||||
|
|
@ -449,6 +449,7 @@ Singleton {
|
|||
property bool foot: false
|
||||
property bool fuzzel: false
|
||||
property bool vesktop: false
|
||||
property bool pywalfox: false
|
||||
property bool enableUserTemplates: false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ NIconButton {
|
|||
colorFg: Color.mOnSurface
|
||||
colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary
|
||||
colorBorder: Color.transparent
|
||||
colorBorderHover: useDistroLogo ? Color.mTertiary : Color.transparent
|
||||
colorBorderHover: useDistroLogo ? Color.mTertiary : Color.transparent
|
||||
onClicked: PanelService.getPanel("sidePanel")?.toggle(this)
|
||||
onRightClicked: PanelService.getPanel("settingsPanel")?.toggle()
|
||||
|
||||
|
|
|
|||
|
|
@ -318,146 +318,187 @@ ColumnLayout {
|
|||
visible: Settings.data.colorSchemes.useWallpaperColors
|
||||
}
|
||||
|
||||
// Matugen template toggles (moved from MatugenTab)
|
||||
// Matugen template toggles organized by category
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
visible: Settings.data.colorSchemes.useWallpaperColors
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
// UI Components
|
||||
NCollapsible {
|
||||
Layout.fillWidth: true
|
||||
label: "UI"
|
||||
description: "Desktop environment and UI toolkit theming."
|
||||
defaultExpanded: false
|
||||
|
||||
NText {
|
||||
text: "Matugen Templates"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mSecondary
|
||||
NCheckbox {
|
||||
label: "GTK 4 (libadwaita)"
|
||||
description: "Write ~/.config/gtk-4.0/gtk.css"
|
||||
checked: Settings.data.matugen.gtk4
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.gtk4 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Select which external components Matugen should apply theming to."
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: Color.mOnSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
NCheckbox {
|
||||
label: "GTK 3"
|
||||
description: "Write ~/.config/gtk-3.0/gtk.css"
|
||||
checked: Settings.data.matugen.gtk3
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.gtk3 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Qt6ct"
|
||||
description: "Write ~/.config/qt6ct/colors/noctalia.conf"
|
||||
checked: Settings.data.matugen.qt6
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.qt6 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Qt5ct"
|
||||
description: "Write ~/.config/qt5ct/colors/noctalia.conf"
|
||||
checked: Settings.data.matugen.qt5
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.qt5 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "GTK 4 (libadwaita)"
|
||||
description: "Write ~/.config/gtk-4.0/gtk.css"
|
||||
checked: Settings.data.matugen.gtk4
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.gtk4 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "GTK 3"
|
||||
description: "Write ~/.config/gtk-3.0/gtk.css"
|
||||
checked: Settings.data.matugen.gtk3
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.gtk3 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Qt6ct"
|
||||
description: "Write ~/.config/qt6ct/colors/noctalia.conf"
|
||||
checked: Settings.data.matugen.qt6
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.qt6 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Qt5ct"
|
||||
description: "Write ~/.config/qt5ct/colors/noctalia.conf"
|
||||
checked: Settings.data.matugen.qt5
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.qt5 = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Kitty"
|
||||
description: "Write ~/.config/kitty/themes/noctalia.conf and reload"
|
||||
checked: Settings.data.matugen.kitty
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.kitty = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Ghostty"
|
||||
description: "Write ~/.config/ghostty/themes/noctalia and reload"
|
||||
checked: Settings.data.matugen.ghostty
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.ghostty = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Foot"
|
||||
description: "Write ~/.config/foot/themes/noctalia and reload"
|
||||
checked: Settings.data.matugen.foot
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.foot = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Fuzzel"
|
||||
description: "Write ~/.config/fuzzel/themes/noctalia and reload"
|
||||
checked: Settings.data.matugen.fuzzel
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.fuzzel = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Vesktop"
|
||||
description: "Write ~/.config/vesktop/themes/noctalia.theme.css"
|
||||
checked: Settings.data.matugen.vesktop
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.vesktop = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
// Terminal Emulators
|
||||
NCollapsible {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
Layout.bottomMargin: Style.marginM * scaling
|
||||
label: "Terminal"
|
||||
description: "Terminal emulator theming."
|
||||
defaultExpanded: false
|
||||
|
||||
NCheckbox {
|
||||
label: "Kitty"
|
||||
description: ProgramCheckerService.kittyAvailable ? "Write ~/.config/kitty/themes/noctalia.conf and reload" : "Requires kitty terminal to be installed"
|
||||
checked: Settings.data.matugen.kitty
|
||||
enabled: ProgramCheckerService.kittyAvailable
|
||||
opacity: ProgramCheckerService.kittyAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.kittyAvailable) {
|
||||
Settings.data.matugen.kitty = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Ghostty"
|
||||
description: ProgramCheckerService.ghosttyAvailable ? "Write ~/.config/ghostty/themes/noctalia and reload" : "Requires ghostty terminal to be installed"
|
||||
checked: Settings.data.matugen.ghostty
|
||||
enabled: ProgramCheckerService.ghosttyAvailable
|
||||
opacity: ProgramCheckerService.ghosttyAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.ghosttyAvailable) {
|
||||
Settings.data.matugen.ghostty = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Foot"
|
||||
description: ProgramCheckerService.footAvailable ? "Write ~/.config/foot/themes/noctalia and reload" : "Requires foot terminal to be installed"
|
||||
checked: Settings.data.matugen.foot
|
||||
enabled: ProgramCheckerService.footAvailable
|
||||
opacity: ProgramCheckerService.footAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.footAvailable) {
|
||||
Settings.data.matugen.foot = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "User Templates"
|
||||
description: "Enable user-defined Matugen config from ~/.config/matugen/config.toml"
|
||||
checked: Settings.data.matugen.enableUserTemplates
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.enableUserTemplates = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
// Applications
|
||||
NCollapsible {
|
||||
Layout.fillWidth: true
|
||||
label: "Programs"
|
||||
description: "Application-specific theming."
|
||||
defaultExpanded: false
|
||||
|
||||
NCheckbox {
|
||||
label: "Fuzzel"
|
||||
description: ProgramCheckerService.fuzzelAvailable ? "Write ~/.config/fuzzel/themes/noctalia and reload" : "Requires fuzzel launcher to be installed"
|
||||
checked: Settings.data.matugen.fuzzel
|
||||
enabled: ProgramCheckerService.fuzzelAvailable
|
||||
opacity: ProgramCheckerService.fuzzelAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.fuzzelAvailable) {
|
||||
Settings.data.matugen.fuzzel = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Vesktop"
|
||||
description: ProgramCheckerService.vesktopAvailable ? "Write ~/.config/vesktop/themes/noctalia.theme.css" : "Requires vesktop Discord client to be installed"
|
||||
checked: Settings.data.matugen.vesktop
|
||||
enabled: ProgramCheckerService.vesktopAvailable
|
||||
opacity: ProgramCheckerService.vesktopAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.vesktopAvailable) {
|
||||
Settings.data.matugen.vesktop = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
label: "Pywalfox (Firefox)"
|
||||
description: ProgramCheckerService.pywalfoxAvailable ? "Write ~/.cache/wal/colors.json and run pywalfox update" : "Requires pywalfox package to be installed"
|
||||
checked: Settings.data.matugen.pywalfox
|
||||
enabled: ProgramCheckerService.pywalfoxAvailable
|
||||
opacity: ProgramCheckerService.pywalfoxAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.pywalfoxAvailable) {
|
||||
Settings.data.matugen.pywalfox = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
NCollapsible {
|
||||
Layout.fillWidth: true
|
||||
label: "Misc"
|
||||
description: "Additional configuration options."
|
||||
defaultExpanded: false
|
||||
|
||||
NCheckbox {
|
||||
label: "User Templates"
|
||||
description: "Enable user-defined Matugen config from ~/.config/matugen/config.toml"
|
||||
checked: Settings.data.matugen.enableUserTemplates
|
||||
onToggled: checked => {
|
||||
Settings.data.matugen.enableUserTemplates = checked
|
||||
if (Settings.data.colorSchemes.useWallpaperColors)
|
||||
MatugenService.generateFromWallpaper()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
118
Services/ProgramCheckerService.qml
Normal file
118
Services/ProgramCheckerService.qml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
|
||||
// Service to check if various programs are available on the system
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Program availability properties
|
||||
property bool matugenAvailable: false
|
||||
property bool pywalfoxAvailable: false
|
||||
property bool kittyAvailable: false
|
||||
property bool ghosttyAvailable: false
|
||||
property bool footAvailable: false
|
||||
property bool fuzzelAvailable: false
|
||||
property bool vesktopAvailable: false
|
||||
property bool gpuScreenRecorderAvailable: false
|
||||
|
||||
// Signal emitted when all checks are complete
|
||||
signal checksCompleted
|
||||
|
||||
// Programs to check - maps property names to commands
|
||||
readonly property var programsToCheck: ({
|
||||
"matugenAvailable": ["which", "matugen"],
|
||||
"pywalfoxAvailable": ["which", "pywalfox"],
|
||||
"kittyAvailable": ["which", "kitty"],
|
||||
"ghosttyAvailable": ["which", "ghostty"],
|
||||
"footAvailable": ["which", "foot"],
|
||||
"fuzzelAvailable": ["which", "fuzzel"],
|
||||
"vesktopAvailable": ["which", "vesktop"],
|
||||
"gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"]
|
||||
})
|
||||
|
||||
// Internal tracking
|
||||
property int completedChecks: 0
|
||||
property int totalChecks: Object.keys(programsToCheck).length
|
||||
|
||||
// Single reusable Process object
|
||||
Process {
|
||||
id: checker
|
||||
running: false
|
||||
|
||||
property string currentProperty: ""
|
||||
|
||||
onExited: function (exitCode) {
|
||||
// Set the availability property
|
||||
root[currentProperty] = (exitCode === 0)
|
||||
|
||||
// Stop the process to free resources
|
||||
running = false
|
||||
|
||||
// Track completion
|
||||
root.completedChecks++
|
||||
|
||||
// Check next program or emit completion signal
|
||||
if (root.completedChecks >= root.totalChecks) {
|
||||
root.checksCompleted()
|
||||
} else {
|
||||
root.checkNextProgram()
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
// Queue of programs to check
|
||||
property var checkQueue: []
|
||||
property int currentCheckIndex: 0
|
||||
|
||||
// Function to check the next program in the queue
|
||||
function checkNextProgram() {
|
||||
if (currentCheckIndex >= checkQueue.length)
|
||||
return
|
||||
|
||||
var propertyName = checkQueue[currentCheckIndex]
|
||||
var command = programsToCheck[propertyName]
|
||||
|
||||
checker.currentProperty = propertyName
|
||||
checker.command = command
|
||||
checker.running = true
|
||||
|
||||
currentCheckIndex++
|
||||
}
|
||||
|
||||
// Function to run all program checks
|
||||
function checkAllPrograms() {
|
||||
// Reset state
|
||||
completedChecks = 0
|
||||
currentCheckIndex = 0
|
||||
checkQueue = Object.keys(programsToCheck)
|
||||
|
||||
// Start first check
|
||||
if (checkQueue.length > 0) {
|
||||
checkNextProgram()
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check a specific program
|
||||
function checkProgram(programProperty) {
|
||||
if (!programsToCheck.hasOwnProperty(programProperty)) {
|
||||
Logger.warn("ProgramChecker", "Unknown program property:", programProperty)
|
||||
return
|
||||
}
|
||||
|
||||
checker.currentProperty = programProperty
|
||||
checker.command = programsToCheck[programProperty]
|
||||
checker.running = true
|
||||
}
|
||||
|
||||
// Initialize checks when service is created
|
||||
Component.onCompleted: {
|
||||
checkAllPrograms()
|
||||
}
|
||||
}
|
||||
|
|
@ -13,16 +13,13 @@ Singleton {
|
|||
property bool isRecording: false
|
||||
property bool isPending: false
|
||||
property string outputPath: ""
|
||||
property bool isAvailable: false
|
||||
property bool isAvailable: ProgramCheckerService.gpuScreenRecorderAvailable
|
||||
|
||||
Component.onCompleted: {
|
||||
checkAvailability()
|
||||
}
|
||||
|
||||
function checkAvailability() {
|
||||
// Detect native or Flatpak gpu-screen-recorder
|
||||
availabilityCheckProcess.command = ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"]
|
||||
availabilityCheckProcess.running = true
|
||||
// Update availability when ProgramCheckerService completes its checks
|
||||
Connections {
|
||||
target: ProgramCheckerService
|
||||
function onChecksCompleted() {// Availability is now automatically updated via property binding
|
||||
}
|
||||
}
|
||||
|
||||
// Start or Stop recording
|
||||
|
|
@ -101,18 +98,6 @@ Singleton {
|
|||
}
|
||||
}
|
||||
|
||||
// Availability check process
|
||||
Process {
|
||||
id: availabilityCheckProcess
|
||||
command: ["sh", "-c", "true"]
|
||||
onExited: function (exitCode, exitStatus) {
|
||||
// exitCode 0 means available, non-zero means unavailable
|
||||
root.isAvailable = (exitCode === 0)
|
||||
}
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: pendingTimer
|
||||
interval: 2000 // Wait 2 seconds to see if process stays alive
|
||||
|
|
|
|||
193
Widgets/NCollapsible.qml
Normal file
193
Widgets/NCollapsible.qml
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property string label: ""
|
||||
property string description: ""
|
||||
property bool expanded: false
|
||||
property bool defaultExpanded: false
|
||||
property real contentSpacing: Style.marginM * scaling
|
||||
signal toggled(bool expanded)
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
// Default property to accept children
|
||||
default property alias content: contentLayout.children
|
||||
|
||||
// Header with clickable area
|
||||
Rectangle {
|
||||
id: headerContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: headerContent.implicitHeight + (Style.marginL * scaling * 2)
|
||||
|
||||
// Material 3 style background
|
||||
color: root.expanded ? Color.mSecondary : Color.mSurfaceVariant
|
||||
radius: Style.radiusL * scaling
|
||||
|
||||
// Subtle border
|
||||
border.color: root.expanded ? Color.mOnSecondary : Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Smooth color transitions
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
root.expanded = !root.expanded
|
||||
root.toggled(root.expanded)
|
||||
}
|
||||
|
||||
// Hover effect overlay
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: headerArea.containsMouse ? Color.mOnSurface : Color.transparent
|
||||
opacity: headerArea.containsMouse ? 0.08 : 0
|
||||
radius: headerContainer.radius // Reference the container's radius directly
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: headerContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Expand/collapse icon with rotation animation
|
||||
NIcon {
|
||||
id: chevronIcon
|
||||
icon: "chevron-right"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
rotation: root.expanded ? 90 : 0
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Header text content - properly contained
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Style.marginXXS * scaling
|
||||
|
||||
NText {
|
||||
text: root.label
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.weight: Style.fontWeightSemiBold
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: root.description
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
font.weight: Style.fontWeightRegular
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
visible: root.description !== ""
|
||||
opacity: 0.87
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collapsible content with Material 3 styling
|
||||
Rectangle {
|
||||
id: contentContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginS * scaling
|
||||
|
||||
visible: root.expanded
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusL * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Dynamic height based on content
|
||||
Layout.preferredHeight: visible ? contentLayout.implicitHeight + (Style.marginL * scaling * 2) : 0
|
||||
|
||||
// Smooth height animation
|
||||
Behavior on Layout.preferredHeight {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
// Content layout
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: root.contentSpacing
|
||||
|
||||
// Clip content during animation
|
||||
clip: true
|
||||
}
|
||||
|
||||
// Fade in animation for content
|
||||
opacity: root.expanded ? 1.0 : 0.0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize expanded state
|
||||
Component.onCompleted: {
|
||||
root.expanded = root.defaultExpanded
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue