Compare commits

...

56 Commits

Author SHA1 Message Date
Ly-sec
899595ec5c Release v2.9.2 2025-09-15 14:52:09 +02:00
Ly-sec
dbf1020636 CustomButton: add script execution/polling support with text display 2025-09-15 14:37:29 +02:00
LemmyCook
7def695c0e AboutTab: Fix hover on contributos 2025-09-15 08:36:18 -04:00
LemmyCook
b51a87a981 NSlider: slightly more discrete bg track 2025-09-15 08:36:08 -04:00
LemmyCook
3da2682111 Settings: fix about tab alignment of download button 2025-09-15 07:58:33 -04:00
Ly-sec
758f2f2e55 SystemMonitor: fix network stats, move text above storage icon (vertical bar) 2025-09-15 13:43:58 +02:00
Lysec
26a27c3393 Merge pull request #282 from Mtendekuyokwa19/everforest
Everforest
2025-09-15 09:39:42 +02:00
Mtende Kuyokwa
9d9bfb54e1 everforest light 2025-09-15 09:30:26 +02:00
Mtende Kuyokwa
ab5b1e4d82 everforest dark complete 2025-09-15 08:40:11 +02:00
Ly-sec
8cb9f04a22 ScreenCorners: add solid black option 2025-09-15 08:28:57 +02:00
Ly-sec
22bc5a3bff Move ScreenCorners to the actualy screen edges when bar is floating,
edit vesktop template
ScreenCorners: move to screen corners on floating bar
BarTab: mention the ScreenCorner changes
vesktop: make read channels have less visible text
2025-09-15 08:08:32 +02:00
Ly-sec
f5982f41a2 Remove noctalia.svg 2025-09-15 07:59:39 +02:00
Ly-sec
9a80d51b10 Vesktop: replace old theme with midnight from refact0r for better looks 2025-09-15 07:59:06 +02:00
Ly-sec
fa838ecdb1 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
2025-09-15 07:44:31 +02:00
ItsLemmy
c0d6780c3d Volume/Brightness/Microphone: fixed tooltips to new mapping 2025-09-14 23:45:17 -04:00
ItsLemmy
8935f9a0f9 NPill: fix broken mouse-wheel control 2025-09-14 23:42:21 -04:00
LemmyCook
b8b97c46a0 DistroLogo: respect original colors, and avoid changing bg color when hovering to compensate. 2025-09-14 23:07:00 -04:00
Lemmy
f2bbf70f93 Merge pull request #279 from MrDowntempo/better-cats
Updated Catppuccin to use more of the official colors.
2025-09-14 22:54:44 -04:00
Corey Woodworth
ecf468f78f Updated Catppuccin to use more of the official colors. Also changed Green to Teal in Mocha and a few minor tweaks for readability. 2025-09-14 22:50:29 -04:00
Lemmy
0b35fc1d2d Merge pull request #278 from kevindiaz314/main
feat: update tokyo night light color scheme with refined color values
2025-09-14 22:47:03 -04:00
LemmyCook
94c5d73a61 NPill: fix icon bg hover color 2025-09-14 22:46:40 -04:00
LemmyCook
f399a6d9f5 TrayMenu: improve tray opening direction in vertical bar more 2025-09-14 22:44:27 -04:00
Kevin Diaz
44fd859aec feat: update tokyo night light color scheme with refined color values 2025-09-14 22:33:13 -04:00
LemmyCook
53d0c3943d TrayMenu: Fix submenu burger icon color when hovered. 2025-09-14 22:24:39 -04:00
Lemmy
d80f923802 Merge pull request #229 from matejc/main
Fix for fingerprint flow on lock screen
2025-09-14 22:18:14 -04:00
Lemmy
1b861d7b7b Merge pull request #277 from kevindiaz314/main
feat: update tokyo night color scheme with refined color values
2025-09-14 22:07:10 -04:00
LemmyCook
65933208ec NPillVertical: match NHorizontal on margins and color 2025-09-14 22:03:44 -04:00
LemmyCook
5df218a789 Bar Widgets: Removed 3 unecessary anchors 2025-09-14 21:56:55 -04:00
LemmyCook
2f7a834b55 Bar: fix centering (againg) 2025-09-14 21:55:21 -04:00
LemmyCook
eca301553e Bar: better vertical centering on horizontal bar. 2025-09-14 21:51:43 -04:00
LemmyCook
91efa38101 HorizontalPill: different color for forceOpen + better margins 2025-09-14 21:49:29 -04:00
Kevin Diaz
acfe94f736 feat: update tokyo night color scheme with refined color values 2025-09-14 21:24:23 -04:00
LemmyCook
97bfcbb9e8 Clock: height calculation similar to NPill to avoid discrepancies 2025-09-14 21:15:27 -04:00
LemmyCook
9e47d91be2 v2.9.1-dev / git 2025-09-14 21:03:21 -04:00
LemmyCook
8872002225 Release 2.9.1 2025-09-14 21:01:14 -04:00
LemmyCook
519a85b251 AudioTab: fixed spinbox 2025-09-14 20:53:42 -04:00
LemmyCook
5aa7ff7e91 NValueSlider: new component + pimped NSlider with a small gradient and removed rounded corners due to issues. 2025-09-14 20:52:32 -04:00
LemmyCook
5ce5659b38 NPills: keep hover even when force open, as there are actions available on clicks. 2025-09-14 18:21:24 -04:00
LemmyCook
00459606ce Brightness: hotfix 2025-09-14 18:19:02 -04:00
LemmyCook
da1081700a NPill: replaced "Normal" by "On Hover" 2025-09-14 18:15:07 -04:00
LemmyCook
7e965262f5 NPill: Restored the old horizontal NPill 2025-09-14 18:07:43 -04:00
LemmyCook
19312d94c3 Removing test mode on battery 2025-09-14 17:21:38 -04:00
Ly-sec
118323e6b5 Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-09-14 23:14:59 +02:00
Ly-sec
aed81e82b0 Remove "%" from NVerticalPill add force close option to it too
NVerticalPill: add force close option
Any vertical bar widget: remove "%" display to have nice horizontal text
BarTab: add "always hide percentage" option so the pills will never
expand (opposite of always show percentage)
2025-09-14 23:13:11 +02:00
Lemmy
76c167c2c2 Merge pull request #273 from ThatOneCalculator/fix/power-menu-suspend-icon
fix(consistency): use unfilled pause icon for suspend in power menu
2025-09-14 17:08:40 -04:00
Ly-sec
852e2fa4d1 Fix N*Pill force show layout 2025-09-14 22:39:16 +02:00
Ly-sec
17dceffff6 Overview: add autoPaddingEnabled:false to MultiEffect blur 2025-09-14 22:33:44 +02:00
Kainoa Kanter
b589f37e0b use unfilled pause icon for suspend in power menu 2025-09-14 13:27:07 -07:00
Lysec
682c6af231 Merge pull request #267 from ThatOneCalculator/patch-1
docs: mention `gpu-screen-recorder` Flatpak
2025-09-14 22:25:13 +02:00
Ly-sec
d71226c6bd Merge branch 'main' of https://github.com/noctalia-dev/noctalia-shell 2025-09-14 22:09:40 +02:00
Ly-sec
1f3725faf8 set version to dev 2025-09-14 22:09:38 +02:00
LemmyCook
efcec1b2f9 Merge branch 'main' of github.com:noctalia-dev/noctalia-shell 2025-09-14 15:58:58 -04:00
LemmyCook
651322aef0 Bar: Removed unecessary qt5compat import 2025-09-14 15:58:56 -04:00
Kainoa Kanter
5ca2c2a095 docs: mention gpu-screen-recorder Flatpak 2025-09-13 11:42:41 -07:00
Matej Cotman
b9ae772987 feat(Modules/LockScreen): divert PAM messages to user (eg: to notify the user about fingerprint reader) 2025-09-12 18:26:15 +03:00
Matej Cotman
4265290a0f fix(fingerprint): better fingerprint integration by removing the check for empty password 2025-09-12 18:26:11 +03:00
59 changed files with 1426 additions and 1509 deletions

View File

@@ -4,7 +4,7 @@
"mOnPrimary": "#11111b",
"mSecondary": "#fab387",
"mOnSecondary": "#11111b",
"mTertiary": "#a6e3a1",
"mTertiary": "#94e2d5",
"mOnTertiary": "#11111b",
"mError": "#f38ba8",
"mOnError": "#11111b",
@@ -16,19 +16,19 @@
"mShadow": "#11111b"
},
"light": {
"mPrimary": "#9349ef",
"mPrimary": "#8839ef",
"mOnPrimary": "#eff1f5",
"mSecondary": "#f67525",
"mSecondary": "#fe640b",
"mOnSecondary": "#eff1f5",
"mTertiary": "#40b635",
"mTertiary": "#40a02b",
"mOnTertiary": "#eff1f5",
"mError": "#f38ba8",
"mOnError": "#11111b",
"mError": "#d20f39",
"mOnError": "#dce0e8",
"mSurface": "#eff1f5",
"mOnSurface": "#4c4f69",
"mSurfaceVariant": "#ccd0da",
"mOnSurfaceVariant": "#6c6f85",
"mOutline": "#aeb5c4",
"mOutline": "#a5adcb",
"mShadow": "#dce0e8"
}
}

View File

@@ -0,0 +1,34 @@
{
"dark": {
"mPrimary": "#D3C6AA",
"mOnPrimary": "#232A2E",
"mSecondary": "#D3C6AA",
"mOnSecondary": "#232A2E",
"mTertiary": "#9DA9A0",
"mOnTertiary": "#232A2E",
"mError": "#E67E80",
"mOnError": "#232A2E",
"mSurface": "#232A2E",
"mOnSurface": "#859289",
"mSurfaceVariant": "#2D353B",
"mOnSurfaceVariant": "#D3C6AA",
"mOutline": "#D3C6AA",
"mShadow": "#475258"
},
"light": {
"mPrimary": "#434F55",
"mOnPrimary": "#D3C6AA",
"mSecondary": "#232a2e",
"mOnSecondary": "#D3C6AA",
"mTertiary": "#333c43",
"mOnTertiary": "#9DA9A0",
"mError": "#E66868",
"mOnError": "#9DA9A0",
"mSurface": "#BEC5B2",
"mOnSurface": "#333C43",
"mSurfaceVariant": "#9DA9A0",
"mOnSurfaceVariant": "#232A2E",
"mOutline": "#232A2E",
"mShadow": "#ECF5ED"
}
}

View File

@@ -1,34 +1,34 @@
{
"dark": {
"mPrimary": "#ff9e64",
"mOnPrimary": "#1a1b26",
"mSecondary": "#e0af68",
"mOnSecondary": "#1a1b26",
"mTertiary": "#7aa2f7",
"mOnTertiary": "#1a1b26",
"mPrimary": "#7aa2f7",
"mOnPrimary": "#16161e",
"mSecondary": "#bb9af7",
"mOnSecondary": "#16161e",
"mTertiary": "#9ece6a",
"mOnTertiary": "#16161e",
"mError": "#f7768e",
"mOnError": "#1a1b26",
"mOnError": "#16161e",
"mSurface": "#1a1b26",
"mOnSurface": "#a9b1d6",
"mSurfaceVariant": "#292e42",
"mOnSurfaceVariant": "#787c99",
"mOutline": "#3d4462",
"mShadow": "#1a1b26"
"mOnSurface": "#c0caf5",
"mSurfaceVariant": "#24283b",
"mOnSurfaceVariant": "#9aa5ce",
"mOutline": "#565f89",
"mShadow": "#15161e"
},
"light": {
"mPrimary": "#fd5d00",
"mOnPrimary": "#e6e7ed",
"mSecondary": "#bb8027",
"mOnSecondary": "#e6e7ed",
"mTertiary": "#4a80f4",
"mOnTertiary": "#e6e7ed",
"mError": "#965027",
"mOnError": "#e6e7ed",
"mSurface": "#e6e7ed",
"mOnSurface": "#343b58",
"mSurfaceVariant": "#d5d6db",
"mOnSurfaceVariant": "#40434f",
"mOutline": "#babbc3",
"mShadow": "#c0caf5"
"mPrimary": "#2e7de9",
"mOnPrimary": "#e1e2e7",
"mSecondary": "#9854f1",
"mOnSecondary": "#e1e2e7",
"mTertiary": "#587539",
"mOnTertiary": "#e1e2e7",
"mError": "#f52a65",
"mOnError": "#e1e2e7",
"mSurface": "#e1e2e7",
"mOnSurface": "#3760bf",
"mSurfaceVariant": "#d0d5e3",
"mOnSurfaceVariant": "#6172b0",
"mOutline": "#b4b5b9",
"mShadow": "#a8aecb"
}
}

View File

@@ -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"
}

View 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}}"
}
}

View File

@@ -1,572 +1,113 @@
/*
* Vesktop Theme
* Generated with Matugen
* Base was taken from https://github.com/catppuccin/discord <3
/**
* @name noctalia
* @description Original theme: midnight | A dark, rounded discord theme.
* @author refact0r
* @version 1.6.2
* @invite nz87hXyvcy
* @website https://github.com/refact0r/midnight-discord
* @source https://github.com/refact0r/midnight-discord/blob/master/midnight.theme.css
* @authorId 508863359777505290
* @authorLink https://www.refact0r.dev
*/
/* IMPORTANT: make sure to enable dark mode in discord settings for the theme to apply properly!!! */
/* Dark Theme */
.visual-refresh.theme-dark,
.visual-refresh .theme-dark {
/* Brand Colors */
--brand-experiment: {{colors.primary.default.hex}};
--bg-brand: {{colors.primary.default.hex}};
--brand-500: {{colors.primary.default.hex}} !important;
--text-link: {{colors.primary.default.hex}} !important;
--text-brand: {{colors.primary.default.hex}};
--control-brand-foreground: {{colors.primary.default.hex}};
--control-brand-foreground-new: {{colors.primary.default.hex}};
--mention-foreground: {{colors.primary.default.hex}};
--mention-background: {{colors.primary.default.hex}}20;
--focus-primary: {{colors.primary.default.hex}};
--logo-primary: {{colors.on_surface.default.hex}};
--badge-brand-bg: {{colors.primary.default.hex}};
--badge-brand-text: {{colors.on_primary.default.hex}};
@import url('https://refact0r.github.io/midnight-discord/build/midnight.css');
/* Text Colors */
--header-primary: {{colors.on_surface.default.hex}} !important;
--header-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}};
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}};
--text-secondary: {{colors.on_surface_variant.default.hex}};
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}};
--interactive-hover: {{colors.on_surface.default.hex}};
--interactive-active: {{colors.on_surface.default.hex}};
/* customize things here */
:root {
/* font, change to 'gg sans' for default discord font*/
--font: 'figtree';
/* Main Background Colors - Bar color (mSurface) colors.surface.default.hex*/
--background-primary: {{colors.surface_variant.default.hex}} !important;
--background-floating: {{colors.surface_variant.default.hex}} !important;
--background-surface-high: {{colors.surface_variant.default.hex}} !important;
--modal-background: {{colors.surface_variant.default.hex}} !important;
--app-background-frame: {{colors.surface_variant.default.hex}} !important;
--home-background: {{colors.surface_variant.default.hex}} !important;
--chat-background: {{colors.surface_variant.default.hex}} !important;
--chat-background-default: {{colors.surface_variant.default.hex}} !important;
--chat-input-container-background: {{colors.surface_container.default.hex}} !important;
/* Secondary Background Colors - Workspace color (mSurfaceVariant) */
--background-secondary: {{colors.surface.default.hex}} !important;
--background-secondary-alt: {{colors.surface.default.hex}} !important;
--background-surface-higher: {{colors.surface.default.hex}} !important;
--background-base-low: {{colors.surface.default.hex}} !important;
--background-base-lower: {{colors.surface.default.hex}} !important;
--channeltextarea-background: {{colors.surface_container.default.hex}} !important;
--modal-footer-background: {{colors.surface.default.hex}} !important;
/* New Messages Banner */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord-specific variables for new messages banner */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
/* Discord unread messages banner specific variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord banner text variables */
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}} !important;
--text-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}} !important;
/* Additional Discord banner variables */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
--background-message-hover: {{colors.surface_variant.default.hex}}50 !important;
--background-modifier-hover: {{colors.surface_variant.default.hex}}80 !important;
--background-modifier-selected: {{colors.primary.default.hex}}20 !important;
--background-modifier-accent: {{colors.primary.default.hex}}30 !important;
--background-modifier-active: {{colors.primary.default.hex}}25 !important;
/* Chat Input Improvements */
--text-input-background: {{colors.surface_container.default.hex}} !important;
--text-input-border: {{colors.outline.default.hex}} !important;
--text-input-border-hover: {{colors.primary.default.hex}} !important;
/* Additional Discord-specific input variables */
--deprecated-text-input-bg: {{colors.surface_container.default.hex}} !important;
--deprecated-text-input-border: {{colors.outline.default.hex}} !important;
--deprecated-text-input-border-hover: {{colors.primary.default.hex}} !important;
--input-background: {{colors.surface_container.default.hex}} !important;
--input-border: {{colors.outline.default.hex}} !important;
--input-placeholder-text: {{colors.on_surface_variant.default.hex}} !important;
/* Elevated/Container Backgrounds */
--background-tertiary: {{colors.surface_container.default.hex}} !important;
--background-accent: {{colors.surface_container.default.hex}} !important;
--background-surface-highest: {{colors.surface_container_high.default.hex}} !important;
--background-base-lowest: {{colors.surface_container.default.hex}} !important;
/* top left corner text */
--corner-text: 'Midnight';
/* Border Colors */
--border-faint: {{colors.outline_variant.default.hex}};
--border-strong: {{colors.surface_container.default.hex}};
--border-normal: {{colors.surface_container_high.default.hex}};
--border-subtle: {{colors.surface.default.hex}} !important;
--chat-border: {{colors.surface_container_high.default.hex}};
/* color of status indicators and window controls */
--online-indicator: {{colors.inverse_primary.default.hex}}; /* change to #23a55a for default green */
--dnd-indicator: {{colors.error.default.hex}}; /* change to #f13f43 for default red */
--idle-indicator: {{colors.tertiary_container.default.hex}}; /* change to #f0b232 for default yellow */
--streaming-indicator: {{colors.on_primary.default.hex}}; /* change to #593695 for default purple */
/* Status Colors */
--status-positive: {{colors.tertiary.default.hex}};
--status-positive-background: {{colors.tertiary.default.hex}};
--status-positive-text: {{colors.on_tertiary.default.hex}};
--text-positive: {{colors.tertiary.default.hex}};
--text-feedback-positive: {{colors.tertiary.default.hex}};
--background-feedback-positive: {{colors.tertiary.default.hex}}20;
--info-positive-background: {{colors.tertiary.default.hex}}20;
--info-positive-foreground: {{colors.tertiary.default.hex}};
--info-positive-text: {{colors.on_surface.default.hex}};
/* accent colors */
--accent-1: {{colors.tertiary.default.hex}}; /* links */
--accent-2: {{colors.primary.default.hex}}; /* general unread/mention elements, some icons when active */
--accent-3: {{colors.primary.default.hex}}; /* accent buttons */
--accent-4: {{colors.surface_bright.default.hex}}; /* accent buttons when hovered */
--accent-5: {{colors.primary_fixed_dim.default.hex}}; /* accent buttons when clicked */
--mention: {{colors.surface.default.hex}}; /* mentions & mention messages */
--mention-hover: {{colors.surface_bright.default.hex}}; /* mentions & mention messages when hovered */
--status-warning: {{colors.secondary.default.hex}};
--status-warning-background: {{colors.secondary.default.hex}};
--status-warning-text: {{colors.on_secondary.default.hex}};
--text-warning: {{colors.secondary.default.hex}};
--text-feedback-warning: {{colors.secondary.default.hex}};
--background-feedback-warning: {{colors.secondary.default.hex}}20;
--info-warning-background: {{colors.secondary.default.hex}}20;
--info-warning-foreground: {{colors.secondary.default.hex}};
--info-warning-text: {{colors.on_surface.default.hex}};
/* text colors */
--text-0: {{colors.surface.default.hex}}; /* text on colored elements */
--text-1: {{colors.on_surface.default.hex}}; /* other normally white text */
--text-2: {{colors.on_surface.default.hex}}; /* headings and important text */
--text-3: {{colors.on_surface_variant.default.hex}}; /* normal text */
--text-4: {{colors.on_surface_variant.default.hex}}; /* icon buttons and channels */
--text-5: {{colors.outline.default.hex}}; /* muted channels/chats and timestamps */
--status-danger: {{colors.error.default.hex}};
--status-danger-background: {{colors.error.default.hex}};
--status-danger-text: {{colors.on_error.default.hex}};
--text-danger: {{colors.error.default.hex}};
--text-feedback-critical: {{colors.error.default.hex}};
--background-feedback-critical: {{colors.error.default.hex}}20;
--info-danger-background: {{colors.error.default.hex}}20;
--info-danger-foreground: {{colors.error.default.hex}};
--info-danger-text: {{colors.on_surface.default.hex}};
/* background and dark colors */
--bg-1: {{colors.primary.default.hex}}; /* dark buttons when clicked */
--bg-2: {{colors.surface_container_high.default.hex}}; /* dark buttons */
--bg-3: {{colors.surface_container_low.default.hex}}; /* spacing, secondary elements */
--bg-4: {{colors.surface.default.hex}}; /* main background color */
--hover: {{colors.surface_bright.default.hex}}; /* channels and buttons when hovered */
--active: {{colors.surface_bright.default.hex}}; /* channels and buttons when clicked or selected */
--message-hover: {{colors.surface_bright.default.hex}}; /* messages when hovered */
/* Button Colors */
--button-secondary-background: {{colors.surface_variant.default.hex}} !important;
--button-secondary-background-hover: {{colors.surface_container.default.hex}};
--button-secondary-background-active: {{colors.surface_container.default.hex}};
--button-secondary-background-disabled: {{colors.surface_variant.default.hex}};
--button-secondary-text: {{colors.on_surface.default.hex}} !important;
/* amount of spacing and padding */
--spacing: 12px;
--button-filled-brand-text: {{colors.on_primary.default.hex}};
--button-filled-brand-background: {{colors.primary.default.hex}};
--button-filled-brand-background-hover: {{colors.primary.default.hex}};
--button-filled-brand-background-active: {{colors.primary.default.hex}};
/* animations */
/* ALL ANIMATIONS CAN BE DISABLED WITH REDUCED MOTION IN DISCORD SETTINGS */
--list-item-transition: 0.2s ease; /* channels/members/settings hover transition */
--unread-bar-transition: 0.2s ease; /* unread bar moving into view transition */
--moon-spin-transition: 0.4s ease; /* moon icon spin */
--icon-spin-transition: 1s ease; /* round icon button spin (settings, emoji, etc.) */
/* Input Colors */
--input-background: {{colors.surface_container.default.hex}};
--input-border: {{colors.outline.default.hex}};
--input-placeholder-text: {{colors.on_surface_variant.default.hex}};
/* corner roundness (border-radius) */
--roundness-xl: 22px; /* roundness of big panel outer corners */
--roundness-l: 20px; /* popout panels */
--roundness-m: 16px; /* smaller panels, images, embeds */
--roundness-s: 12px; /* members, settings inputs */
--roundness-xs: 10px; /* channels, buttons */
--roundness-xxs: 8px; /* searchbar, small elements */
/* Scrollbar Colors */
--scrollbar-thin-thumb: {{colors.primary.default.hex}};
--scrollbar-thin-track: transparent;
--scrollbar-auto-thumb: {{colors.primary.default.hex}};
--scrollbar-auto-track: {{colors.surface_container_high.default.hex}};
--scrollbar-auto-scrollbar-color-thumb: {{colors.primary.default.hex}};
--scrollbar-auto-scrollbar-color-track: {{colors.surface_container_high.default.hex}};
/* direct messages moon icon */
/* change to block to show, none to hide */
--discord-icon: none; /* discord icon */
--moon-icon: block; /* moon icon */
--moon-icon-url: url('https://upload.wikimedia.org/wikipedia/commons/c/c4/Font_Awesome_5_solid_moon.svg'); /* custom icon url */
--moon-icon-size: auto;
/* Icon Colors */
--icon-muted: {{colors.on_surface_variant.default.hex}};
--icon-default: {{colors.on_surface.default.hex}};
--icon-primary: {{colors.on_surface.default.hex}};
--icon-secondary: {{colors.on_surface_variant.default.hex}};
--icon-tertiary: {{colors.on_surface_variant.default.hex}} !important;
/* Channel Colors */
--channels-default: {{colors.on_surface_variant.default.hex}} !important;
--channel-icon: {{colors.on_surface_variant.default.hex}} !important;
--channel-text-area-placeholder: {{colors.on_surface.default.hex}}80;
/* Selection and Hover States */
--background-modifier-hover: {{colors.surface_variant.default.hex}}80;
--background-modifier-selected: {{colors.primary.default.hex}}20 !important;
--background-modifier-accent: {{colors.primary.default.hex}}30;
--background-modifier-active: {{colors.primary.default.hex}}25 !important;
--background-message-hover: {{colors.surface_variant.default.hex}}50 !important;
--background-message-highlight: {{colors.primary.default.hex}}15;
--background-message-highlight-hover: {{colors.primary.default.hex}}20;
/* Code Block - Use workspace background */
--background-code: {{colors.surface_container.default.hex}};
--textbox-markdown-syntax: {{colors.on_surface_variant.default.hex}};
/* Spoiler */
--spoiler-revealed-background: {{colors.surface_container.default.hex}};
--spoiler-hidden-background: {{colors.surface_variant.default.hex}};
/* White/Black Overrides */
--white: {{colors.on_surface.default.hex}};
--white-400: {{colors.on_surface.default.hex}};
--white-500: {{colors.on_surface.default.hex}};
--white-600: {{colors.on_surface_variant.default.hex}};
--white-700: {{colors.on_surface_variant.default.hex}};
--black-500: {{colors.surface_container_high.default.hex}};
/* Force styling for Discord unread messages banner */
--unread-bar-background: {{colors.primary.default.hex}}15 !important;
--unread-bar-text: {{colors.on_surface.default.hex}} !important;
--unread-bar-hover: {{colors.primary.default.hex}}20 !important;
/* Additional Discord unread bar variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Discord banner specific variables */
--background-message-automod: {{colors.primary.default.hex}}15 !important;
--background-message-automod-hover: {{colors.primary.default.hex}}20 !important;
--background-message-highlight: {{colors.primary.default.hex}}15 !important;
--background-message-highlight-hover: {{colors.primary.default.hex}}20 !important;
/* Discord unread bar specific variables */
--background-mentioned: {{colors.primary.default.hex}}15 !important;
--background-mentioned-hover: {{colors.primary.default.hex}}20 !important;
--text-mentioned: {{colors.on_surface.default.hex}} !important;
--text-mentioned-hover: {{colors.on_surface.default.hex}} !important;
--text-mentioned-link: {{colors.primary.default.hex}} !important;
/* Additional Discord text variables that might affect the banner */
--text-normal: {{colors.on_surface.default.hex}} !important;
--text-default: {{colors.on_surface.default.hex}} !important;
--text-primary: {{colors.on_surface.default.hex}} !important;
--text-secondary: {{colors.on_surface_variant.default.hex}} !important;
--text-tertiary: {{colors.on_surface_variant.default.hex}} !important;
--text-muted: {{colors.on_surface_variant.default.hex}} !important;
--interactive-normal: {{colors.on_surface.default.hex}} !important;
--interactive-muted: {{colors.on_surface_variant.default.hex}} !important;
/* Force styling for Discord chat input */
--chat-input-background: {{colors.surface_container.default.hex}} !important;
--chat-input-placeholder: {{colors.on_surface_variant.default.hex}} !important;
/* Discord unread messages banner specific variables */
--new-messages-bar-background: {{colors.surface_container.default.hex}} !important;
--new-messages-bar-text: {{colors.on_surface.default.hex}} !important;
--new-messages-bar-hover: {{colors.surface_container_high.default.hex}} !important;
--bar-button-background: {{colors.surface_container.default.hex}} !important;
--bar-button-text: {{colors.on_surface.default.hex}} !important;
--bar-button-hover: {{colors.surface_container_high.default.hex}} !important;
/* filter uncolorable elements to fit theme */
/* (just set to none, they're too much work to configure) */
--login-bg-filter: saturate(0.3) hue-rotate(-15deg) brightness(0.4); /* login background artwork */
--green-to-accent-3-filter: hue-rotate(56deg) saturate(1.43); /* add friend page explore icon */
--blurple-to-accent-3-filter: hue-rotate(304deg) saturate(0.84) brightness(1.2); /* add friend page school icon */
}
.visual-refresh.theme-dark ::selection,
.visual-refresh .theme-dark ::selection {
background-color: {{colors.primary.default.hex}};
/* Selected chat/friend text */
.selected_f5eb4b,
.selected_f6f816 .link_d8bfb3 {
color: var(--text-0) !important;
background: var(--accent-3) !important;
}
/* Force Discord unread messages banner styling */
.visual-refresh.theme-dark .newMessagesBar__0f481,
.visual-refresh.theme-dark .barButtonMain__0f481,
.visual-refresh.theme-dark .barButtonBase__0f481,
.visual-refresh.theme-dark .span__0f481 {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
.selected_f6f816 .link_d8bfb3 * {
color: var(--text-0) !important;
fill: var(--text-0) !important;
}
.visual-refresh.theme-dark .newMessagesBar__0f481:hover,
.visual-refresh.theme-dark .barButtonMain__0f481:hover,
.visual-refresh.theme-dark .barButtonBase__0f481:hover {
background-color: {{colors.surface_container_high.default.hex}} !important;
/* Make channel name text less visible (darker) */
.name__2ea32 {
color: var(--text-5) !important;
opacity: 0.7 !important;
}
/* Force Discord chat input styling */
.visual-refresh.theme-dark .channelTextArea-rNsIhG,
.visual-refresh.theme-dark .channelTextArea-rNsIhG *,
.visual-refresh.theme-dark .scrollableContainer-2NUZem,
.visual-refresh.theme-dark [data-slate-editor="true"] {
background-color: {{colors.surface_container.default.hex}} !important;
/* Make unread channel names brighter */
.link__2ea32[aria-label*="unread"] .name__2ea32 {
color: var(--text-2) !important;
opacity: 1 !important;
font-weight: 600 !important;
}
.visual-refresh.theme-dark [data-slate-editor="true"]::placeholder,
.visual-refresh.theme-dark .channelTextArea-rNsIhG [data-slate-editor="true"]::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Discord Emoji Picker Theming */
.visual-refresh.theme-dark .contentWrapper__08434,
.visual-refresh.theme-dark .emojiPicker_c0e32c,
.visual-refresh.theme-dark .wrapper_c0e32c {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .nav__08434,
.visual-refresh.theme-dark .navList__08434 {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .navButton__08434 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .navButtonActive__08434 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .searchBar_c0e32c,
.visual-refresh.theme-dark .input_a45028 {
background-color: {{colors.surface.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .input_a45028::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .header_c656ac,
.visual-refresh.theme-dark .header__14245,
.visual-refresh.theme-dark .wrapper__14245 {
background-color: {{colors.surface_variant.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerLabel__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .interactive__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .header__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .header__14245 * {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerIcon__14245 svg,
.visual-refresh.theme-dark .headerCollapseIcon__14245 svg {
color: {{colors.on_surface.default.hex}} !important;
fill: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .emojiItem_fc7141 {
background-color: transparent !important;
}
.visual-refresh.theme-dark .emojiItem_fc7141:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
.visual-refresh.theme-dark .emojiItemSelected_fc7141 {
background-color: {{colors.primary.default.hex}}20 !important;
}
.visual-refresh.theme-dark .inspector_aeaaeb {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryList_c0e32c {
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryItem_b9ee0c {
background-color: transparent !important;
}
.visual-refresh.theme-dark .categoryItem_b9ee0c:hover {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryItemDefaultCategorySelected_b9ee0c {
background-color: {{colors.surface_variant.default.hex}} !important;
}
/* Additional Discord emoji picker elements */
.visual-refresh.theme-dark .navItem__08434 {
background-color: {{colors.surface_variant.default.hex}} !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .navItem__08434:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
.visual-refresh.theme-dark .stickersNavItem__08434 {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .wrapper__14245 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .headerLabel__14245 {
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .headerIcon__14245 svg,
.visual-refresh.theme-dark .headerCollapseIcon__14245 svg {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .interactive__14245:hover {
background-color: {{colors.surface_container.default.hex}} !important;
}
/* Chat input styling */
.visual-refresh.theme-dark .scrollableContainer__74017,
.visual-refresh.theme-dark .themedBackground__74017,
.visual-refresh.theme-dark .inner__74017,
.visual-refresh.theme-dark .textArea__74017,
.visual-refresh.theme-dark .slateContainer_ec4baf,
.visual-refresh.theme-dark .markup__75297,
.visual-refresh.theme-dark .editor__1b31f,
.visual-refresh.theme-dark .slateTextArea_ec4baf {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .emptyText__1464f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .placeholder__1b31f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Message content styling */
.visual-refresh.theme-dark .messageContent_c19a55 {
color: {{colors.on_surface.default.hex}} !important;
background-color: {{colors.surface.default.hex}} !important;
}
.visual-refresh.theme-dark .messageContent_c19a55 .markup__75297 {
color: {{colors.on_surface.default.hex}} !important;
background-color: {{colors.surface.default.hex}} !important;
}
/* Message background styling */
.visual-refresh.theme-dark .message__5126c,
.visual-refresh.theme-dark .cozyMessage__5126c,
.visual-refresh.theme-dark .wrapper_c19a55,
.visual-refresh.theme-dark .contents_c19a55 {
background-color: {{colors.surface.default.hex}} !important;
}
/* Message hover effects */
.visual-refresh.theme-dark .message__5126c:hover {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .message__5126c:hover * {
color: {{colors.on_surface.default.hex}} !important;
}
/* Remove Discord's native quote/reply bar */
.visual-refresh.theme-dark .message__5126c::before {
display: none !important;
}
.visual-refresh.theme-dark .message__5126c.hasReply_c19a55::before {
display: none !important;
}
/* Channel styling - darker text for read channels */
.visual-refresh.theme-dark .link__2ea32 .name__2ea32 {
color: {{colors.outline.default.hex}} !important;
}
/* Unread channels keep normal color */
.visual-refresh.theme-dark .link__2ea32[aria-label*="unread"] .name__2ea32 {
color: {{colors.on_surface.default.hex}} !important;
}
/* Search input styling */
.visual-refresh.theme-dark .inner_a45028 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .input_a45028 {
background-color: transparent !important;
}
.visual-refresh.theme-dark .input_a45028::placeholder {
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Chat input placeholder styling */
.visual-refresh.theme-dark .emptyText__1464f {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .slateTextArea_ec4baf > div:first-child .emptyText__1464f::before {
content: "send a message" !important;
color: {{colors.on_surface_variant.default.hex}} !important;
}
/* Hide placeholder when input is focused */
.visual-refresh.theme-dark .slateTextArea_ec4baf:focus .emptyText__1464f::before,
.visual-refresh.theme-dark .markup__75297:focus .emptyText__1464f::before {
display: none !important;
}
.visual-refresh.theme-dark .message__5126c:hover .messageContent_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .markup__75297,
.visual-refresh.theme-dark .message__5126c:hover .header_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .headerText_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .username_c19a55,
.visual-refresh.theme-dark .message__5126c:hover .timestamp_c19a55 {
background-color: {{colors.surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .categoryIcon_b9ee0c svg {
color: {{colors.on_surface_variant.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c {
background-color: {{colors.surface_container.default.hex}} !important;
color: {{colors.on_surface.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c:hover {
background-color: {{colors.surface_container_high.default.hex}} !important;
}
.visual-refresh.theme-dark .unicodeShortcut_b9ee0c svg {
color: {{colors.on_surface.default.hex}} !important;
}
/* Number badge styling */
.visual-refresh.theme-dark .numberBadge__2b1f5 {
color: {{colors.surface.default.hex}} !important;
background-color: {{colors.primary.default.hex}} !important;
}
/* New badge styling */
.visual-refresh.theme-dark .newBadge__4ed1a {
color: {{colors.surface.default.hex}} !important;
background-color: {{colors.primary.default.hex}} !important;
}

View File

@@ -46,7 +46,7 @@ Singleton {
"lock": "lock",
"logout": "logout",
"reboot": "refresh",
"suspend": "player-pause-filled",
"suspend": "player-pause",
"nightlight-on": "moon",
"nightlight-off": "moon-off",
"nightlight-forced": "moon-stars",

View File

@@ -322,6 +322,7 @@ Singleton {
property string avatarImage: defaultAvatar
property bool dimDesktop: false
property bool showScreenCorners: false
property bool forceBlackScreenCorners: false
property real radiusRatio: 1.0
property real screenRadiusRatio: 1.0
// Animation speed multiplier (0.1x - 2.0x)
@@ -449,6 +450,7 @@ Singleton {
property bool foot: false
property bool fuzzel: false
property bool vesktop: false
property bool pywalfox: false
property bool enableUserTemplates: false
}

View File

@@ -60,6 +60,7 @@ Variants {
MultiEffect {
anchors.fill: parent
source: bgImage
autoPaddingEnabled: false
blurEnabled: true
blur: 0.48
blurMax: 128

View File

@@ -7,7 +7,7 @@ import qs.Services
import qs.Widgets
Loader {
active: Settings.data.general.showScreenCorners && !Settings.data.bar.floating
active: Settings.data.general.showScreenCorners
sourceComponent: Variants {
model: Quickshell.screens
@@ -19,7 +19,7 @@ Loader {
property real scaling: ScalingService.getScreenScale(screen)
screen: modelData
property color cornerColor: Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
property color cornerColor: Settings.data.general.forceBlackScreenCorners ? Qt.rgba(0, 0, 0, 1) : Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity)
property real cornerRadius: Style.screenRadius * scaling
property real cornerSize: Style.screenRadius * scaling
@@ -46,10 +46,12 @@ Loader {
}
margins {
top: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "top" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
bottom: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "bottom" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
left: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "left" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
right: ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "right" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
// When bar is floating, corners should be at screen edges (no margins)
// When bar is not floating, respect bar margins as before
top: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "top" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
bottom: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "bottom" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
left: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "left" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
right: !Settings.data.bar.floating && ((modelData && Settings.data.bar.monitors.includes(modelData.name)) || (Settings.data.bar.monitors.length === 0)) && Settings.data.bar.position === "right" && Settings.data.bar.backgroundOpacity > 0 ? Math.round(Style.barHeight * scaling) : 0
}
mask: Region {}

View File

@@ -91,7 +91,7 @@ Variants {
anchors.fill: parent
// Top section (left widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: Style.marginM * root.scaling
@@ -109,13 +109,13 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.left.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
// Center section (center widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginS * root.scaling
@@ -132,13 +132,13 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.center.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
// Bottom section (right widgets)
Column {
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.marginM * root.scaling
@@ -156,7 +156,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.right.length
}
anchors.horizontalCenter: parent.horizontalCenter
Layout.alignment: Qt.AlignHCenter
}
}
}
@@ -189,6 +189,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.left.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -213,6 +214,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.center.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -238,6 +240,7 @@ Variants {
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.right.length
}
Layout.alignment: Qt.AlignVCenter
}
}
}

View File

@@ -178,7 +178,7 @@ PopupWindow {
font.pointSize: Style.fontSizeS * scaling
verticalAlignment: Text.AlignVCenter
visible: modelData?.hasChildren ?? false
color: Color.mOnSurface
color: (mouseArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface)
}
}
@@ -220,9 +220,32 @@ PopupWindow {
const submenuWidth = menuWidth * scaling // Assuming a similar width as the parent
const overlap = 4 * scaling // A small overlap to bridge the mouse path
// Check if there's enough space on the right
// Determine submenu opening direction based on bar position and available space
let openLeft = false
// Check bar position first
const barPosition = Settings.data.bar.position
const globalPos = entry.mapToGlobal(0, 0)
const openLeft = (globalPos.x + entry.width + submenuWidth > (screen ? screen.width : Screen.width))
if (barPosition === "right") {
// Bar is on the right, prefer opening submenus to the left
openLeft = true
} else if (barPosition === "left") {
// Bar is on the left, prefer opening submenus to the right
openLeft = false
} else {
// Bar is horizontal (top/bottom) or undefined, use space-based logic
openLeft = (globalPos.x + entry.width + submenuWidth > (screen ? screen.width : Screen.width))
// Secondary check: ensure we don't open off-screen
if (openLeft && globalPos.x - submenuWidth < 0) {
// Would open off the left edge, force right opening
openLeft = false
} else if (!openLeft && globalPos.x + entry.width + submenuWidth > (screen ? screen.width : Screen.width)) {
// Would open off the right edge, force left opening
openLeft = true
}
}
// Position with overlap
const anchorX = openLeft ? -submenuWidth + overlap : entry.width - overlap

View File

@@ -29,13 +29,13 @@ Item {
return {}
}
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool alwaysShowPercentage: widgetSettings.alwaysShowPercentage !== undefined ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
readonly property bool isBarVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
readonly property string displayMode: widgetSettings.displayMode !== undefined ? widgetSettings.displayMode : widgetMetadata.displayMode
readonly property real warningThreshold: widgetSettings.warningThreshold !== undefined ? widgetSettings.warningThreshold : widgetMetadata.warningThreshold
// Test mode
readonly property bool testMode: false
readonly property int testPercent: 90
readonly property int testPercent: 100
readonly property bool testCharging: false
// Main properties
@@ -86,9 +86,11 @@ Item {
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent, charging, isReady)
text: (isReady || testMode) ? Math.round(percent) + "%" : "-"
text: (isReady || testMode) ? Math.round(percent) : "-"
suffix: "%"
autoHide: false
forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage
forceOpen: isReady && (testMode || battery.isLaptopBattery) && displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
tooltipText: {
let lines = []

View File

@@ -28,7 +28,8 @@ Item {
return {}
}
readonly property bool userAlwaysShowPercentage: (widgetSettings.alwaysShowPercentage !== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
readonly property bool isBarVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
// Used to avoid opening the pill on Quickshell startup
property bool firstBrightnessReceived: false
@@ -80,14 +81,16 @@ Item {
autoHide: false // Important to be false so we can hover as long as we want
text: {
var monitor = getMonitor()
return monitor ? (Math.round(monitor.brightness * 100) + "%") : ""
return monitor ? Math.round(monitor.brightness * 100) : ""
}
forceOpen: userAlwaysShowPercentage
suffix: text.length > 0 ? "%" : "-"
forceOpen: displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
tooltipText: {
var monitor = getMonitor()
if (!monitor)
return ""
return "Brightness: " + Math.round(monitor.brightness * 100) + "%\nMethod: " + monitor.method + "\nLeft click for advanced settings.\nScroll up/down to change brightness."
return "Brightness: " + Math.round(monitor.brightness * 100) + "%\nRight click for settings.\nScroll to modify brightness."
}
onWheel: function (angle) {
@@ -101,6 +104,12 @@ Item {
}
}
onClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.open()
}
onRightClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Display

View File

@@ -36,10 +36,10 @@ Rectangle {
readonly property string displayFormat: widgetSettings.displayFormat !== undefined ? widgetSettings.displayFormat : widgetMetadata.displayFormat
// Use compact mode for vertical bars
readonly property bool useCompactMode: barPosition === "left" || barPosition === "right"
readonly property bool verticalMode: barPosition === "left" || barPosition === "right"
implicitWidth: useCompactMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling)
implicitHeight: useCompactMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.capsuleHeight * scaling)
implicitWidth: verticalMode ? Math.round(Style.capsuleHeight * scaling) : Math.round(layout.implicitWidth + Style.marginM * 2 * scaling)
implicitHeight: verticalMode ? Math.round(Style.capsuleHeight * 2.5 * scaling) : Math.round(Style.baseWidgetSize * 0.8 * scaling) // Match NPill
radius: Math.round(Style.radiusS * scaling)
color: Color.mSurfaceVariant
@@ -52,18 +52,18 @@ Rectangle {
ColumnLayout {
id: layout
anchors.centerIn: parent
spacing: useCompactMode ? -2 * scaling : -3 * scaling
spacing: verticalMode ? -2 * scaling : -3 * scaling
// Compact mode for vertical bars - Time section (HH, MM)
Repeater {
model: useCompactMode ? 2 : 1
model: verticalMode ? 2 : 1
NText {
readonly property bool showSeconds: (displayFormat === "time-seconds")
readonly property bool inlineDate: (displayFormat === "time-date")
readonly property var now: Time.date
text: {
if (useCompactMode) {
if (verticalMode) {
// Compact mode: time section (first 2 lines)
switch (index) {
case 0:
@@ -126,7 +126,7 @@ Rectangle {
}
font.family: Settings.data.ui.fontFixed
font.pointSize: useCompactMode ? Style.fontSizeXXS * scaling : Style.fontSizeXS * scaling
font.pointSize: verticalMode ? Style.fontSizeXXS * scaling : Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
@@ -135,7 +135,7 @@ Rectangle {
// Separator line for compact mode (between time and date)
Rectangle {
visible: useCompactMode
visible: verticalMode
Layout.preferredWidth: 20 * scaling
Layout.preferredHeight: 2 * scaling
Layout.alignment: Qt.AlignHCenter
@@ -148,12 +148,12 @@ Rectangle {
// Compact mode for vertical bars - Date section (DD, MM)
Repeater {
model: useCompactMode ? 2 : 0
model: verticalMode ? 2 : 0
NText {
readonly property var now: Time.date
text: {
if (useCompactMode) {
if (verticalMode) {
// Compact mode: date section (last 2 lines)
switch (index) {
case 0:
@@ -178,7 +178,7 @@ Rectangle {
// Second line for normal mode (date)
NText {
visible: !useCompactMode && (displayFormat === "time-date-short")
visible: !verticalMode && (displayFormat === "time-date-short")
text: {
const now = Time.date
const day = now.getDate().toString().padStart(2, '0')

View File

@@ -1,12 +1,13 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services
import qs.Widgets
import qs.Modules.SettingsPanel
NIconButton {
Item {
id: root
// Widget properties passed from Bar.qml
@@ -35,33 +36,80 @@ NIconButton {
readonly property string leftClickExec: widgetSettings.leftClickExec || widgetMetadata.leftClickExec
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
readonly property string textCommand: widgetSettings.textCommand !== undefined ? widgetSettings.textCommand : (widgetMetadata.textCommand || "")
readonly property int textIntervalMs: widgetSettings.textIntervalMs !== undefined ? widgetSettings.textIntervalMs : (widgetMetadata.textIntervalMs || 3000)
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
enabled: hasExec
allowClickWhenDisabled: true // we want to be able to open config with left click when its not setup properly
colorBorder: Color.transparent
colorBorderHover: Color.transparent
sizeRatio: 0.8
icon: customIcon
tooltipText: {
if (!hasExec) {
return "Custom Button - Configure in settings"
} else {
var lines = []
if (leftClickExec !== "") {
lines.push(`Left click: <i>${leftClickExec}</i>.`)
implicitWidth: pill.width
implicitHeight: pill.height
NPill {
id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: customIcon
text: _dynamicText
autoHide: false
forceOpen: _dynamicText !== ""
forceClose: false
disableOpen: true
tooltipText: {
if (!hasExec) {
return "Custom Button - Configure in settings"
} else {
var lines = []
if (leftClickExec !== "") {
lines.push(`Left click: ${leftClickExec}.`)
}
if (rightClickExec !== "") {
lines.push(`Right click: ${rightClickExec}.`)
}
if (middleClickExec !== "") {
lines.push(`Middle click: ${middleClickExec}.`)
}
return lines.join("\n")
}
if (rightClickExec !== "") {
lines.push(`Right click: <i>${rightClickExec}</i>.`)
}
if (middleClickExec !== "") {
lines.push(`Middle click: <i>${middleClickExec}</i>.`)
}
return lines.join("<br/>")
}
onClicked: root.onClicked()
onRightClicked: root.onRightClicked()
onMiddleClicked: root.onMiddleClicked()
}
// Internal state for dynamic text
property string _dynamicText: ""
// Periodically run the text command (if set)
Timer {
id: refreshTimer
interval: Math.max(250, textIntervalMs)
repeat: true
running: (textCommand && textCommand.length > 0)
triggeredOnStart: true
onTriggered: {
if (!textCommand || textCommand.length === 0)
return
if (textProc.running)
return
textProc.command = ["sh", "-lc", textCommand]
textProc.running = true
}
}
onClicked: {
Process {
id: textProc
stdout: StdioCollector {}
stderr: StdioCollector {}
onExited: (exitCode, exitStatus) => {
var out = String(stdout.text || "").trim()
if (out.indexOf("\n") !== -1) {
out = out.split("\n")[0]
}
_dynamicText = out
}
}
function onClicked() {
if (leftClickExec) {
Quickshell.execDetached(["sh", "-c", leftClickExec])
Logger.log("CustomButton", `Executing command: ${leftClickExec}`)
@@ -73,14 +121,14 @@ NIconButton {
}
}
onRightClicked: {
function onRightClicked() {
if (rightClickExec) {
Quickshell.execDetached(["sh", "-c", rightClickExec])
Logger.log("CustomButton", `Executing command: ${rightClickExec}`)
}
}
onMiddleClicked: {
function onMiddleClicked() {
if (middleClickExec) {
Quickshell.execDetached(["sh", "-c", middleClickExec])
Logger.log("CustomButton", `Executing command: ${middleClickExec}`)

View File

@@ -17,7 +17,5 @@ NIconButton {
colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary
colorBorder: Color.transparent
colorBorderHover: Color.transparent
anchors.verticalCenter: parent.verticalCenter
onClicked: Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode
}

View File

@@ -30,7 +30,7 @@ Item {
return {}
}
readonly property bool forceOpen: (widgetSettings.forceOpen !== undefined) ? widgetSettings.forceOpen : widgetMetadata.forceOpen
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
// Use the shared service for keyboard layout
property string currentLayout: KeyboardLayoutService.currentLayout
@@ -47,9 +47,8 @@ Item {
autoHide: false // Important to be false so we can hover as long as we want
text: currentLayout.toUpperCase()
tooltipText: "Keyboard layout: " + currentLayout.toUpperCase()
forceOpen: root.forceOpen
fontSize: Style.fontSizeS // Use larger font size
forceOpen: root.displayMode === "forceOpen"
forceClose: root.displayMode === "alwaysHide"
onClicked: {
// You could open keyboard settings here if needed

View File

@@ -30,7 +30,8 @@ Item {
return {}
}
readonly property bool alwaysShowPercentage: (widgetSettings.alwaysShowPercentage !== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
readonly property bool isBarVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
// Used to avoid opening the pill on Quickshell startup
property bool firstInputVolumeReceived: false
@@ -87,13 +88,14 @@ Item {
NPill {
id: pill
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: getIcon()
autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.inputVolume * 100) + "%"
forceOpen: alwaysShowPercentage
tooltipText: "Microphone: " + Math.round(AudioService.inputVolume * 100) + "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."
text: Math.floor(AudioService.inputVolume * 100)
suffix: "%"
forceOpen: displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
tooltipText: "Microphone: " + Math.round(AudioService.inputVolume * 100) + "%\nLeft click to toggle mute.\nRight click for settings.\nScroll to modify volume."
onWheel: function (delta) {
wheelAccumulator += delta

View File

@@ -16,6 +16,5 @@ NIconButton {
sizeRatio: 0.8
colorBg: Color.mPrimary
colorFg: Color.mOnPrimary
anchors.verticalCenter: parent.verticalCenter
onClicked: ScreenRecorderService.toggleRecording()
}

View File

@@ -33,34 +33,23 @@ NIconButton {
icon: useDistroLogo ? "" : "noctalia"
tooltipText: "Open side panel."
sizeRatio: 0.8
sizeRatio: 0.85
colorBg: Color.mSurfaceVariant
colorFg: Color.mOnSurface
colorBgHover: useDistroLogo ? Color.mSurfaceVariant : Color.mTertiary
colorBorder: Color.transparent
colorBorderHover: Color.transparent
anchors.verticalCenter: parent.verticalCenter
colorBorderHover: useDistroLogo ? Color.mTertiary : Color.transparent
onClicked: PanelService.getPanel("sidePanel")?.toggle(this)
onRightClicked: PanelService.getPanel("settingsPanel")?.toggle()
IconImage {
id: logo
anchors.centerIn: parent
width: root.width * 0.6
width: root.width * 0.85
height: width
source: useDistroLogo ? DistroLogoService.osLogo : ""
visible: useDistroLogo && source !== ""
smooth: true
}
MultiEffect {
anchors.fill: logo
source: logo
//visible: logo.visible
colorization: 1
brightness: 1
saturation: 1
colorizationColor: root.hovering ? Color.mSurfaceVariant : Color.mOnSurface
}
}

View File

@@ -43,6 +43,26 @@ Rectangle {
radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant
// Compact speed formatter for vertical bar display
function formatCompactSpeed(bytesPerSecond) {
if (!bytesPerSecond || bytesPerSecond <= 0)
return "0"
const units = ["", "k", "M", "G"]
let value = bytesPerSecond
let unitIndex = 0
while (value >= 1024 && unitIndex < units.length - 1) {
value = value / 1024.0
unitIndex++
}
// Promote at ~100 of current unit (e.g., 100k -> ~0.1M shown as 0.1M or 0M if rounded)
if (unitIndex < units.length - 1 && value >= 100) {
value = value / 1024.0
unitIndex++
}
const display = (value >= 10) ? Math.round(value).toString() : value.toFixed(1)
return display + units[unitIndex]
}
// Horizontal layout for top/bottom bars
RowLayout {
id: horizontalLayout
@@ -347,21 +367,21 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NText {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: formatCompactSpeed(SystemStatService.rxSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
color: Color.mPrimary
}
NIcon {
icon: "download-speed"
font.pointSize: Style.fontSizeS * scaling
anchors.horizontalCenter: parent.horizontalCenter
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
}
}
@@ -377,21 +397,21 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NText {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
text: formatCompactSpeed(SystemStatService.txSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
color: Color.mPrimary
}
NIcon {
icon: "upload-speed"
font.pointSize: Style.fontSizeS * scaling
anchors.horizontalCenter: parent.horizontalCenter
}
NText {
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
}
}
@@ -407,12 +427,6 @@ Rectangle {
anchors.centerIn: parent
spacing: Style.marginXXS * scaling
NIcon {
icon: "storage"
font.pointSize: Style.fontSizeS * scaling
Layout.alignment: Qt.AlignHCenter
}
NText {
text: `${SystemStatService.diskPercent}%`
font.family: Settings.data.ui.fontFixed
@@ -422,6 +436,12 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter
color: Color.mPrimary
}
NIcon {
icon: "storage"
font.pointSize: Style.fontSizeS * scaling
Layout.alignment: Qt.AlignHCenter
}
}
}
}

View File

@@ -30,7 +30,8 @@ Item {
return {}
}
readonly property bool alwaysShowPercentage: (widgetSettings.alwaysShowPercentage !== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
readonly property bool isBarVertical: Settings.data.bar.position === "left" || Settings.data.bar.position === "right"
readonly property string displayMode: (widgetSettings.displayMode !== undefined) ? widgetSettings.displayMode : widgetMetadata.displayMode
// Used to avoid opening the pill on Quickshell startup
property bool firstVolumeReceived: false
@@ -76,9 +77,11 @@ Item {
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: getIcon()
autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.volume * 100) + "%"
forceOpen: alwaysShowPercentage
tooltipText: "Volume: " + Math.round(AudioService.volume * 100) + "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."
text: Math.floor(AudioService.volume * 100)
suffix: "%"
forceOpen: displayMode === "alwaysShow"
forceClose: displayMode === "alwaysHide"
tooltipText: "Volume: " + Math.round(AudioService.volume * 100) + "%\nLeft click to toggle mute.\nRight click for settings.\nScroll to modify volume."
onWheel: function (delta) {
wheelAccumulator += delta

View File

@@ -12,6 +12,7 @@ Scope {
property bool unlockInProgress: false
property bool showFailure: false
property string errorMessage: ""
property string infoMessage: ""
property bool pamAvailable: typeof PamContext !== "undefined"
onCurrentTextChanged: {
@@ -28,12 +29,6 @@ Scope {
return
}
if (currentText === "") {
errorMessage = "Password required"
showFailure = true
return
}
root.unlockInProgress = true
errorMessage = ""
showFailure = false
@@ -52,6 +47,8 @@ Scope {
if (messageIsError) {
errorMessage = message
} else {
infoMessage = message
}
if (responseRequired) {

View File

@@ -511,6 +511,7 @@ Loader {
width: 0
height: 0
visible: false
enabled: !lockContext.unlockInProgress
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
color: Color.mOnSurface
@@ -540,7 +541,7 @@ Loader {
color: Color.mOnSurface
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeL * scaling
visible: passwordInput.activeFocus
visible: passwordInput.activeFocus && !lockContext.unlockInProgress
SequentialAnimation {
id: typingEffect
@@ -584,7 +585,7 @@ Loader {
NText {
text: {
if (lockContext.unlockInProgress)
return "Authenticating..."
return lockContext.infoMessage || "Authenticating..."
if (lockContext.showFailure && lockContext.errorMessage)
return lockContext.errorMessage
if (lockContext.showFailure)

View File

@@ -19,7 +19,7 @@ Popup {
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: 420 * scaling
width: 500 * scaling
height: content.implicitHeight + padding * 2
padding: Style.marginXL * scaling
modal: true

View File

@@ -14,20 +14,36 @@ ColumnLayout {
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage !== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
property int valueWarningThreshold: widgetData.warningThreshold !== undefined ? widgetData.warningThreshold : widgetMetadata.warningThreshold
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
settings.displayMode = valueDisplayMode
settings.warningThreshold = valueWarningThreshold
return settings
}
NToggle {
label: "Always show percentage"
checked: root.valueAlwaysShowPercentage
onToggled: checked => root.valueAlwaysShowPercentage = checked
NComboBox {
label: "Display mode"
description: "Choose how you'd like this value to appear."
minimumWidth: 134 * scaling
model: ListModel {
ListElement {
key: "onhover"
name: "On Hover"
}
ListElement {
key: "alwaysShow"
name: "Always Show"
}
ListElement {
key: "alwaysHide"
name: "Always Hide"
}
}
currentKey: root.valueDisplayMode
onSelected: key => root.valueDisplayMode = key
}
NSpinBox {

View File

@@ -14,17 +14,33 @@ ColumnLayout {
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage !== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
settings.displayMode = valueDisplayMode
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
NComboBox {
label: "Display mode"
description: "Choose how you'd like this value to appear."
minimumWidth: 134 * scaling
model: ListModel {
ListElement {
key: "onhover"
name: "On Hover"
}
ListElement {
key: "alwaysShow"
name: "Always Show"
}
ListElement {
key: "alwaysHide"
name: "Always Hide"
}
}
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
}
}

View File

@@ -19,6 +19,8 @@ ColumnLayout {
settings.leftClickExec = leftClickExecInput.text
settings.rightClickExec = rightClickExecInput.text
settings.middleClickExec = middleClickExecInput.text
settings.textCommand = textCommandInput.text
settings.textIntervalMs = parseInt(textIntervalInput.text || textIntervalInput.placeholderText, 10)
return settings
}
@@ -228,4 +230,33 @@ ColumnLayout {
placeholderText: "Enter command to execute (app or custom script)"
text: widgetData.middleClickExec || widgetMetadata.middleClickExec
}
NDivider {
Layout.fillWidth: true
}
NText {
text: "Dynamic Text"
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
}
NTextInput {
id: textCommandInput
Layout.fillWidth: true
label: "Text Command"
description: "Shell command to run periodically (first line becomes the text)."
placeholderText: "echo \"Hello World\""
text: widgetData?.textCommand || widgetMetadata.textCommand
}
NTextInput {
id: textIntervalInput
Layout.fillWidth: true
label: "Refresh Interval"
description: "Interval in milliseconds."
placeholderText: String(widgetMetadata.textIntervalMs || 3000)
text: widgetData && widgetData.textIntervalMs !== undefined ? String(widgetData.textIntervalMs) : ""
}
}

View File

@@ -14,18 +14,33 @@ ColumnLayout {
property var widgetMetadata: null
// Local state
property bool valueForceOpen: widgetData.forceOpen !== undefined ? widgetData.forceOpen : widgetMetadata.forceOpen
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.forceOpen = valueForceOpen
settings.displayMode = valueDisplayMode
return settings
}
NToggle {
label: "Force open"
description: "Keep the keyboard layout widget always expanded."
checked: valueForceOpen
onToggled: checked => valueForceOpen = checked
NComboBox {
label: "Display mode"
description: "Choose how you'd like this value to appear."
minimumWidth: 134 * scaling
model: ListModel {
ListElement {
key: "onhover"
name: "On Hover"
}
ListElement {
key: "forceOpen"
name: "Force Open"
}
ListElement {
key: "alwaysHide"
name: "Always Hide"
}
}
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
}
}

View File

@@ -14,17 +14,33 @@ ColumnLayout {
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage !== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
settings.displayMode = valueDisplayMode
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
NComboBox {
label: "Display mode"
description: "Choose how you'd like this value to appear."
minimumWidth: 134 * scaling
model: ListModel {
ListElement {
key: "onhover"
name: "On Hover"
}
ListElement {
key: "alwaysShow"
name: "Always Show"
}
ListElement {
key: "alwaysHide"
name: "Always Hide"
}
}
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
}
}

View File

@@ -14,17 +14,33 @@ ColumnLayout {
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage !== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
property string valueDisplayMode: widgetData.displayMode !== undefined ? widgetData.displayMode : widgetMetadata.displayMode
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
settings.displayMode = valueDisplayMode
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
NComboBox {
label: "Display mode"
description: "Choose how you'd like this value to appear."
minimumWidth: 134 * scaling
model: ListModel {
ListElement {
key: "onhover"
name: "On Hover"
}
ListElement {
key: "alwaysShow"
name: "Always Show"
}
ListElement {
key: "alwaysHide"
name: "Always Hide"
}
}
currentKey: valueDisplayMode
onSelected: key => valueDisplayMode = key
}
}

View File

@@ -59,7 +59,7 @@ ColumnLayout {
// Update button
Rectangle {
Layout.alignment: Qt.alignmentRight
Layout.alignment: Qt.AlignRight
Layout.preferredWidth: Math.round(updateRow.implicitWidth + (Style.marginL * scaling * 2))
Layout.preferredHeight: Math.round(Style.barHeight * scaling)
radius: Style.radiusL * scaling
@@ -189,7 +189,7 @@ ColumnLayout {
NText {
text: modelData.login || "Unknown"
font.weight: Style.fontWeightBold
color: contributorArea.containsMouse ? Color.mSurface : Color.mOnSurface
color: contributorArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface
elide: Text.ElideRight
Layout.fillWidth: true
}
@@ -197,7 +197,7 @@ ColumnLayout {
NText {
text: (modelData.contributions || 0) + " " + ((modelData.contributions || 0) === 1 ? "commit" : "commits")
font.pointSize: Style.fontSizeXS * scaling
color: contributorArea.containsMouse ? Color.mSurface : Color.mOnSurface
color: contributorArea.containsMouse ? Color.mOnTertiary : Color.mOnSurface
}
}
}

View File

@@ -2,9 +2,9 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Services.Pipewire
import qs.Widgets
import qs.Commons
import qs.Services
import qs.Widgets
ColumnLayout {
id: root
@@ -34,37 +34,29 @@ ColumnLayout {
description: "System-wide volume level."
}
RowLayout {
// Pipewire seems a bit finicky, if we spam too many volume changes it breaks easily
// Probably because they have some quick fades in and out to avoid clipping
// We use a timer to space out the updates, to avoid lock up
Timer {
interval: Style.animationFast
running: true
repeat: true
onTriggered: {
if (Math.abs(localVolume - AudioService.volume) >= 0.01) {
AudioService.setVolume(localVolume)
}
// Pipewire seems a bit finicky, if we spam too many volume changes it breaks easily
// Probably because they have some quick fades in and out to avoid clipping
// We use a timer to space out the updates, to avoid lock up
Timer {
interval: Style.animationFast
running: true
repeat: true
onTriggered: {
if (Math.abs(localVolume - AudioService.volume) >= 0.01) {
AudioService.setVolume(localVolume)
}
}
}
NSlider {
Layout.fillWidth: true
from: 0
to: Settings.data.audio.volumeOverdrive ? 2.0 : 1.0
value: localVolume
stepSize: 0.01
onMoved: {
localVolume = value
}
}
NText {
text: Math.floor(AudioService.volume * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
NValueSlider {
Layout.fillWidth: true
from: 0
to: Settings.data.audio.volumeOverdrive ? 2.0 : 1.0
value: localVolume
stepSize: 0.01
text: Math.floor(AudioService.volume * 100) + "%"
onMoved: {
localVolume = value
}
}
}
@@ -96,24 +88,14 @@ ColumnLayout {
description: "Microphone input volume level."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1.0
value: AudioService.inputVolume
stepSize: 0.01
onMoved: {
AudioService.setInputVolume(value)
}
}
NText {
text: Math.floor(AudioService.inputVolume * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1.0
value: AudioService.inputVolume
stepSize: 0.01
text: Math.floor(AudioService.inputVolume * 100) + "%"
onMoved: value => AudioService.setInputVolume(value)
}
}
@@ -144,9 +126,7 @@ ColumnLayout {
value: Settings.data.audio.volumeStep
stepSize: 1
suffix: "%"
onValueChanged: {
Settings.data.audio.volumeStep = value
}
onValueChanged: Settings.data.audio.volumeStep = value
}
}

View File

@@ -82,29 +82,20 @@ ColumnLayout {
description: "Adjust the background opacity of the bar."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.backgroundOpacity
onMoved: Settings.data.bar.backgroundOpacity = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.bar.backgroundOpacity * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.backgroundOpacity
onMoved: value => Settings.data.bar.backgroundOpacity = value
text: Math.floor(Settings.data.bar.backgroundOpacity * 100) + "%"
}
}
NToggle {
Layout.fillWidth: true
label: "Floating Bar"
description: "Make the bar float with rounded corners and margins. This will hide screen corners."
description: "Make the bar float with rounded corners and margins. Screen corners will move to screen edges."
checked: Settings.data.bar.floating
onToggled: checked => Settings.data.bar.floating = checked
}
@@ -133,25 +124,14 @@ ColumnLayout {
color: Color.mOnSurfaceVariant
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginVertical
onMoved: Settings.data.bar.marginVertical = value
cutoutColor: Color.mSurface
}
NText {
text: Math.round(Settings.data.bar.marginVertical * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginXS * scaling
Layout.preferredWidth: 50
horizontalAlignment: Text.AlignRight
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginVertical
onMoved: value => Settings.data.bar.marginVertical = value
text: Math.round(Settings.data.bar.marginVertical * 100) + "%"
}
}
@@ -164,25 +144,14 @@ ColumnLayout {
color: Color.mOnSurfaceVariant
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginHorizontal
onMoved: Settings.data.bar.marginHorizontal = value
cutoutColor: Color.mSurface
}
NText {
text: Math.round(Settings.data.bar.marginHorizontal * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginXS * scaling
Layout.preferredWidth: 50
horizontalAlignment: Text.AlignRight
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginHorizontal
onMoved: value => Settings.data.bar.marginHorizontal = value
text: Math.round(Settings.data.bar.marginHorizontal * 100) + "%"
}
}
}

View File

@@ -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()
}
}
}
}
}

View File

@@ -116,21 +116,15 @@ ColumnLayout {
Layout.preferredWidth: 80 * scaling
}
NSlider {
NValueSlider {
id: scaleSlider
from: 0.7
to: 1.8
stepSize: 0.01
value: localScaling
onPressedChanged: ScalingService.setScreenScale(modelData, value)
Layout.fillWidth: true
Layout.minimumWidth: 200 * scaling
}
NText {
onPressedChanged: (pressed, value) => ScalingService.setScreenScale(modelData, value)
text: `${Math.round(localScaling * 100)}%`
Layout.preferredWidth: 50 * scaling
horizontalAlignment: Text.AlignRight
Layout.fillWidth: true
}
// Reset button container
@@ -165,24 +159,14 @@ ColumnLayout {
Layout.preferredWidth: 80 * scaling
}
NSlider {
NValueSlider {
Layout.fillWidth: true
Layout.minimumWidth: 200 * scaling
from: 0
to: 1
value: brightnessMonitor ? brightnessMonitor.brightness : 0.5
stepSize: 0.05
onPressedChanged: {
if (!pressed && brightnessMonitor) {
brightnessMonitor.setBrightness(value)
}
}
}
NText {
stepSize: 0.01
onPressedChanged: (pressed, value) => brightnessMonitor.setBrightness(value)
text: brightnessMonitor ? Math.round(brightnessMonitor.brightness * 100) + "%" : "N/A"
Layout.preferredWidth: 50 * scaling
horizontalAlignment: Text.AlignRight
}
// Empty container to match scale row layout
@@ -237,9 +221,7 @@ ColumnLayout {
value: Settings.data.brightness.brightnessStep
stepSize: 1
suffix: "%"
onValueChanged: {
Settings.data.brightness.brightnessStep = value
}
onValueChanged: Settings.data.brightness.brightnessStep = value
}
}
}

View File

@@ -49,24 +49,14 @@ ColumnLayout {
label: "Background Opacity"
description: "Adjust the background opacity."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: Settings.data.dock.backgroundOpacity = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: value => Settings.data.dock.backgroundOpacity = value
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
}
}
@@ -79,23 +69,14 @@ ColumnLayout {
description: "Adjust the floating distance from the screen edge."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 4
stepSize: 0.01
value: Settings.data.dock.floatingRatio
onMoved: Settings.data.dock.floatingRatio = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 4
stepSize: 0.01
value: Settings.data.dock.floatingRatio
onMoved: value => Settings.data.dock.floatingRatio = value
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
}
}

View File

@@ -74,23 +74,14 @@ ColumnLayout {
description: "Adjust the rounded border of all UI elements."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.general.radiusRatio
onMoved: Settings.data.general.radiusRatio = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.general.radiusRatio * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.general.radiusRatio
onMoved: value => Settings.data.general.radiusRatio = value
text: Math.floor(Settings.data.general.radiusRatio * 100) + "%"
}
}
@@ -104,23 +95,14 @@ ColumnLayout {
description: "Adjust global animation speed."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0.1
to: 2.0
stepSize: 0.01
value: Settings.data.general.animationSpeed
onMoved: Settings.data.general.animationSpeed = value
cutoutColor: Color.mSurface
}
NText {
text: Math.round(Settings.data.general.animationSpeed * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0.1
to: 2.0
stepSize: 0.01
value: Settings.data.general.animationSpeed
onMoved: value => Settings.data.general.animationSpeed = value
text: Math.round(Settings.data.general.animationSpeed * 100) + "%"
}
}
}
@@ -147,6 +129,13 @@ ColumnLayout {
onToggled: checked => Settings.data.general.showScreenCorners = checked
}
NToggle {
label: "Solid Black Corners"
description: "Force screen corners to always render as solid black."
checked: Settings.data.general.forceBlackScreenCorners
onToggled: checked => Settings.data.general.forceBlackScreenCorners = checked
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
@@ -156,23 +145,14 @@ ColumnLayout {
description: "Adjust the rounded corners of the screen."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.screenRadiusRatio
onMoved: Settings.data.general.screenRadiusRatio = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.general.screenRadiusRatio * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.screenRadiusRatio
onMoved: value => Settings.data.general.screenRadiusRatio = value
text: Math.floor(Settings.data.general.screenRadiusRatio * 100) + "%"
}
}
}

View File

@@ -74,24 +74,15 @@ ColumnLayout {
Layout.fillWidth: true
}
RowLayout {
NSlider {
id: launcherBgOpacity
Layout.fillWidth: true
from: 0.0
to: 1.0
stepSize: 0.01
value: Settings.data.appLauncher.backgroundOpacity
onMoved: Settings.data.appLauncher.backgroundOpacity = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.appLauncher.backgroundOpacity * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
id: launcherBgOpacity
Layout.fillWidth: true
from: 0.0
to: 1.0
stepSize: 0.01
value: Settings.data.appLauncher.backgroundOpacity
onMoved: value => Settings.data.appLauncher.backgroundOpacity = value
text: Math.floor(Settings.data.appLauncher.backgroundOpacity * 100) + "%"
}
}

View File

@@ -100,23 +100,14 @@ ColumnLayout {
description: "How long low priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.lowUrgencyDuration
onMoved: Settings.data.notifications.lowUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.lowUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.lowUrgencyDuration
onMoved: value => Settings.data.notifications.lowUrgencyDuration = value
text: Settings.data.notifications.lowUrgencyDuration + "s"
}
}
@@ -130,23 +121,14 @@ ColumnLayout {
description: "How long normal priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.normalUrgencyDuration
onMoved: Settings.data.notifications.normalUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.normalUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.normalUrgencyDuration
onMoved: value => Settings.data.notifications.normalUrgencyDuration = value
text: Settings.data.notifications.normalUrgencyDuration + "s"
}
}
@@ -160,23 +142,14 @@ ColumnLayout {
description: "How long critical priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.criticalUrgencyDuration
onMoved: Settings.data.notifications.criticalUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.criticalUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.criticalUrgencyDuration
onMoved: value => Settings.data.notifications.criticalUrgencyDuration = value
text: Settings.data.notifications.criticalUrgencyDuration + "s"
}
}
}

View File

@@ -19,7 +19,6 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
Layout.topMargin: Style.marginS * scaling
NTextInput {
label: "Output Directory"

View File

@@ -138,21 +138,14 @@ ColumnLayout {
description: "Duration of transition animations in seconds."
}
RowLayout {
spacing: Style.marginL * scaling
NSlider {
Layout.fillWidth: true
from: 500
to: 10000
stepSize: 100
value: Settings.data.wallpaper.transitionDuration
onMoved: Settings.data.wallpaper.transitionDuration = value
cutoutColor: Color.mSurface
}
NText {
text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s"
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
}
NValueSlider {
Layout.fillWidth: true
from: 500
to: 10000
stepSize: 100
value: Settings.data.wallpaper.transitionDuration
onMoved: value => Settings.data.wallpaper.transitionDuration = value
text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(1) + "s"
}
}
@@ -163,20 +156,13 @@ ColumnLayout {
description: "Duration of transition animations in seconds."
}
RowLayout {
spacing: Style.marginL * scaling
NSlider {
Layout.fillWidth: true
from: 0.0
to: 1.0
value: Settings.data.wallpaper.transitionEdgeSmoothness
onMoved: Settings.data.wallpaper.transitionEdgeSmoothness = value
cutoutColor: Color.mSurface
}
NText {
text: Math.round(Settings.data.wallpaper.transitionEdgeSmoothness * 100) + "%"
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
}
NValueSlider {
Layout.fillWidth: true
from: 0.0
to: 1.0
value: Settings.data.wallpaper.transitionEdgeSmoothness
onMoved: value => Settings.data.wallpaper.transitionEdgeSmoothness = value
text: Math.round(Settings.data.wallpaper.transitionEdgeSmoothness * 100) + "%"
}
}
}

View File

@@ -258,7 +258,6 @@ NBox {
stepSize: 0
snapAlways: false
enabled: MediaService.trackLength > 0 && MediaService.canSeek
cutoutColor: Color.mSurface
heightRatio: 0.65
onMoved: {

View File

@@ -77,7 +77,7 @@ https://github.com/user-attachments/assets/72c6d6dc-48b0-48a0-bd8b-c7e70990edc4
- `quickshell-git` - Core shell framework
- `ttf-roboto` - The default font used for most of the UI
- `inter-font` - The default font used for Headers (ex: clock on the LockScreen)
- `gpu-screen-recorder` - Screen recording functionality
- `gpu-screen-recorder` - Screen recording functionality (Flatpak also supported)
- `brightnessctl` - For internal/laptop monitor brightness
- `ddcutil` - For desktop monitor brightness (might introduce some system instability with certain monitors)

View File

@@ -43,12 +43,12 @@ Singleton {
},
"Battery": {
"allowUserSettings": true,
"alwaysShowPercentage": false,
"displayMode": "onhover",
"warningThreshold": 30
},
"Brightness": {
"allowUserSettings": true,
"alwaysShowPercentage": false
"displayMode": "onhover"
},
"Clock": {
"allowUserSettings": true,
@@ -61,11 +61,13 @@ Singleton {
"icon": "heart",
"leftClickExec": "",
"rightClickExec": "",
"middleClickExec": ""
"middleClickExec": "",
"textCommand": "",
"textIntervalMs": 3000
},
"Microphone": {
"allowUserSettings": true,
"alwaysShowPercentage": false
"displayMode": "onhover"
},
"NotificationHistory": {
"allowUserSettings": true,
@@ -102,11 +104,11 @@ Singleton {
},
"Volume": {
"allowUserSettings": true,
"alwaysShowPercentage": false
"displayMode": "onhover"
},
"KeyboardLayout": {
"allowUserSettings": true,
"forceOpen": false
"displayMode": "onhover"
}
})

View 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()
}
}

View File

@@ -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

View File

@@ -8,7 +8,7 @@ Singleton {
id: root
// Public properties
property string baseVersion: "2.9.0"
property string baseVersion: "2.9.2"
property bool isDevelopment: false
property string currentVersion: `v${!isDevelopment ? baseVersion : baseVersion + "-dev"}`

193
Widgets/NCollapsible.qml Normal file
View 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
}
}

View File

@@ -243,24 +243,19 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: redSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.r * 255)
onMoved: {
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1)
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(redSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(value / 255, root.selectedColor.g, root.selectedColor.b, 1)
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@@ -274,25 +269,20 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: greenSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.g * 255)
onMoved: {
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(greenSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, value / 255, root.selectedColor.b, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@@ -306,25 +296,20 @@ Popup {
Layout.preferredWidth: 20 * scaling
}
NSlider {
NValueSlider {
id: blueSlider
Layout.fillWidth: true
from: 0
to: 255
value: Math.round(root.selectedColor.b * 255)
onMoved: {
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
}
NText {
text: Math.round(blueSlider.value)
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 30 * scaling
onMoved: value => {
root.selectedColor = Qt.rgba(root.selectedColor.r, root.selectedColor.g, value / 255, 1)
// Update stored hue and saturation when RGB changes
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
root.currentHue = hsv[0]
root.currentSaturation = hsv[1]
}
text: Math.round(value)
}
}
@@ -338,7 +323,7 @@ Popup {
Layout.preferredWidth: 80 * scaling
}
NSlider {
NValueSlider {
id: brightnessSlider
Layout.fillWidth: true
from: 0
@@ -347,27 +332,22 @@ Popup {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
return hsv[2]
}
onMoved: {
var hue = root.currentHue
var saturation = root.currentSaturation
onMoved: value => {
var hue = root.currentHue
var saturation = root.currentSaturation
if (hue === 0 && saturation === 0) {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
hue = hsv[0]
saturation = hsv[1]
root.currentHue = hue
root.currentSaturation = saturation
}
if (hue === 0 && saturation === 0) {
var hsv = root.rgbToHsv(root.selectedColor.r * 255, root.selectedColor.g * 255, root.selectedColor.b * 255)
hue = hsv[0]
saturation = hsv[1]
root.currentHue = hue
root.currentSaturation = saturation
}
var rgb = root.hsvToRgb(hue, saturation, value)
root.selectedColor = Qt.rgba(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1)
}
}
NText {
var rgb = root.hsvToRgb(hue, saturation, value)
root.selectedColor = Qt.rgba(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, 1)
}
text: Math.round(brightnessSlider.value) + "%"
font.family: Settings.data.ui.fontFixed
Layout.preferredWidth: 40 * scaling
}
}
}

View File

@@ -8,14 +8,15 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
property bool disableOpen: false
property bool rightOpen: false
property bool hovered: false
property real fontSize: Style.fontSizeXS
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
@@ -43,15 +44,15 @@ Item {
NPillVertical {
icon: root.icon
text: root.text
suffix: root.suffix
tooltipText: root.tooltipText
sizeRatio: root.sizeRatio
autoHide: root.autoHide
forceOpen: root.forceOpen
forceClose: root.forceClose
disableOpen: root.disableOpen
rightOpen: root.rightOpen
hovered: root.hovered
fontSize: root.fontSize
onShown: root.shown()
onHidden: root.hidden()
onEntered: root.entered()
@@ -59,7 +60,7 @@ Item {
onClicked: root.clicked()
onRightClicked: root.rightClicked()
onMiddleClicked: root.middleClicked()
onWheel: root.wheel
onWheel: delta => root.wheel(delta)
}
}
@@ -68,15 +69,15 @@ Item {
NPillHorizontal {
icon: root.icon
text: root.text
suffix: root.suffix
tooltipText: root.tooltipText
sizeRatio: root.sizeRatio
autoHide: root.autoHide
forceOpen: root.forceOpen
forceClose: root.forceClose
disableOpen: root.disableOpen
rightOpen: root.rightOpen
hovered: root.hovered
fontSize: root.fontSize
onShown: root.shown()
onHidden: root.hidden()
onEntered: root.entered()
@@ -84,7 +85,7 @@ Item {
onClicked: root.clicked()
onRightClicked: root.rightClicked()
onMiddleClicked: root.middleClicked()
onWheel: root.wheel
onWheel: delta => root.wheel(delta)
}
}
}

View File

@@ -8,24 +8,17 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
property bool disableOpen: false
property bool rightOpen: false
property bool hovered: false
property real fontSize: Style.fontSizeXS
// Bar position detection for pill direction
readonly property string barPosition: Settings.data.bar.position
readonly property bool isVerticalBar: barPosition === "left" || barPosition === "right"
// Determine pill direction based on section position
readonly property bool openRightward: rightOpen
readonly property bool openLeftward: !rightOpen
// Effective shown state (true if animated open or forced)
// Effective shown state (true if hovered/animated open or forced)
readonly property bool revealed: forceOpen || showPill
signal shown
@@ -41,51 +34,51 @@ Item {
property bool showPill: false
property bool shouldAnimateHide: false
// Sizing logic for horizontal bars
// Exposed width logic
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling)
readonly property int pillWidth: iconSize
readonly property int pillPaddingHorizontal: Style.marginS * scaling
readonly property int pillPaddingVertical: Style.marginS * scaling
readonly property int pillHeight: iconSize
readonly property int pillPaddingHorizontal: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 4)
readonly property int maxPillHeight: iconSize
readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap)
// For horizontal bars: height is just icon size, width includes pill space
width: revealed ? (openRightward ? (iconSize + maxPillWidth - pillOverlap) : (iconSize + maxPillWidth - pillOverlap)) : iconSize
height: iconSize
width: iconSize + Math.max(0, pill.width - pillOverlap)
height: pillHeight
Rectangle {
id: pill
width: revealed ? maxPillWidth : 1
height: revealed ? maxPillHeight : 1
height: pillHeight
// Position based on direction - center the pill relative to the icon
x: openLeftward ? (iconCircle.x + iconCircle.width / 2 - width) : (iconCircle.x + iconCircle.width / 2 - pillOverlap)
y: 0
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
(iconCircle.x + iconCircle.width / 2) - width // Opens left
opacity: revealed ? Style.opacityFull : Style.opacityNone
color: Color.mSurfaceVariant
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Radius logic for horizontal expansion - rounded on the side that connects to icon
topLeftRadius: openLeftward ? iconSize * 0.5 : 0
bottomLeftRadius: openLeftward ? iconSize * 0.5 : 0
topRightRadius: openRightward ? iconSize * 0.5 : 0
bottomRightRadius: openRightward ? iconSize * 0.5 : 0
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
topRightRadius: rightOpen ? pillHeight * 0.5 : 0
bottomRightRadius: rightOpen ? pillHeight * 0.5 : 0
anchors.verticalCenter: parent.verticalCenter
NText {
id: textItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: openLeftward ? -6 * scaling : 6 * scaling // Adjust based on opening direction
text: root.text
x: {
// Better text horizontal centering
var centerX = (parent.width - width) / 2
var offset = rightOpen ? Style.marginXS * scaling : -Style.marginXS * scaling
if (forceOpen) {
// If its force open, the icon disc background is the same color as the bg pill move text slightly
offset += rightOpen ? -Style.marginXXS * scaling : Style.marginXXS * scaling
}
return centerX + offset
}
text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
}
@@ -96,13 +89,6 @@ Item {
easing.type: Easing.OutCubic
}
}
Behavior on height {
enabled: showAnim.running || hideAnim.running
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Behavior on opacity {
enabled: showAnim.running || hideAnim.running
NumberAnimation {
@@ -117,13 +103,11 @@ Item {
width: iconSize
height: iconSize
radius: width * 0.5
color: hovered && !forceOpen ? Color.mTertiary : Color.mSurfaceVariant
// Icon positioning based on direction
x: openLeftward ? (parent.width - width) : 0
y: 0
color: hovered ? Color.mTertiary : Color.mSurfaceVariant
anchors.verticalCenter: parent.verticalCenter
x: rightOpen ? 0 : (parent.width - width)
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
@@ -134,7 +118,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: Style.fontSizeM * scaling
color: hovered && !forceOpen ? Color.mOnTertiary : Color.mOnSurface
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
// Center vertically accounting for font metrics
@@ -153,14 +137,6 @@ Item {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
NumberAnimation {
target: pill
property: "height"
from: 1
to: maxPillHeight
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
NumberAnimation {
target: pill
property: "opacity"
@@ -202,14 +178,6 @@ Item {
duration: Style.animationNormal
easing.type: Easing.InCubic
}
NumberAnimation {
target: pill
property: "height"
from: maxPillHeight
to: 1
duration: Style.animationNormal
easing.type: Easing.InCubic
}
NumberAnimation {
target: pill
property: "opacity"
@@ -227,12 +195,10 @@ Item {
NTooltip {
id: tooltip
target: pill
text: root.tooltipText
positionLeft: barPosition === "right"
positionRight: barPosition === "left"
positionAbove: Settings.data.bar.position === "bottom"
target: pill
delay: Style.tooltipDelayLong
text: root.tooltipText
}
Timer {
@@ -277,9 +243,7 @@ Item {
root.middleClicked()
}
}
onWheel: wheel => {
root.wheel(wheel.angleDelta.y)
}
onWheel: wheel => root.wheel(wheel.angleDelta.y)
}
function show() {

View File

@@ -8,14 +8,15 @@ Item {
property string icon: ""
property string text: ""
property string suffix: ""
property string tooltipText: ""
property real sizeRatio: 0.8
property bool autoHide: false
property bool forceOpen: false
property bool forceClose: false
property bool disableOpen: false
property bool rightOpen: false
property bool hovered: false
property real fontSize: Style.fontSizeXS
// Bar position detection for pill direction
readonly property string barPosition: Settings.data.bar.position
@@ -25,8 +26,8 @@ Item {
readonly property bool openDownward: rightOpen
readonly property bool openUpward: !rightOpen
// Effective shown state (true if animated open or forced)
readonly property bool revealed: forceOpen || showPill
// Effective shown state (true if animated open or forced, but not if force closed)
readonly property bool revealed: !forceClose && (forceOpen || showPill)
signal shown
signal hidden
@@ -44,11 +45,10 @@ Item {
// Sizing logic for vertical bars
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling)
readonly property int pillHeight: iconSize
readonly property int pillPaddingHorizontal: Style.marginS * scaling
readonly property int pillPaddingVertical: Style.marginS * scaling
readonly property int pillPaddingVertical: 3 * 2 * scaling // Very precise adjustment don't replace by Style.margin
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: iconSize
readonly property int maxPillHeight: Math.max(1, textItem.implicitHeight + pillPaddingVertical * 3)
readonly property int maxPillHeight: Math.max(1, textItem.implicitHeight + pillPaddingVertical * 4)
// For vertical bars: width is just icon size, height includes pill space
width: iconSize
@@ -65,8 +65,6 @@ Item {
opacity: revealed ? Style.opacityFull : Style.opacityNone
color: Color.mSurfaceVariant
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
// Radius logic for vertical expansion - rounded on the side that connects to icon
topLeftRadius: openUpward ? iconSize * 0.5 : 0
@@ -76,15 +74,25 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
NTextVertical {
NText {
id: textItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: openUpward ? -6 * scaling : 6 * scaling // Adjust based on opening direction
text: root.text
fontSize: Style.fontSizeXXS * scaling
fontWeight: Style.fontWeightBold
color: Color.mOnSurface
anchors.verticalCenterOffset: {
var offset = openDownward ? pillPaddingVertical * 0.75 : -pillPaddingVertical * 0.75
if (forceOpen) {
// If its force open, the icon disc background is the same color as the bg pill move text slightly
offset += rightOpen ? -Style.marginXXS * scaling : Style.marginXXS * scaling
}
return offset
}
text: root.text + root.suffix
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeXXS * scaling
font.weight: Style.fontWeightMedium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: forceOpen ? Color.mOnSurface : Color.mPrimary
visible: revealed
}
@@ -116,7 +124,7 @@ Item {
width: iconSize
height: iconSize
radius: width * 0.5
color: hovered && !forceOpen ? Color.mTertiary : Color.mSurfaceVariant
color: hovered ? Color.mTertiary : Color.mSurfaceVariant
// Icon positioning based on direction
x: 0
@@ -133,7 +141,7 @@ Item {
NIcon {
icon: root.icon
font.pointSize: Style.fontSizeM * scaling
color: hovered && !forceOpen ? Color.mOnTertiary : Color.mOnSurface
color: hovered ? Color.mOnTertiary : Color.mOnSurface
// Center horizontally
x: (iconCircle.width - width) / 2
// Center vertically accounting for font metrics
@@ -252,7 +260,7 @@ Item {
hovered = true
root.entered()
tooltip.show()
if (disableOpen) {
if (disableOpen || forceClose) {
return
}
if (!forceOpen) {
@@ -262,7 +270,7 @@ Item {
onExited: {
hovered = false
root.exited()
if (!forceOpen) {
if (!forceOpen && !forceClose) {
hide()
}
tooltip.hide()
@@ -276,9 +284,7 @@ Item {
root.middleClicked()
}
}
onWheel: wheel => {
root.wheel(wheel.angleDelta.y)
}
onWheel: wheel => root.wheel(wheel.angleDelta.y)
}
function show() {

View File

@@ -7,13 +7,12 @@ import qs.Services
Slider {
id: root
// Optional color to cut the track beneath the knob (should match surrounding background)
property var cutoutColor
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real heightRatio: 0.75
readonly property real knobDiameter: Math.round(Style.baseWidgetSize * heightRatio * scaling)
readonly property real trackHeight: knobDiameter * 0.5
readonly property real trackHeight: knobDiameter * 0.4
readonly property real cutoutExtra: Math.round(Style.baseWidgetSize * 0.1 * scaling)
snapMode: snapAlways ? Slider.SnapAlways : Slider.SnapOnRelease
@@ -26,15 +25,54 @@ Slider {
implicitHeight: trackHeight
width: root.availableWidth
height: implicitHeight
radius: height / 2
color: Color.mSurface
radius: 0
color: Qt.alpha(Color.mSurface, 0.5)
border.color: Qt.alpha(Color.mOutline, 0.5)
border.width: Math.max(1, Style.borderS * scaling)
// Animated gradient active track
Rectangle {
id: activeTrack
width: root.visualPosition * parent.width
height: parent.height
color: Color.mPrimary
radius: parent.radius
// Animated gradient fill
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
position: 0.0
color: Qt.darker(Color.mPrimary, 1.2)
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
GradientStop {
position: 0.5
color: Color.mPrimary
SequentialAnimation on position {
loops: Animation.Infinite
NumberAnimation {
from: 0.3
to: 0.7
duration: 2000
easing.type: Easing.InOutSine
}
NumberAnimation {
from: 0.7
to: 0.3
duration: 2000
easing.type: Easing.InOutSine
}
}
}
GradientStop {
position: 1.0
color: Qt.lighter(Color.mPrimary, 1.2)
}
}
}
// Circular cutout
@@ -44,8 +82,7 @@ Slider {
height: knobDiameter + cutoutExtra
radius: width / 2
color: root.cutoutColor !== undefined ? root.cutoutColor : Color.mSurface
x: Math.max(0, Math.min(parent.width - width, Math.round(root.visualPosition * (parent.width - root.knobDiameter) - cutoutExtra / 2)))
y: (parent.height - height) / 2
x: root.leftPadding + root.visualPosition * (root.availableWidth - root.knobDiameter) - cutoutExtra / 2
anchors.verticalCenter: parent.verticalCenter
}
}

View File

@@ -1,61 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services
import qs.Widgets
RowLayout {
id: root
// Properties that mirror NSlider
property real from: 0
property real to: 1
property real value: 0
property real stepSize: 0.01
property var cutoutColor
property bool snapAlways: true
property real heightRatio: 0.75
property bool showPercentage: true
property string suffix: "%"
property int decimalPlaces: 0 // 0 for integers, 1 for one decimal place, etc.
// Signals
signal moved(real value)
signal pressedChanged(bool pressed)
spacing: Style.marginS * scaling
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.heightRatio
stableWidth: true
minWidth: 200 * scaling
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed)
}
NText {
id: percentageLabel
visible: root.showPercentage
text: {
if (root.decimalPlaces === 0) {
return Math.round(slider.value * 100) + root.suffix
} else {
return (slider.value * 100).toFixed(root.decimalPlaces) + root.suffix
}
}
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
Layout.preferredWidth: 50 * scaling
horizontalAlignment: Text.AlignRight
}
}

View File

@@ -1,26 +0,0 @@
import QtQuick
import qs.Commons
import qs.Services
Column {
id: root
property string text: ""
property real fontSize: Style.fontSizeXS
property color color: Color.mOnSurface
property int fontWeight: Style.fontWeightBold
spacing: -2 * scaling
Repeater {
model: root.text.split("")
NText {
text: modelData
font.family: Settings.data.ui.fontFixed
font.pointSize: root.fontSize
font.weight: root.fontWeight
color: root.color
horizontalAlignment: Text.AlignHCenter
}
}
}

48
Widgets/NValueSlider.qml Normal file
View File

@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services
import qs.Widgets
RowLayout {
id: root
property real from: 0
property real to: 1
property real value: 0
property real stepSize: 0.01
property var cutoutColor: Color.mSurface
property bool snapAlways: true
property real heightRatio: 0.75
property string text: ""
// Signals
signal moved(real value)
signal pressedChanged(bool pressed, real value)
spacing: Style.marginL * scaling
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.heightRatio
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed, value)
}
NText {
visible: root.text !== ""
text: root.text
font.family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 40 * scaling
horizontalAlignment: Text.AlignRight
}
}

View File

@@ -1,5 +1,6 @@
{
description = "Noctalia shell - a Wayland desktop shell built with Quickshell";
description =
"Noctalia shell - a Wayland desktop shell built with Quickshell";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
@@ -11,22 +12,13 @@
};
};
outputs =
{
self,
nixpkgs,
systems,
quickshell,
...
}:
let
eachSystem = nixpkgs.lib.genAttrs (import systems);
in
{
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
outputs = { self, nixpkgs, systems, quickshell, ... }:
let eachSystem = nixpkgs.lib.genAttrs (import systems);
in {
formatter =
eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
packages = eachSystem (
system:
packages = eachSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
qs = quickshell.packages.${system}.default.override {
@@ -34,8 +26,7 @@
withI3 = false;
};
runtimeDeps =
with pkgs;
runtimeDeps = with pkgs;
[
bash
bluez
@@ -50,34 +41,21 @@
matugen
networkmanager
wl-clipboard
]
++ lib.optionals (pkgs.stdenv.hostPlatform.isx86_64) [
gpu-screen-recorder
];
] ++ lib.optionals (pkgs.stdenv.hostPlatform.isx86_64)
[ gpu-screen-recorder ];
fontconfig = pkgs.makeFontsConf {
fontDirectories = [
pkgs.roboto
pkgs.inter-nerdfont
];
fontDirectories = [ pkgs.roboto pkgs.inter-nerdfont ];
};
in
{
in {
default = pkgs.stdenv.mkDerivation {
pname = "noctalia-shell";
version = self.rev or self.dirtyRev or "dirty";
src = ./.;
nativeBuildInputs = [
pkgs.gcc
pkgs.makeWrapper
pkgs.qt6.wrapQtAppsHook
];
buildInputs = [
qs
pkgs.xkeyboard-config
pkgs.qt6.qtbase
];
nativeBuildInputs =
[ pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook ];
buildInputs = [ qs pkgs.xkeyboard_config pkgs.qt6.qtbase ];
propagatedBuildInputs = runtimeDeps;
installPhase = ''
@@ -91,14 +69,14 @@
'';
meta = {
description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
description =
"A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell.";
homepage = "https://github.com/noctalia-dev/noctalia-shell";
license = pkgs.lib.licenses.mit;
mainProgram = "noctalia-shell";
};
};
}
);
});
defaultPackage = eachSystem (system: self.packages.${system}.default);
};