mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
899595ec5c | ||
|
|
dbf1020636 | ||
|
|
7def695c0e | ||
|
|
b51a87a981 | ||
|
|
3da2682111 | ||
|
|
758f2f2e55 | ||
|
|
26a27c3393 | ||
|
|
9d9bfb54e1 | ||
|
|
ab5b1e4d82 | ||
|
|
8cb9f04a22 | ||
|
|
22bc5a3bff | ||
|
|
f5982f41a2 | ||
|
|
9a80d51b10 | ||
|
|
fa838ecdb1 | ||
|
|
c0d6780c3d | ||
|
|
8935f9a0f9 | ||
|
|
b8b97c46a0 | ||
|
|
f2bbf70f93 | ||
|
|
ecf468f78f | ||
|
|
0b35fc1d2d | ||
|
|
94c5d73a61 | ||
|
|
f399a6d9f5 | ||
|
|
44fd859aec | ||
|
|
53d0c3943d | ||
|
|
d80f923802 | ||
|
|
1b861d7b7b | ||
|
|
65933208ec | ||
|
|
5df218a789 | ||
|
|
2f7a834b55 | ||
|
|
eca301553e | ||
|
|
91efa38101 | ||
|
|
acfe94f736 | ||
|
|
97bfcbb9e8 | ||
|
|
9e47d91be2 | ||
|
|
8872002225 | ||
|
|
519a85b251 | ||
|
|
5aa7ff7e91 | ||
|
|
5ce5659b38 | ||
|
|
00459606ce | ||
|
|
da1081700a | ||
|
|
7e965262f5 | ||
|
|
19312d94c3 | ||
|
|
118323e6b5 | ||
|
|
aed81e82b0 | ||
|
|
76c167c2c2 | ||
|
|
852e2fa4d1 | ||
|
|
17dceffff6 | ||
|
|
b589f37e0b | ||
|
|
682c6af231 | ||
|
|
d71226c6bd | ||
|
|
1f3725faf8 | ||
|
|
efcec1b2f9 | ||
|
|
651322aef0 | ||
|
|
5ca2c2a095 | ||
|
|
b9ae772987 | ||
|
|
4265290a0f |
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
34
Assets/ColorScheme/Everforest.json
Normal file
34
Assets/ColorScheme/Everforest.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ Singleton {
|
||||
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/vesktop.css"')
|
||||
lines.push('output_path = "~/.config/vesktop/themes/noctalia.theme.css"')
|
||||
}
|
||||
if (Settings.data.matugen.pywalfox) {
|
||||
lines.push("\n[templates.pywalfox]")
|
||||
lines.push('input_path = "' + Quickshell.shellDir + '/Assets/Matugen/templates/pywalfox.json"')
|
||||
lines.push('output_path = "~/.cache/wal/colors.json"')
|
||||
lines.push('post_hook = "pywalfox update"')
|
||||
}
|
||||
|
||||
return lines.join("\n") + "\n"
|
||||
}
|
||||
|
||||
22
Assets/Matugen/templates/pywalfox.json
Normal file
22
Assets/Matugen/templates/pywalfox.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"wallpaper": "{{image}}",
|
||||
"alpha": "100",
|
||||
"colors": {
|
||||
"color0": "{{colors.background.default.hex}}",
|
||||
"color1": "",
|
||||
"color2": "",
|
||||
"color3": "",
|
||||
"color4": "",
|
||||
"color5": "",
|
||||
"color6": "",
|
||||
"color7": "",
|
||||
"color8": "",
|
||||
"color9": "",
|
||||
"color10": "{{colors.primary.default.hex}}",
|
||||
"color11": "",
|
||||
"color12": "",
|
||||
"color13": "{{colors.surface_bright.default.hex}}",
|
||||
"color14": "",
|
||||
"color15": "{{colors.on_surface.default.hex}}"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ Variants {
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: bgImage
|
||||
autoPaddingEnabled: false
|
||||
blurEnabled: true
|
||||
blur: 0.48
|
||||
blurMax: 128
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,6 +16,5 @@ NIconButton {
|
||||
sizeRatio: 0.8
|
||||
colorBg: Color.mPrimary
|
||||
colorFg: Color.mOnPrimary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: ScreenRecorderService.toggleRecording()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) : ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ ColumnLayout {
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginS * scaling
|
||||
|
||||
NTextInput {
|
||||
label: "Output Directory"
|
||||
|
||||
@@ -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) + "%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,6 @@ NBox {
|
||||
stepSize: 0
|
||||
snapAlways: false
|
||||
enabled: MediaService.trackLength > 0 && MediaService.canSeek
|
||||
cutoutColor: Color.mSurface
|
||||
heightRatio: 0.65
|
||||
|
||||
onMoved: {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
118
Services/ProgramCheckerService.qml
Normal file
118
Services/ProgramCheckerService.qml
Normal file
@@ -0,0 +1,118 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
|
||||
// Service to check if various programs are available on the system
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Program availability properties
|
||||
property bool matugenAvailable: false
|
||||
property bool pywalfoxAvailable: false
|
||||
property bool kittyAvailable: false
|
||||
property bool ghosttyAvailable: false
|
||||
property bool footAvailable: false
|
||||
property bool fuzzelAvailable: false
|
||||
property bool vesktopAvailable: false
|
||||
property bool gpuScreenRecorderAvailable: false
|
||||
|
||||
// Signal emitted when all checks are complete
|
||||
signal checksCompleted
|
||||
|
||||
// Programs to check - maps property names to commands
|
||||
readonly property var programsToCheck: ({
|
||||
"matugenAvailable": ["which", "matugen"],
|
||||
"pywalfoxAvailable": ["which", "pywalfox"],
|
||||
"kittyAvailable": ["which", "kitty"],
|
||||
"ghosttyAvailable": ["which", "ghostty"],
|
||||
"footAvailable": ["which", "foot"],
|
||||
"fuzzelAvailable": ["which", "fuzzel"],
|
||||
"vesktopAvailable": ["which", "vesktop"],
|
||||
"gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"]
|
||||
})
|
||||
|
||||
// Internal tracking
|
||||
property int completedChecks: 0
|
||||
property int totalChecks: Object.keys(programsToCheck).length
|
||||
|
||||
// Single reusable Process object
|
||||
Process {
|
||||
id: checker
|
||||
running: false
|
||||
|
||||
property string currentProperty: ""
|
||||
|
||||
onExited: function (exitCode) {
|
||||
// Set the availability property
|
||||
root[currentProperty] = (exitCode === 0)
|
||||
|
||||
// Stop the process to free resources
|
||||
running = false
|
||||
|
||||
// Track completion
|
||||
root.completedChecks++
|
||||
|
||||
// Check next program or emit completion signal
|
||||
if (root.completedChecks >= root.totalChecks) {
|
||||
root.checksCompleted()
|
||||
} else {
|
||||
root.checkNextProgram()
|
||||
}
|
||||
}
|
||||
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
// Queue of programs to check
|
||||
property var checkQueue: []
|
||||
property int currentCheckIndex: 0
|
||||
|
||||
// Function to check the next program in the queue
|
||||
function checkNextProgram() {
|
||||
if (currentCheckIndex >= checkQueue.length)
|
||||
return
|
||||
|
||||
var propertyName = checkQueue[currentCheckIndex]
|
||||
var command = programsToCheck[propertyName]
|
||||
|
||||
checker.currentProperty = propertyName
|
||||
checker.command = command
|
||||
checker.running = true
|
||||
|
||||
currentCheckIndex++
|
||||
}
|
||||
|
||||
// Function to run all program checks
|
||||
function checkAllPrograms() {
|
||||
// Reset state
|
||||
completedChecks = 0
|
||||
currentCheckIndex = 0
|
||||
checkQueue = Object.keys(programsToCheck)
|
||||
|
||||
// Start first check
|
||||
if (checkQueue.length > 0) {
|
||||
checkNextProgram()
|
||||
}
|
||||
}
|
||||
|
||||
// Function to check a specific program
|
||||
function checkProgram(programProperty) {
|
||||
if (!programsToCheck.hasOwnProperty(programProperty)) {
|
||||
Logger.warn("ProgramChecker", "Unknown program property:", programProperty)
|
||||
return
|
||||
}
|
||||
|
||||
checker.currentProperty = programProperty
|
||||
checker.command = programsToCheck[programProperty]
|
||||
checker.running = true
|
||||
}
|
||||
|
||||
// Initialize checks when service is created
|
||||
Component.onCompleted: {
|
||||
checkAllPrograms()
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,13 @@ Singleton {
|
||||
property bool isRecording: false
|
||||
property bool isPending: false
|
||||
property string outputPath: ""
|
||||
property bool isAvailable: false
|
||||
property bool isAvailable: ProgramCheckerService.gpuScreenRecorderAvailable
|
||||
|
||||
Component.onCompleted: {
|
||||
checkAvailability()
|
||||
}
|
||||
|
||||
function checkAvailability() {
|
||||
// Detect native or Flatpak gpu-screen-recorder
|
||||
availabilityCheckProcess.command = ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"]
|
||||
availabilityCheckProcess.running = true
|
||||
// Update availability when ProgramCheckerService completes its checks
|
||||
Connections {
|
||||
target: ProgramCheckerService
|
||||
function onChecksCompleted() {// Availability is now automatically updated via property binding
|
||||
}
|
||||
}
|
||||
|
||||
// Start or Stop recording
|
||||
@@ -101,18 +98,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Availability check process
|
||||
Process {
|
||||
id: availabilityCheckProcess
|
||||
command: ["sh", "-c", "true"]
|
||||
onExited: function (exitCode, exitStatus) {
|
||||
// exitCode 0 means available, non-zero means unavailable
|
||||
root.isAvailable = (exitCode === 0)
|
||||
}
|
||||
stdout: StdioCollector {}
|
||||
stderr: StdioCollector {}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: pendingTimer
|
||||
interval: 2000 // Wait 2 seconds to see if process stays alive
|
||||
|
||||
@@ -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
193
Widgets/NCollapsible.qml
Normal file
@@ -0,0 +1,193 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property string label: ""
|
||||
property string description: ""
|
||||
property bool expanded: false
|
||||
property bool defaultExpanded: false
|
||||
property real contentSpacing: Style.marginM * scaling
|
||||
signal toggled(bool expanded)
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
// Default property to accept children
|
||||
default property alias content: contentLayout.children
|
||||
|
||||
// Header with clickable area
|
||||
Rectangle {
|
||||
id: headerContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: headerContent.implicitHeight + (Style.marginL * scaling * 2)
|
||||
|
||||
// Material 3 style background
|
||||
color: root.expanded ? Color.mSecondary : Color.mSurfaceVariant
|
||||
radius: Style.radiusL * scaling
|
||||
|
||||
// Subtle border
|
||||
border.color: root.expanded ? Color.mOnSecondary : Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Smooth color transitions
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
root.expanded = !root.expanded
|
||||
root.toggled(root.expanded)
|
||||
}
|
||||
|
||||
// Hover effect overlay
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: headerArea.containsMouse ? Color.mOnSurface : Color.transparent
|
||||
opacity: headerArea.containsMouse ? 0.08 : 0
|
||||
radius: headerContainer.radius // Reference the container's radius directly
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: headerContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginM * scaling
|
||||
|
||||
// Expand/collapse icon with rotation animation
|
||||
NIcon {
|
||||
id: chevronIcon
|
||||
icon: "chevron-right"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
rotation: root.expanded ? 90 : 0
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Header text content - properly contained
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: Style.marginXXS * scaling
|
||||
|
||||
NText {
|
||||
text: root.label
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.weight: Style.fontWeightSemiBold
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: root.description
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
font.weight: Style.fontWeightRegular
|
||||
color: root.expanded ? Color.mOnSecondary : Color.mOnSurfaceVariant
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
visible: root.description !== ""
|
||||
opacity: 0.87
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collapsible content with Material 3 styling
|
||||
Rectangle {
|
||||
id: contentContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginS * scaling
|
||||
|
||||
visible: root.expanded
|
||||
color: Color.mSurface
|
||||
radius: Style.radiusL * scaling
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Dynamic height based on content
|
||||
Layout.preferredHeight: visible ? contentLayout.implicitHeight + (Style.marginL * scaling * 2) : 0
|
||||
|
||||
// Smooth height animation
|
||||
Behavior on Layout.preferredHeight {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
// Content layout
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: root.contentSpacing
|
||||
|
||||
// Clip content during animation
|
||||
clip: true
|
||||
}
|
||||
|
||||
// Fade in animation for content
|
||||
opacity: root.expanded ? 1.0 : 0.0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize expanded state
|
||||
Component.onCompleted: {
|
||||
root.expanded = root.defaultExpanded
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
48
Widgets/NValueSlider.qml
Normal 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
|
||||
}
|
||||
}
|
||||
60
flake.nix
60
flake.nix
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user