This commit is contained in:
Ly-sec
2025-09-02 17:03:36 +02:00
19 changed files with 154 additions and 159 deletions

View File

@@ -63,6 +63,7 @@ Singleton {
Logger.log("Settings", "Bar monitor list is empty, will show on all available screens")
}
}
Item {
Component.onCompleted: {
@@ -100,8 +101,13 @@ Singleton {
// Emit the signal
root.settingsLoaded()
// Kickoff ColorScheme service
ColorSchemeService.init()
// Kickoff Matugen service
MatugenService.init()
Qt.callLater(function () {
// Some stuff like settings validation should just be executed once on startup and not on every reload
validateMonitorConfigurations()
})
}

View File

@@ -0,0 +1,23 @@
import Quickshell
import qs.Commons
import qs.Widgets
import qs.Services
NIconButton {
id: root
property ShellScreen screen
property real scaling: 1.0
icon: "contrast"
tooltipText: "Toggle light/dark mode"
sizeRatio: 0.8
colorBg: Color.mSurfaceVariant
colorFg: Color.mOnSurface
colorBorder: Color.transparent
colorBorderHover: Color.transparent
anchors.verticalCenter: parent.verticalCenter
onClicked: Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode
}

View File

@@ -100,6 +100,19 @@ Item {
}
}
IpcHandler {
target: "darkMode"
function toggle() {
Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode
}
function setDark() {
Settings.data.colorSchemes.darkMode = true
}
function setLight() {
Settings.data.colorSchemes.darkMode = false
}
}
IpcHandler {
target: "volume"
function increase() {

View File

@@ -123,9 +123,7 @@ ColumnLayout {
label: "Mute Audio Input"
description: "Mute or unmute the default audio input (microphone)."
checked: AudioService.inputMuted
onToggled: checked => {
AudioService.setInputMuted(checked)
}
onToggled: checked => AudioService.setInputMuted(checked)
}
}
@@ -249,18 +247,14 @@ ColumnLayout {
label: "Show Album Art In Bar Media Player"
description: "Show the album art of the currently playing song next to the title."
checked: Settings.data.audio.showMiniplayerAlbumArt
onToggled: checked => {
Settings.data.audio.showMiniplayerAlbumArt = checked
}
onToggled: checked => Settings.data.audio.showMiniplayerAlbumArt = checked
}
NToggle {
label: "Show Audio Visualizer In Bar Media Player"
description: "Shows an audio visualizer in the background of the miniplayer."
checked: Settings.data.audio.showMiniplayerCava
onToggled: checked => {
Settings.data.audio.showMiniplayerCava = checked
}
onToggled: checked => Settings.data.audio.showMiniplayerCava = checked
}
// Preferred player (persistent)
NTextInput {
@@ -412,9 +406,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.audio.visualizerType
onSelected: key => {
Settings.data.audio.visualizerType = key
}
onSelected: key => Settings.data.audio.visualizerType = key
}
NComboBox {
@@ -451,9 +443,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.audio.cavaFrameRate
onSelected: key => {
Settings.data.audio.cavaFrameRate = key
}
onSelected: key => Settings.data.audio.cavaFrameRate = key
}
}
// Divider

View File

@@ -43,7 +43,7 @@ ColumnLayout {
}
NText {
text: "Adjust the background opacity of the bar"
text: "Adjust the background opacity of the bar."
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
@@ -74,27 +74,21 @@ ColumnLayout {
label: "Show Active Window's Icon"
description: "Display the app icon next to the title of the currently focused window."
checked: Settings.data.bar.showActiveWindowIcon
onToggled: checked => {
Settings.data.bar.showActiveWindowIcon = checked
}
onToggled: checked => Settings.data.bar.showActiveWindowIcon = checked
}
NToggle {
label: "Show Battery Percentage"
description: "Display battery percentage at all times."
checked: Settings.data.bar.alwaysShowBatteryPercentage
onToggled: checked => {
Settings.data.bar.alwaysShowBatteryPercentage = checked
}
onToggled: checked => Settings.data.bar.alwaysShowBatteryPercentage = checked
}
NToggle {
label: "Show Network Statistics"
description: "Display network upload and download speeds in the system monitor."
checked: Settings.data.bar.showNetworkStats
onToggled: checked => {
Settings.data.bar.showNetworkStats = checked
}
onToggled: checked => Settings.data.bar.showNetworkStats = checked
}
NToggle {
@@ -108,7 +102,7 @@ ColumnLayout {
NComboBox {
label: "Show Workspaces Labels"
description: "Display the workspace name or index in the workspace indicator"
description: "Show the workspace name or index within the workspace indicator."
model: ListModel {
ListElement {
key: "none"
@@ -124,9 +118,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.bar.showWorkspaceLabel
onSelected: key => {
Settings.data.bar.showWorkspaceLabel = key
}
onSelected: key => Settings.data.bar.showWorkspaceLabel = key
}
}
@@ -151,7 +143,7 @@ ColumnLayout {
NText {
text: "Drag and drop widgets to reorder them within each section, or use the add/remove buttons to manage widgets."
font.pointSize: Style.fontSizeXS * scaling
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true

View File

@@ -298,9 +298,8 @@ ColumnLayout {
model: timeOptions
currentKey: Settings.data.nightLight.manualSunrise
placeholder: "Select start time"
onSelected: key => {
Settings.data.nightLight.manualSunrise = key
}
onSelected: key => Settings.data.nightLight.manualSunrise = key
preferredWidth: 120 * scaling
}
@@ -316,9 +315,8 @@ ColumnLayout {
model: timeOptions
currentKey: Settings.data.nightLight.manualSunset
placeholder: "Select stop time"
onSelected: key => {
Settings.data.nightLight.manualSunset = key
}
onSelected: key => Settings.data.nightLight.manualSunset = key
preferredWidth: 120 * scaling
}
}

View File

@@ -63,7 +63,6 @@ ColumnLayout {
if (exitCode === 0) {
// Matugen exists, enable it
Settings.data.colorSchemes.useWallpaperColors = true
Settings.data.colorSchemes.predefinedScheme = ""
MatugenService.generateFromWallpaper()
ToastService.showNotice("Matugen", "Enabled")
} else {
@@ -122,19 +121,7 @@ ColumnLayout {
description: Settings.data.colorSchemes.useWallpaperColors ? "Generate dark theme colors when using Matugen." : "Use a dark variant if available."
checked: Settings.data.colorSchemes.darkMode
enabled: true
onToggled: checked => {
Settings.data.colorSchemes.darkMode = checked
if (Settings.data.colorSchemes.useWallpaperColors) {
MatugenService.generateFromWallpaper()
} else if (Settings.data.colorSchemes.predefinedScheme) {
// Re-apply current scheme to pick the right variant
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
// Force refresh of previews
var tmp = schemeColorsCache
schemeColorsCache = {}
schemeColorsCache = tmp
}
}
onToggled: checked => Settings.data.colorSchemes.darkMode = checked
}
// Use Matugen
@@ -149,6 +136,11 @@ ColumnLayout {
} else {
Settings.data.colorSchemes.useWallpaperColors = false
ToastService.showNotice("Matugen", "Disabled")
if (Settings.data.colorSchemes.predefinedScheme) {
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
}
}
}
}
@@ -199,7 +191,8 @@ ColumnLayout {
radius: Style.radiusM * scaling
color: getSchemeColor(modelData, "mSurface")
border.width: Math.max(1, Style.borderL * scaling)
border.color: Settings.data.colorSchemes.predefinedScheme === modelData ? Color.mPrimary : Color.mOutline
border.color: (!Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === modelData)) ? Color.mPrimary : Color.mOutline
scale: root.cardScaleLow
// Mouse area for selection
@@ -290,9 +283,10 @@ ColumnLayout {
}
}
// Selection indicator
// Selection indicator (Checkmark)
Rectangle {
visible: Settings.data.colorSchemes.predefinedScheme === schemePath
visible: !Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === schemePath)
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Style.marginS * scaling

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services
import qs.Widgets
@@ -15,8 +16,8 @@ ColumnLayout {
// Avatar preview
NImageCircled {
width: 128 * scaling
height: 128 * scaling
width: 108 * scaling
height: 108 * scaling
imagePath: Settings.data.general.avatarImage
fallbackIcon: "person"
borderColor: Color.mPrimary
@@ -24,7 +25,7 @@ ColumnLayout {
}
NTextInput {
label: "Profile Picture"
label: `${Quickshell.env("USER") || "user"}'s profile picture`
description: "Your profile picture that appears throughout the interface."
text: Settings.data.general.avatarImage
placeholderText: "/home/user/.face"
@@ -57,27 +58,21 @@ ColumnLayout {
label: "Show Corners"
description: "Display rounded corners on the edge of the screen."
checked: Settings.data.general.showScreenCorners
onToggled: checked => {
Settings.data.general.showScreenCorners = checked
}
onToggled: checked => Settings.data.general.showScreenCorners = checked
}
NToggle {
label: "Dim Desktop"
description: "Dim the desktop when panels or menus are open."
checked: Settings.data.general.dimDesktop
onToggled: checked => {
Settings.data.general.dimDesktop = checked
}
onToggled: checked => Settings.data.general.dimDesktop = checked
}
NToggle {
label: "Auto-hide Dock"
description: "Automatically hide the dock when not in use."
checked: Settings.data.dock.autoHide
onToggled: checked => {
Settings.data.dock.autoHide = checked
}
onToggled: checked => Settings.data.dock.autoHide = checked
}
ColumnLayout {

View File

@@ -56,9 +56,7 @@ ColumnLayout {
label: "Enable Clipboard History"
description: "Show clipboard history in the launcher."
checked: Settings.data.appLauncher.enableClipboardHistory
onToggled: checked => {
Settings.data.appLauncher.enableClipboardHistory = checked
}
onToggled: checked => Settings.data.appLauncher.enableClipboardHistory = checked
}
ColumnLayout {

View File

@@ -37,9 +37,7 @@ ColumnLayout {
label: "Show Cursor"
description: "Record mouse cursor in the video."
checked: Settings.data.screenRecorder.showCursor
onToggled: checked => {
Settings.data.screenRecorder.showCursor = checked
}
onToggled: checked => Settings.data.screenRecorder.showCursor = checked
}
}
}
@@ -66,7 +64,7 @@ ColumnLayout {
// Source
NComboBox {
label: "Video Source"
description: "We recommend using portal, if you get artifacts try screen."
description: "Portal is recommend, if you get artifacts try Screen."
model: ListModel {
ListElement {
key: "portal"
@@ -78,9 +76,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.videoSource
onSelected: key => {
Settings.data.screenRecorder.videoSource = key
}
onSelected: key => Settings.data.screenRecorder.videoSource = key
}
// Frame Rate
@@ -118,9 +114,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.frameRate
onSelected: key => {
Settings.data.screenRecorder.frameRate = key
}
onSelected: key => Settings.data.screenRecorder.frameRate = key
}
// Video Quality
@@ -146,15 +140,13 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.quality
onSelected: key => {
Settings.data.screenRecorder.quality = key
}
onSelected: key => Settings.data.screenRecorder.quality = key
}
// Video Codec
NComboBox {
label: "Video Codec"
description: "Different codecs offer different compression and compatibility."
description: "h264 is the most common codec."
model: ListModel {
ListElement {
key: "h264"
@@ -178,9 +170,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.videoCodec
onSelected: key => {
Settings.data.screenRecorder.videoCodec = key
}
onSelected: key => Settings.data.screenRecorder.videoCodec = key
}
// Color Range
@@ -198,9 +188,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.colorRange
onSelected: key => {
Settings.data.screenRecorder.colorRange = key
}
onSelected: key => Settings.data.screenRecorder.colorRange = key
}
}
@@ -242,9 +230,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.audioSource
onSelected: key => {
Settings.data.screenRecorder.audioSource = key
}
onSelected: key => Settings.data.screenRecorder.audioSource = key
}
// Audio Codec
@@ -262,9 +248,7 @@ ColumnLayout {
}
}
currentKey: Settings.data.screenRecorder.audioCodec
onSelected: key => {
Settings.data.screenRecorder.audioCodec = key
}
onSelected: key => Settings.data.screenRecorder.audioCodec = key
}
}

View File

@@ -63,27 +63,21 @@ ColumnLayout {
label: "Use 12-Hour Clock"
description: "Display time in 12-hour format (AM/PM) instead of 24-hour."
checked: Settings.data.location.use12HourClock
onToggled: checked => {
Settings.data.location.use12HourClock = checked
}
onToggled: checked => Settings.data.location.use12HourClock = checked
}
NToggle {
label: "Reverse Day/Month"
description: "Display date as DD/MM instead of MM/DD."
description: "Display date as dd/mm instead of mm/dd."
checked: Settings.data.location.reverseDayMonth
onToggled: checked => {
Settings.data.location.reverseDayMonth = checked
}
onToggled: checked => Settings.data.location.reverseDayMonth = checked
}
NToggle {
label: "Show Date with Clock"
description: "Display date alongside time (e.g., 18:12 - Sat, 23 Aug)."
checked: Settings.data.location.showDateWithClock
onToggled: checked => {
Settings.data.location.showDateWithClock = checked
}
onToggled: checked => Settings.data.location.showDateWithClock = checked
}
}
@@ -109,9 +103,7 @@ ColumnLayout {
label: "Use Fahrenheit"
description: "Display temperature in Fahrenheit instead of Celsius."
checked: Settings.data.location.useFahrenheit
onToggled: checked => {
Settings.data.location.useFahrenheit = checked
}
onToggled: checked => Settings.data.location.useFahrenheit = checked
}
}

View File

@@ -43,9 +43,10 @@ NBox {
NText {
text: Quickshell.env("USER") || "user"
font.weight: Style.fontWeightBold
font.capitalization: Font.Capitalize
}
NText {
text: `System Uptime: ${uptimeText}`
text: `System uptime: ${uptimeText}`
color: Color.mOnSurface
}
}

View File

@@ -29,19 +29,19 @@ NBox {
NIcon {
text: weatherReady ? LocationService.weatherSymbolFromCode(
LocationService.data.weather.current_weather.weathercode) : ""
font.pointSize: Style.fontSizeXXXL * 1.5 * scaling
font.pointSize: Style.fontSizeXXXL * 1.75 * scaling
color: Color.mPrimary
}
ColumnLayout {
spacing: -Style.marginXS * scaling
spacing: Style.marginXXS * scaling
NText {
text: {
// Ensure the name is not too long if one had to specify the country
const chunks = Settings.data.location.name.split(",")
return chunks[0]
}
font.pointSize: Style.fontSizeXL * scaling
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
}
@@ -61,13 +61,14 @@ NBox {
temp = Math.round(temp)
return `${temp}°${suffix}`
}
font.pointSize: Style.fontSizeXXL * scaling
font.pointSize: Style.fontSizeXL * scaling
font.weight: Style.fontWeightBold
}
NText {
text: weatherReady ? `(${LocationService.data.weather.timezone_abbreviation})` : ""
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
visible: LocationService.data.weather
}
}

View File

@@ -191,39 +191,9 @@ Alternatively, you can add it to your NixOS configuration or flake:
### Usage
<details>
<summary> Nix </summary>
The following commands apply to the Nix flake installation.
Noctalia-shell offers many IPC calls for you convenience, so you can add them to your favorite keybinds or scripts.
| Action | Command |
| --------------------------- | ----------------------------------------------------- |
| Start the Shell | `noctalia-shell` |
| Toggle Application Launcher | `noctalia-shell ipc call launcher toggle` |
| Toggle Side Panel | `noctalia-shell ipc call sidePanel toggle` |
| Open Clipboard History | `noctalia-shell ipc call launcher clipboard` |
| Open Calculator | `noctalia-shell ipc call launcher calculator` |
| Increase Brightness | `noctalia-shell ipc call brightness increase` |
| Decrease Brightness | `noctalia-shell ipc call brightness decrease` |
| Increase Output Volume | `noctalia-shell ipc call volume increase` |
| Decrease Output Volume | `noctalia-shell ipc call volume decrease` |
| Toggle Mute Audio Output | `noctalia-shell ipc call volume muteOutput` |
| Toggle Mute Audio Input | `noctalia-shell ipc call volume muteInput` |
| Toggle Power Panel | `noctalia-shell ipc call powerPanel toggle` |
| Toggle Idle Inhibitor | `noctalia-shell ipc call idleInhibitor toggle` |
| Toggle Settings Window | `noctalia-shell ipc call settings toggle` |
| Toggle Lock Screen | `noctalia-shell ipc call lockScreen toggle` |
| Toggle Notification History | `noctalia-shell ipc call notifications toggleHistory` |
| Select new random wallpaper | `noctalia-shell ipc call wallpaper random` |
</details>
<details>
<summary> AUR/Manual install </summary>
The following commands apply to both AUR package and manual installation.
| Action | Command |
| Action | Command* |
| --------------------------- | ----------------------------------------------------------- |
| Start the Shell | `qs -c noctalia-shell` |
| Toggle Application Launcher | `qs -c noctalia-shell ipc call launcher toggle` |
@@ -242,9 +212,11 @@ The following commands apply to both AUR package and manual installation.
| Toggle Lock Screen | `qs -c noctalia-shell ipc call lockScreen toggle` |
| Toggle Notification History | `qs -c noctalia-shell ipc call notifications toggleHistory` |
| Select new random wallpaper | `qs -c noctalia-shell ipc call wallpaper random` |
| Toggle Dark Mode | `qs -c noctalia-shell ipc call darkMode toggle` |
| Set Dark Mode | `qs -c noctalia-shell ipc call darkMode setDark` |
| Set Light Mode | `qs -c noctalia-shell ipc call darkMode setLight` |
</details>
*If using the Flake installation on NixOS, replace `qs -c noctalia-shell` by `noctalia-shell`*
### Configuration

View File

@@ -16,6 +16,7 @@ Singleton {
"Bluetooth": bluetoothComponent,
"Brightness": brightnessComponent,
"Clock": clockComponent,
"DarkModeToggle": darkModeToggle,
"KeyboardLayout": keyboardLayoutComponent,
"MediaMini": mediaMiniComponent,
"Microphone": microphoneComponent,
@@ -51,6 +52,9 @@ Singleton {
property Component clockComponent: Component {
Clock {}
}
property Component darkModeToggle: Component {
DarkModeToggle {}
}
property Component keyboardLayoutComponent: Component {
KeyboardLayout {}
}

View File

@@ -10,16 +10,30 @@ import qs.Services
Singleton {
id: root
Component.onCompleted: {
Logger.log("ColorScheme", "Service started")
loadColorSchemes()
}
property var schemes: []
property bool scanning: false
property string schemesDirectory: Quickshell.shellDir + "/Assets/ColorScheme"
property string colorsJsonFilePath: Settings.configDir + "colors.json"
Connections {
target: Settings.data.colorSchemes
function onDarkModeChanged() {
Logger.log("ColorScheme", "Detected dark mode change")
if (!Settings.data.colorSchemes.useWallpaperColors && Settings.data.colorSchemes.predefinedScheme) {
// Re-apply current scheme to pick the right variant
applyScheme(Settings.data.colorSchemes.predefinedScheme)
}
}
}
// --------------------------------
function init() {
// does nothing but ensure the singleton is created
// do not remove
Logger.log("ColorScheme", "Service started")
loadColorSchemes()
}
function loadColorSchemes() {
Logger.log("ColorScheme", "Load ColorScheme")
scanning = true

View File

@@ -10,7 +10,7 @@ import qs.Services
Singleton {
id: root
property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"
property string dynamicConfigPath: Settings.isLoaded ? Settings.cacheDir + "matugen.dynamic.toml" : ""
// External state management
Connections {
@@ -23,6 +23,23 @@ Singleton {
}
}
Connections {
target: Settings.data.colorSchemes
function onDarkModeChanged() {
Logger.log("Matugen", "Detected dark mode change")
if (Settings.data.colorSchemes.useWallpaperColors) {
MatugenService.generateFromWallpaper()
}
}
}
// --------------------------------
function init() {
// does nothing but ensure the singleton is created
// do not remove
Logger.log("Matugen", "Service started")
}
// Build TOML content based on settings
function buildConfigToml() {
return Matugen.buildConfigToml()
@@ -30,11 +47,12 @@ Singleton {
// Generate colors using current wallpaper and settings
function generateFromWallpaper() {
// Ensure cache dir exists
Quickshell.execDetached(["mkdir", "-p", Settings.cacheDir])
Logger.log("Matugen", "Generating from wallpaper on screen:", Screen.name)
var wp = WallpaperService.getWallpaper(Screen.name).replace(/'/g, "'\\''")
if (wp === "") {
Logger.error("Matugen", "No wallpaper was found")
return
}
var content = buildConfigToml()
var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"

View File

@@ -17,6 +17,7 @@ ColumnLayout {
text: label
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
font.capitalization: Font.Capitalize
color: labelColor
visible: label !== ""
}

View File

@@ -75,9 +75,8 @@ NBox {
label: ""
description: ""
placeholder: "Select a widget to add..."
onSelected: key => {
comboBox.currentKey = key
}
onSelected: key => comboBox.currentKey = key
Layout.alignment: Qt.AlignVCenter
}