Compare commits
11
Commits
4207f91f5a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc41dfb40a | ||
|
|
01e4dcccd0 | ||
|
|
2074f92244 | ||
|
|
89af518e7d | ||
|
|
0a03db8fe6 | ||
|
|
069a09e77b | ||
|
|
b77c7d399f | ||
|
|
96c3dc5ddb | ||
|
|
f3b2fb1c18 | ||
|
|
67d50fb504 | ||
|
|
6a9ae98c26 |
@@ -17,3 +17,10 @@ alias dotfiles='/usr/bin/git --git-dir="$HOME/.dotfiles/" --work-tree="$HOME"'
|
|||||||
|
|
||||||
alias vi=nvim
|
alias vi=nvim
|
||||||
alias svi='sudo nvim'
|
alias svi='sudo nvim'
|
||||||
|
|
||||||
|
. "$HOME/.local/bin/env"
|
||||||
|
|
||||||
|
# Added by LM Studio CLI (lms)
|
||||||
|
export PATH="$PATH:/home/michaelb/.lmstudio/bin"
|
||||||
|
# End of LM Studio CLI section
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Cava Audio Visualizer Configuration Template
|
||||||
|
# Optimized for smooth and responsive visualization
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# Number of bars (20-200) - fewer bars = better performance
|
||||||
|
bars = 64
|
||||||
|
# Framerate (1-144) - higher = smoother but more CPU intensive
|
||||||
|
framerate = 60
|
||||||
|
|
||||||
|
[input]
|
||||||
|
# Audio input method: pulse, alsa, fifo, or portaudio
|
||||||
|
method = pulse
|
||||||
|
# Audio device (leave as default for auto-detection)
|
||||||
|
source = auto
|
||||||
|
|
||||||
|
[output]
|
||||||
|
# Output method: ncurses, terminal, raw, or circle
|
||||||
|
method = ncurses
|
||||||
|
# Terminal color scheme
|
||||||
|
style = stereo
|
||||||
|
|
||||||
|
[color]
|
||||||
|
# Color gradient for bars using template variables
|
||||||
|
gradient = 1
|
||||||
|
gradient_count = 8
|
||||||
|
gradient_color_1 = '#ffdaa8'
|
||||||
|
gradient_color_2 = '#ffdb94'
|
||||||
|
gradient_color_3 = '#e1df87'
|
||||||
|
gradient_color_4 = '#b3d27e'
|
||||||
|
gradient_color_5 = '#ffa2bd'
|
||||||
|
gradient_color_6 = '#ffbcbb'
|
||||||
|
gradient_color_7 = '#ffac91'
|
||||||
|
gradient_color_8 = '#f6ae85'
|
||||||
|
|
||||||
|
[smoothing]
|
||||||
|
# Noise reduction (0-100) - higher = smoother but less responsive
|
||||||
|
# 77 is default, 85 provides good balance for smooth visualization
|
||||||
|
noise_reduction = 85
|
||||||
|
|
||||||
|
# Monstercat smoothing (0 or 1) - adds smoothing between adjacent bars
|
||||||
|
monstercat = 1
|
||||||
|
|
||||||
|
# Wave effect (0 or 1) - creates wave-like motion across bars
|
||||||
|
waves = 0
|
||||||
|
|
||||||
|
# Gravity (0-200) - controls how fast bars fall
|
||||||
|
# 100 = normal gravity, 150 = faster fall, 50 = slower fall
|
||||||
|
gravity = 120
|
||||||
|
|
||||||
|
[eq]
|
||||||
|
# Equalizer settings for frequency response
|
||||||
|
# Lower frequencies tend to be louder, so reduce them slightly
|
||||||
|
1 = 0.8
|
||||||
|
2 = 0.9
|
||||||
|
3 = 1.0
|
||||||
|
4 = 1.1
|
||||||
|
5 = 1.2
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// bar values. defaults to left channels first (low to high), then right (high to low).
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count; // number of bars (left + right) (configurable)
|
||||||
|
uniform int bar_width; // bar width (configurable), not used here
|
||||||
|
uniform int bar_spacing; // space bewteen bars (configurable)
|
||||||
|
|
||||||
|
uniform vec3 u_resolution; // window resolution
|
||||||
|
|
||||||
|
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
|
||||||
|
uniform vec3 bg_color; // background color
|
||||||
|
uniform vec3 fg_color; // foreground color
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8]; // gradient colors
|
||||||
|
|
||||||
|
uniform float shader_time; // shader execution time s (not used here)
|
||||||
|
|
||||||
|
uniform sampler2D inputTexture; // Texture from the last render pass (not used here)
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
// create color based on fraction of this color and next color
|
||||||
|
float yr = (y - y_min) / (y_max - y_min);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// find which bar to use based on where we are on the x axis
|
||||||
|
float x = u_resolution.x * fragCoord.x;
|
||||||
|
int bar = int(bars_count * fragCoord.x);
|
||||||
|
|
||||||
|
// calculate a bar size
|
||||||
|
float bar_size = u_resolution.x / bars_count;
|
||||||
|
|
||||||
|
// the y coordinate and bar values are the same
|
||||||
|
float y = bars[bar];
|
||||||
|
|
||||||
|
// make sure there is a thin line at bottom
|
||||||
|
if (y * u_resolution.y < 1.0) {
|
||||||
|
y = 1.0 / u_resolution.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the bar up to current height
|
||||||
|
if (y > fragCoord.y) {
|
||||||
|
// make some space between bars basen on settings
|
||||||
|
if (x > (bar + 1) * (bar_size)-bar_spacing) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
} else {
|
||||||
|
if (gradient_count == 0) {
|
||||||
|
fragColor = vec4(fg_color, 1.0);
|
||||||
|
} else {
|
||||||
|
// find which color in the configured gradient we are at
|
||||||
|
int color = int((gradient_count - 1) * fragCoord.y);
|
||||||
|
|
||||||
|
// find where on y this and next color is supposed to be
|
||||||
|
float y_min = color / (gradient_count - 1.0);
|
||||||
|
float y_max = (color + 1.0) / (gradient_count - 1.0);
|
||||||
|
|
||||||
|
// make color
|
||||||
|
fragColor = vec4(normalize_C(fragCoord.y, gradient_colors[color],
|
||||||
|
gradient_colors[color + 1], y_min, y_max),
|
||||||
|
1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// this shader was stolen from shadertoy user ChunderFPV
|
||||||
|
|
||||||
|
#define SCALE 8.0
|
||||||
|
#define PI radians(180.0)
|
||||||
|
#define TAU (PI * 2.0)
|
||||||
|
#define CS(a) vec2(cos(a), sin(a))
|
||||||
|
#define PT(u, r) smoothstep(0.0, r, r - length(u))
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count; // number of bars (left + right) (configurable)
|
||||||
|
uniform float shader_time; // shader execution time s
|
||||||
|
uniform int bar_width; // bar width (configurable), not used here
|
||||||
|
uniform int bar_spacing; // space bewteen bars (configurable)
|
||||||
|
|
||||||
|
uniform vec3 u_resolution; // window resolution
|
||||||
|
|
||||||
|
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
|
||||||
|
uniform vec3 bg_color; // background color
|
||||||
|
uniform vec3 fg_color; // foreground color
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8]; // gradient colors
|
||||||
|
|
||||||
|
// gradient map ( color, equation, time, width, shadow, reciprocal )
|
||||||
|
vec3 gm(vec3 c, float n, float t, float w, float d, bool i) {
|
||||||
|
float g = min(abs(n), 1.0 / abs(n));
|
||||||
|
float s = abs(sin(n * PI - t));
|
||||||
|
if (i)
|
||||||
|
s = min(s, abs(sin(PI / n + t)));
|
||||||
|
return (1.0 - pow(abs(s), w)) * c * pow(g, d) * 6.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// denominator spiral, use 1/n for numerator
|
||||||
|
// ( screen xy, spiral exponent, decimal, line width, hardness, rotation )
|
||||||
|
float ds(vec2 u, float e, float n, float w, float h, float ro) {
|
||||||
|
float ur = length(u); // unit radius
|
||||||
|
float sr = pow(ur, e); // spiral radius
|
||||||
|
float a = round(sr) * n * TAU; // arc
|
||||||
|
vec2 xy = CS(a + ro) * ur; // xy coords
|
||||||
|
float l = PT(u - xy, w); // line
|
||||||
|
float s = mod(sr + 0.5, 1.0); // gradient smooth
|
||||||
|
s = min(s, 1.0 - s); // darken filter
|
||||||
|
return l * s * h;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float t = shader_time / PI * 2.0;
|
||||||
|
vec4 m = vec4(0, 0, 0, 0); // iMouse;
|
||||||
|
m.xy = m.xy * 2.0 / u_resolution.xy - 1.0; // ±1x, ±1y
|
||||||
|
if (m.z > 0.0)
|
||||||
|
t += m.y * SCALE; // move time with mouse y
|
||||||
|
float z = (m.z > 0.0) ? pow(1.0 - abs(m.y), sign(m.y)) : 1.0; // zoom (+)
|
||||||
|
float e = (m.z > 0.0) ? pow(1.0 - abs(m.x), -sign(m.x))
|
||||||
|
: 1.0; // screen exponent (+)
|
||||||
|
float se = (m.z > 0.0) ? e * -sign(m.y) : 1.0; // spiral exponent
|
||||||
|
vec3 bg = vec3(0); // black background
|
||||||
|
|
||||||
|
float aa = 3.0; // anti-aliasing
|
||||||
|
|
||||||
|
for (float j = 0.0; j < aa; j++)
|
||||||
|
for (float k = 0.0; k < aa; k++) {
|
||||||
|
vec3 c = vec3(0);
|
||||||
|
vec2 o = vec2(j, k) / aa;
|
||||||
|
vec2 uv = (fragCoord * u_resolution.xy - 0.5 * u_resolution.xy + o) /
|
||||||
|
u_resolution.y * SCALE * z; // apply cartesian, scale and zoom
|
||||||
|
if (m.z > 0.0)
|
||||||
|
uv =
|
||||||
|
exp(log(abs(uv)) * e) * sign(uv); // warp screen space with exponent
|
||||||
|
|
||||||
|
float px = length(fwidth(uv)); // pixel width
|
||||||
|
float x = uv.x; // every pixel on x
|
||||||
|
float y = uv.y; // every pixel on y
|
||||||
|
float l = length(uv); // hypot of xy: sqrt(x*x+y*y)
|
||||||
|
|
||||||
|
float mc = (x * x + y * y - 1.0) / y; // metallic circle at xy
|
||||||
|
float g = min(abs(mc), 1.0 / abs(mc)); // gradient
|
||||||
|
vec3 gold = vec3(1.0, 0.6, 0.0) * g * l;
|
||||||
|
vec3 blue = vec3(0.3, 0.5, 0.9) * (1.0 - g);
|
||||||
|
vec3 rgb = max(gold, blue);
|
||||||
|
|
||||||
|
float w = 0.1; // line width
|
||||||
|
float d = 0.4; // shadow depth
|
||||||
|
c = max(c, gm(rgb, mc, -t, w * bars[0], d, false)); // metallic
|
||||||
|
c = max(c, gm(rgb, abs(y / x) * sign(y), -t, w * bars[1], d,
|
||||||
|
false)); // tangent
|
||||||
|
c = max(c, gm(rgb, (x * x) / (y * y) * sign(y), -t, w * bars[2], d,
|
||||||
|
false)); // sqrt cotangent
|
||||||
|
c = max(c, gm(rgb, (x * x) + (y * y), t, w * bars[3], d,
|
||||||
|
true)); // sqrt circles
|
||||||
|
|
||||||
|
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[4], 2.0, 0.0); // spiral 1a
|
||||||
|
c += rgb * ds(uv, se, t / TAU, px * 2.0 * bars[5], 2.0, PI); // spiral 1b
|
||||||
|
c +=
|
||||||
|
rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[6], 2.0, 0.0); // spiral 2a
|
||||||
|
c += rgb * ds(uv, -se, t / TAU, px * 2.0 * bars[7], 2.0, PI); // spiral 2b
|
||||||
|
c = max(c, 0.0); // clear negative color
|
||||||
|
|
||||||
|
c += pow(max(1.0 - l, 0.0), 3.0 / z); // center glow
|
||||||
|
|
||||||
|
if (m.z > 0.0) // display grid on click
|
||||||
|
{
|
||||||
|
vec2 xyg = abs(fract(uv + 0.5) - 0.5) / px; // xy grid
|
||||||
|
c.gb += 0.2 * (1.0 - min(min(xyg.x, xyg.y), 1.0));
|
||||||
|
}
|
||||||
|
bg += c;
|
||||||
|
}
|
||||||
|
bg /= aa * aa;
|
||||||
|
bg *= sqrt(bg) * 1.5;
|
||||||
|
|
||||||
|
fragColor = vec4(bg, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// bar values. defaults to left channels first (low to high), then right (high to low).
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count; // number of bars (left + right) (configurable)
|
||||||
|
|
||||||
|
uniform vec3 u_resolution; // window resolution, not used here
|
||||||
|
|
||||||
|
//colors, configurable in cava config file
|
||||||
|
uniform vec3 bg_color; // background color(r,g,b) (0.0 - 1.0), not used here
|
||||||
|
uniform vec3 fg_color; // foreground color, not used here
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// find which bar to use based on where we are on the x axis
|
||||||
|
int bar = int(bars_count * fragCoord.x);
|
||||||
|
|
||||||
|
float bar_y = 1.0 - abs((fragCoord.y - 0.5)) * 2.0;
|
||||||
|
float y = (bars[bar]) * bar_y;
|
||||||
|
|
||||||
|
float bar_x = (fragCoord.x - float(bar) / float(bars_count)) * bars_count;
|
||||||
|
float bar_r = 1.0 - abs((bar_x - 0.5)) * 2;
|
||||||
|
|
||||||
|
bar_r = bar_r * bar_r * 2;
|
||||||
|
|
||||||
|
// set color
|
||||||
|
fragColor.r = fg_color.x * y * bar_r;
|
||||||
|
fragColor.g = fg_color.y * y * bar_r;
|
||||||
|
fragColor.b = fg_color.z * y * bar_r;
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 rezky_nightky <with.rezky@gmail.com>
|
||||||
|
|
||||||
|
// Static Orion (non-rotating)
|
||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count;
|
||||||
|
uniform int bar_width;
|
||||||
|
uniform int bar_spacing;
|
||||||
|
|
||||||
|
uniform vec3 u_resolution;
|
||||||
|
|
||||||
|
uniform vec3 bg_color;
|
||||||
|
uniform vec3 fg_color;
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8];
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
const float EPS = 0.0001;
|
||||||
|
float yr = (y - y_min) / max(y_max - y_min, EPS);
|
||||||
|
yr = clamp(yr, 0.0, 1.0);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = fragCoord - vec2(0.5);
|
||||||
|
p.x *= u_resolution.x / u_resolution.y;
|
||||||
|
|
||||||
|
float base_radius = 0.35;
|
||||||
|
float max_len = 0.15;
|
||||||
|
float pad = 2.0 / u_resolution.y;
|
||||||
|
float min_r = max(base_radius - pad, 0.0);
|
||||||
|
float max_r = base_radius + max_len + pad;
|
||||||
|
|
||||||
|
float r2 = dot(p, p);
|
||||||
|
if (r2 < min_r * min_r || r2 > max_r * max_r) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = sqrt(r2);
|
||||||
|
|
||||||
|
float theta = atan(p.y, p.x);
|
||||||
|
|
||||||
|
float pi = radians(180.0);
|
||||||
|
float tau = pi * 2.0;
|
||||||
|
|
||||||
|
float a = (theta + pi) / tau;
|
||||||
|
a = fract(a);
|
||||||
|
|
||||||
|
int bc = min(bars_count, 512);
|
||||||
|
if (bc <= 0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cell = a * float(bc);
|
||||||
|
int bar = int(floor(cell));
|
||||||
|
bar = clamp(bar, 0, bc - 1);
|
||||||
|
int bar_next = bar + 1;
|
||||||
|
if (bar_next >= bc) {
|
||||||
|
bar_next = 0;
|
||||||
|
}
|
||||||
|
float f = fract(cell);
|
||||||
|
|
||||||
|
float fill = float(bar_width) / max(float(bar_width + bar_spacing), 1.0);
|
||||||
|
float angular = abs(f - 0.5);
|
||||||
|
float px = max(length(dFdx(p)), length(dFdy(p)));
|
||||||
|
float df = 0.35 * (float(bc) * px) / (tau * max(r, px));
|
||||||
|
float gap_half = (1.0 - fill) * 0.5;
|
||||||
|
float eps = 1.0 / (float(bc) * 2048.0);
|
||||||
|
float gap_cap = max(gap_half - eps, 0.0);
|
||||||
|
float df_cap = min(gap_cap, fill * 0.15);
|
||||||
|
df = min(df, max(df_cap, 1e-6));
|
||||||
|
float angular_alpha = 1.0 - smoothstep(fill * 0.5 - df, fill * 0.5 + df, angular);
|
||||||
|
angular_alpha *= step(angular, fill * 0.5 + df);
|
||||||
|
angular_alpha *= step(0.01, angular_alpha);
|
||||||
|
|
||||||
|
float y0 = clamp(bars[bar], 0.0, 1.0);
|
||||||
|
float y1 = clamp(bars[bar_next], 0.0, 1.0);
|
||||||
|
float y = mix(y0, y1, f);
|
||||||
|
|
||||||
|
float amp = y * (1.0 + 0.8 * (1.0 - y));
|
||||||
|
|
||||||
|
float min_len = 1.0 / u_resolution.y;
|
||||||
|
float max_len_cap = max(max_len - min_len, min_len);
|
||||||
|
float len = min(max(amp * max_len, min_len), max_len_cap);
|
||||||
|
float act = smoothstep(0.0, min_len / max_len, amp);
|
||||||
|
|
||||||
|
float dr = clamp(px, min_len, 2.0 * min_len);
|
||||||
|
float inner = smoothstep(base_radius - dr, base_radius + dr, r);
|
||||||
|
float outer = 1.0 - smoothstep(base_radius + len - dr, base_radius + len + dr, r);
|
||||||
|
float radial_alpha = inner * outer * act;
|
||||||
|
float outer_cap = 1.0 - smoothstep(base_radius + max_len - dr, base_radius + max_len + dr, r);
|
||||||
|
radial_alpha *= outer_cap;
|
||||||
|
|
||||||
|
float alpha = angular_alpha * radial_alpha;
|
||||||
|
alpha *= step(0.0035, alpha);
|
||||||
|
|
||||||
|
if (alpha == 0.0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 col;
|
||||||
|
if (gradient_count == 0) {
|
||||||
|
col = fg_color;
|
||||||
|
} else {
|
||||||
|
if (gradient_count == 1) {
|
||||||
|
col = gradient_colors[0];
|
||||||
|
} else {
|
||||||
|
int color = int(floor((gradient_count - 1) * amp));
|
||||||
|
color = clamp(color, 0, gradient_count - 2);
|
||||||
|
float y_min = float(color) / (gradient_count - 1.0);
|
||||||
|
float y_max = float(color + 1) / (gradient_count - 1.0);
|
||||||
|
col =
|
||||||
|
normalize_C(amp, gradient_colors[color], gradient_colors[color + 1], y_min, y_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColor = vec4(mix(bg_color, col, alpha), 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 rezky_nightky <with.rezky@gmail.com>
|
||||||
|
|
||||||
|
// Rotate Orion
|
||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count;
|
||||||
|
uniform int bar_width;
|
||||||
|
uniform int bar_spacing;
|
||||||
|
|
||||||
|
uniform vec3 u_resolution;
|
||||||
|
|
||||||
|
uniform vec3 bg_color;
|
||||||
|
uniform vec3 fg_color;
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8];
|
||||||
|
|
||||||
|
uniform float shader_time;
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
const float EPS = 0.0001;
|
||||||
|
float yr = (y - y_min) / max(y_max - y_min, EPS);
|
||||||
|
yr = clamp(yr, 0.0, 1.0);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = fragCoord - vec2(0.5);
|
||||||
|
p.x *= u_resolution.x / u_resolution.y;
|
||||||
|
|
||||||
|
float base_radius = 0.35;
|
||||||
|
float max_len = 0.15;
|
||||||
|
float pad = 2.0 / u_resolution.y;
|
||||||
|
float min_r = max(base_radius - pad, 0.0);
|
||||||
|
float max_r = base_radius + max_len + pad;
|
||||||
|
|
||||||
|
float r2 = dot(p, p);
|
||||||
|
if (r2 < min_r * min_r || r2 > max_r * max_r) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = sqrt(r2);
|
||||||
|
|
||||||
|
float theta = atan(p.y, p.x);
|
||||||
|
|
||||||
|
float pi = radians(180.0);
|
||||||
|
float tau = pi * 2.0;
|
||||||
|
|
||||||
|
float a = (theta + pi) / tau;
|
||||||
|
a = fract(a);
|
||||||
|
|
||||||
|
int bc = min(bars_count, 512);
|
||||||
|
if (bc <= 0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: rotation is achieved by phase-shifting bar sampling, not by rotating geometry.
|
||||||
|
float rotate_speed = 0.10;
|
||||||
|
float t = fract(shader_time * 0.1);
|
||||||
|
float phase = fract(t * (rotate_speed / 0.1));
|
||||||
|
|
||||||
|
float sweep_speed = 0.12;
|
||||||
|
float sweep_pos = fract(t * (sweep_speed / 0.1));
|
||||||
|
float da = abs(a - sweep_pos);
|
||||||
|
da = min(da, 1.0 - da);
|
||||||
|
float sweep = 1.0 - smoothstep(0.0, 0.08 + fwidth(a), da);
|
||||||
|
|
||||||
|
float a_sample = fract(a + phase);
|
||||||
|
|
||||||
|
float cell = a_sample * float(bc);
|
||||||
|
int bar = int(floor(cell));
|
||||||
|
bar = clamp(bar, 0, bc - 1);
|
||||||
|
int bar_next = bar + 1;
|
||||||
|
if (bar_next >= bc) {
|
||||||
|
bar_next = 0;
|
||||||
|
}
|
||||||
|
float f = fract(cell);
|
||||||
|
|
||||||
|
float fill = float(bar_width) / max(float(bar_width + bar_spacing), 1.0);
|
||||||
|
float angular = abs(f - 0.5);
|
||||||
|
float px = max(length(dFdx(p)), length(dFdy(p)));
|
||||||
|
float df = 0.35 * (float(bc) * px) / (tau * max(r, px));
|
||||||
|
float gap_half = (1.0 - fill) * 0.5;
|
||||||
|
float eps = 1.0 / (float(bc) * 2048.0);
|
||||||
|
float gap_cap = max(gap_half - eps, 0.0);
|
||||||
|
float df_cap = min(gap_cap, fill * 0.15);
|
||||||
|
df = min(df, max(df_cap, 1e-6));
|
||||||
|
float angular_alpha = 1.0 - smoothstep(fill * 0.5 - df, fill * 0.5 + df, angular);
|
||||||
|
angular_alpha *= step(angular, fill * 0.5 + df);
|
||||||
|
angular_alpha *= step(0.01, angular_alpha);
|
||||||
|
|
||||||
|
float y0 = clamp(bars[bar], 0.0, 1.0);
|
||||||
|
float y1 = clamp(bars[bar_next], 0.0, 1.0);
|
||||||
|
float y = mix(y0, y1, f);
|
||||||
|
|
||||||
|
float amp = y * (1.0 + 0.8 * (1.0 - y));
|
||||||
|
|
||||||
|
float min_len = 1.0 / u_resolution.y;
|
||||||
|
float max_len_cap = max(max_len - min_len, min_len);
|
||||||
|
float len = min(max(amp * max_len, min_len), max_len_cap);
|
||||||
|
float act = smoothstep(0.0, min_len / max_len, amp);
|
||||||
|
|
||||||
|
float dr = clamp(px, min_len, 2.0 * min_len);
|
||||||
|
float inner = smoothstep(base_radius - dr, base_radius + dr, r);
|
||||||
|
float outer = 1.0 - smoothstep(base_radius + len - dr, base_radius + len + dr, r);
|
||||||
|
float radial_alpha = inner * outer * act;
|
||||||
|
float outer_cap = 1.0 - smoothstep(base_radius + max_len - dr, base_radius + max_len + dr, r);
|
||||||
|
radial_alpha *= outer_cap;
|
||||||
|
|
||||||
|
float alpha = angular_alpha * radial_alpha;
|
||||||
|
alpha *= step(0.0035, alpha);
|
||||||
|
|
||||||
|
if (alpha == 0.0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 col;
|
||||||
|
if (gradient_count == 0) {
|
||||||
|
col = fg_color;
|
||||||
|
} else {
|
||||||
|
if (gradient_count == 1) {
|
||||||
|
col = gradient_colors[0];
|
||||||
|
} else {
|
||||||
|
int color = int(floor((gradient_count - 1) * amp));
|
||||||
|
color = clamp(color, 0, gradient_count - 2);
|
||||||
|
float y_min = float(color) / (gradient_count - 1.0);
|
||||||
|
float y_max = float(color + 1) / (gradient_count - 1.0);
|
||||||
|
col = normalize_C(amp, gradient_colors[color], gradient_colors[color + 1], y_min, y_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
col = min(col * (1.0 + 0.35 * sweep * alpha), vec3(1.0));
|
||||||
|
|
||||||
|
fragColor = vec4(mix(bg_color, col, alpha), 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 rezky_nightky <with.rezky@gmail.com>
|
||||||
|
|
||||||
|
// Orion Saturn core
|
||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count;
|
||||||
|
uniform int bar_width;
|
||||||
|
uniform int bar_spacing;
|
||||||
|
|
||||||
|
uniform vec3 u_resolution;
|
||||||
|
|
||||||
|
uniform vec3 bg_color;
|
||||||
|
uniform vec3 fg_color;
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8];
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
const float EPS = 0.0001;
|
||||||
|
float yr = (y - y_min) / max(y_max - y_min, EPS);
|
||||||
|
yr = clamp(yr, 0.0, 1.0);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 gradient_map(float amp) {
|
||||||
|
if (gradient_count == 0) {
|
||||||
|
return fg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gradient_count == 1) {
|
||||||
|
return gradient_colors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int color = int(floor((gradient_count - 1) * amp));
|
||||||
|
color = clamp(color, 0, gradient_count - 2);
|
||||||
|
float y_min = float(color) / (gradient_count - 1.0);
|
||||||
|
float y_max = float(color + 1) / (gradient_count - 1.0);
|
||||||
|
return normalize_C(amp, gradient_colors[color], gradient_colors[color + 1], y_min, y_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = fragCoord - vec2(0.5);
|
||||||
|
p.x *= u_resolution.x / u_resolution.y;
|
||||||
|
|
||||||
|
float base_radius = 0.35;
|
||||||
|
float max_len = 0.15;
|
||||||
|
float pad = 2.0 / u_resolution.y;
|
||||||
|
|
||||||
|
float max_r = base_radius + max_len + pad;
|
||||||
|
|
||||||
|
float r2 = dot(p, p);
|
||||||
|
if (r2 > max_r * max_r) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bc = min(bars_count, 512);
|
||||||
|
if (bc <= 0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = sqrt(r2);
|
||||||
|
|
||||||
|
float pi = radians(180.0);
|
||||||
|
float tau = pi * 2.0;
|
||||||
|
|
||||||
|
float theta = atan(p.y, p.x);
|
||||||
|
float a = fract((theta + pi) / tau);
|
||||||
|
|
||||||
|
float cell = a * float(bc);
|
||||||
|
int bar = int(floor(cell));
|
||||||
|
bar = clamp(bar, 0, bc - 1);
|
||||||
|
int bar_next = bar + 1;
|
||||||
|
if (bar_next >= bc) {
|
||||||
|
bar_next = 0;
|
||||||
|
}
|
||||||
|
float f = fract(cell);
|
||||||
|
|
||||||
|
float fill = float(bar_width) / max(float(bar_width + bar_spacing), 1.0);
|
||||||
|
float angular = abs(f - 0.5);
|
||||||
|
float px_ang = max(length(dFdx(p)), length(dFdy(p)));
|
||||||
|
float df = 0.35 * (float(bc) * px_ang) / (tau * max(r, px_ang));
|
||||||
|
float gap_half = (1.0 - fill) * 0.5;
|
||||||
|
float eps = 1.0 / (float(bc) * 2048.0);
|
||||||
|
float gap_cap = max(gap_half - eps, 0.0);
|
||||||
|
float df_cap = min(gap_cap, fill * 0.15);
|
||||||
|
df = min(df, max(df_cap, 1e-6));
|
||||||
|
float angular_alpha = 1.0 - smoothstep(fill * 0.5 - df, fill * 0.5 + df, angular);
|
||||||
|
angular_alpha *= step(angular, fill * 0.5 + df);
|
||||||
|
angular_alpha *= step(0.01, angular_alpha);
|
||||||
|
|
||||||
|
float y0 = clamp(bars[bar], 0.0, 1.0);
|
||||||
|
float y1 = clamp(bars[bar_next], 0.0, 1.0);
|
||||||
|
float y = mix(y0, y1, f);
|
||||||
|
float amp = y * (1.0 + 0.8 * (1.0 - y));
|
||||||
|
|
||||||
|
float min_len = 1.0 / u_resolution.y;
|
||||||
|
float max_len_cap = max(max_len - min_len, min_len);
|
||||||
|
float len = min(max(amp * max_len, min_len), max_len_cap);
|
||||||
|
float act = smoothstep(0.0, min_len / max_len, amp);
|
||||||
|
|
||||||
|
float dr = clamp(px_ang, min_len, 2.0 * min_len);
|
||||||
|
float inner = smoothstep(base_radius - dr, base_radius + dr, r);
|
||||||
|
float outer = 1.0 - smoothstep(base_radius + len - dr, base_radius + len + dr, r);
|
||||||
|
float radial_alpha = inner * outer * act;
|
||||||
|
float outer_cap = 1.0 - smoothstep(base_radius + max_len - dr, base_radius + max_len + dr, r);
|
||||||
|
radial_alpha *= outer_cap;
|
||||||
|
|
||||||
|
float ring_alpha = angular_alpha * radial_alpha;
|
||||||
|
ring_alpha *= step(0.0035, ring_alpha);
|
||||||
|
|
||||||
|
float core_energy = 0.0;
|
||||||
|
int core_samples = 0;
|
||||||
|
|
||||||
|
int core_limit = min(bc, 64);
|
||||||
|
for (int i = 0; i < core_limit; i += 2) {
|
||||||
|
core_energy += clamp(bars[i], 0.0, 1.0);
|
||||||
|
core_samples++;
|
||||||
|
}
|
||||||
|
core_energy /= max(float(core_samples), 1.0);
|
||||||
|
|
||||||
|
float core_amp = core_energy * (1.0 + 0.8 * (1.0 - core_energy));
|
||||||
|
|
||||||
|
float core_radius = mix(0.07, 0.25, clamp(core_amp * 1.1, 0.0, 1.0));
|
||||||
|
|
||||||
|
float px = 1.0 / u_resolution.y;
|
||||||
|
float core_edge = max(px * 1.5, 0.003);
|
||||||
|
float core_act = smoothstep(0.0, 0.04, core_amp);
|
||||||
|
|
||||||
|
float core_feather = core_edge + dr;
|
||||||
|
float core_alpha = 1.0 - smoothstep(core_radius - core_feather, core_radius + core_feather, r);
|
||||||
|
core_alpha = clamp(core_alpha, 0.0, 1.0) * core_act;
|
||||||
|
|
||||||
|
if (ring_alpha == 0.0 && core_alpha == 0.0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 col_core = gradient_map(core_amp);
|
||||||
|
vec3 col_ring = gradient_map(amp);
|
||||||
|
|
||||||
|
vec3 col = mix(bg_color, col_core, core_alpha);
|
||||||
|
col = mix(col, col_ring, ring_alpha);
|
||||||
|
fragColor = vec4(col, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: 2026 rezky_nightky <with.rezky@gmail.com>
|
||||||
|
|
||||||
|
// Orion Saturn subring
|
||||||
|
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count;
|
||||||
|
uniform int bar_width;
|
||||||
|
uniform int bar_spacing;
|
||||||
|
|
||||||
|
uniform vec3 u_resolution;
|
||||||
|
|
||||||
|
uniform vec3 bg_color;
|
||||||
|
uniform vec3 fg_color;
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8];
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
const float EPS = 0.0001;
|
||||||
|
float yr = (y - y_min) / max(y_max - y_min, EPS);
|
||||||
|
yr = clamp(yr, 0.0, 1.0);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 gradient_map(float amp) {
|
||||||
|
if (gradient_count == 0) {
|
||||||
|
return fg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gradient_count == 1) {
|
||||||
|
return gradient_colors[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int color = int(floor((gradient_count - 1) * amp));
|
||||||
|
color = clamp(color, 0, gradient_count - 2);
|
||||||
|
float y_min = float(color) / (gradient_count - 1.0);
|
||||||
|
float y_max = float(color + 1) / (gradient_count - 1.0);
|
||||||
|
return normalize_C(amp, gradient_colors[color], gradient_colors[color + 1], y_min, y_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 p = fragCoord - vec2(0.5);
|
||||||
|
p.x *= u_resolution.x / u_resolution.y;
|
||||||
|
|
||||||
|
float base_radius = 0.35;
|
||||||
|
float max_len = 0.15;
|
||||||
|
float pad = 2.0 / u_resolution.y;
|
||||||
|
|
||||||
|
float max_r = base_radius + max_len + pad;
|
||||||
|
|
||||||
|
float r2 = dot(p, p);
|
||||||
|
|
||||||
|
if (r2 > max_r * max_r) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bc = min(bars_count, 512);
|
||||||
|
if (bc <= 0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = sqrt(r2);
|
||||||
|
|
||||||
|
float pi = radians(180.0);
|
||||||
|
float tau = pi * 2.0;
|
||||||
|
|
||||||
|
float theta = atan(p.y, p.x);
|
||||||
|
float a = fract((theta + pi) / tau);
|
||||||
|
|
||||||
|
float cell = a * float(bc);
|
||||||
|
int bar = int(floor(cell));
|
||||||
|
bar = clamp(bar, 0, bc - 1);
|
||||||
|
int bar_next = bar + 1;
|
||||||
|
if (bar_next >= bc) {
|
||||||
|
bar_next = 0;
|
||||||
|
}
|
||||||
|
float f = fract(cell);
|
||||||
|
|
||||||
|
float fill = float(bar_width) / max(float(bar_width + bar_spacing), 1.0);
|
||||||
|
float angular = abs(f - 0.5);
|
||||||
|
float px = max(length(dFdx(p)), length(dFdy(p)));
|
||||||
|
float df = 0.35 * (float(bc) * px) / (tau * max(r, px));
|
||||||
|
float gap_half = (1.0 - fill) * 0.5;
|
||||||
|
float eps = 1.0 / (float(bc) * 2048.0);
|
||||||
|
float gap_cap = max(gap_half - eps, 0.0);
|
||||||
|
float df_cap = min(gap_cap, fill * 0.15);
|
||||||
|
df = min(df, max(df_cap, 1e-6));
|
||||||
|
float angular_alpha = 1.0 - smoothstep(fill * 0.5 - df, fill * 0.5 + df, angular);
|
||||||
|
angular_alpha *= step(angular, fill * 0.5 + df);
|
||||||
|
angular_alpha *= step(0.01, angular_alpha);
|
||||||
|
|
||||||
|
float y0 = clamp(bars[bar], 0.0, 1.0);
|
||||||
|
float y1 = clamp(bars[bar_next], 0.0, 1.0);
|
||||||
|
float y = mix(y0, y1, f);
|
||||||
|
float amp = y * (1.0 + 0.8 * (1.0 - y));
|
||||||
|
|
||||||
|
float min_len = 1.0 / u_resolution.y;
|
||||||
|
float max_len_cap = max(max_len - min_len, min_len);
|
||||||
|
float len = min(max(amp * max_len, min_len), max_len_cap);
|
||||||
|
float act = smoothstep(0.0, min_len / max_len, amp);
|
||||||
|
|
||||||
|
float dr = clamp(px, min_len, 2.0 * min_len);
|
||||||
|
float inner = smoothstep(base_radius - dr, base_radius + dr, r);
|
||||||
|
float outer = 1.0 - smoothstep(base_radius + len - dr, base_radius + len + dr, r);
|
||||||
|
float radial_alpha = inner * outer * act;
|
||||||
|
float outer_cap = 1.0 - smoothstep(base_radius + max_len - dr, base_radius + max_len + dr, r);
|
||||||
|
radial_alpha *= outer_cap;
|
||||||
|
|
||||||
|
float ring_alpha = angular_alpha * radial_alpha;
|
||||||
|
ring_alpha *= step(0.0035, ring_alpha);
|
||||||
|
|
||||||
|
float core_energy = 0.0;
|
||||||
|
int core_samples = 0;
|
||||||
|
|
||||||
|
int core_limit = min(bc, 64);
|
||||||
|
for (int i = 0; i < core_limit; i += 4) {
|
||||||
|
core_energy += clamp(bars[i], 0.0, 1.0);
|
||||||
|
core_samples++;
|
||||||
|
}
|
||||||
|
core_energy /= max(float(core_samples), 1.0);
|
||||||
|
|
||||||
|
float core_amp = core_energy * (1.0 + 0.8 * (1.0 - core_energy));
|
||||||
|
float core_radius = mix(0.05, 0.18, clamp(core_amp * 1.2, 0.0, 1.0));
|
||||||
|
|
||||||
|
float core_act = smoothstep(0.0, 0.04, core_amp);
|
||||||
|
float core_half_thickness = 0.007;
|
||||||
|
|
||||||
|
float core = smoothstep(core_radius - core_half_thickness - dr,
|
||||||
|
core_radius - core_half_thickness + dr, r) -
|
||||||
|
smoothstep(core_radius + core_half_thickness - dr,
|
||||||
|
core_radius + core_half_thickness + dr, r);
|
||||||
|
float core_alpha = clamp(core, 0.0, 1.0) * core_act;
|
||||||
|
|
||||||
|
if (ring_alpha == 0.0 && core_alpha == 0.0) {
|
||||||
|
fragColor = vec4(bg_color, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 col_ring = gradient_map(amp);
|
||||||
|
vec3 col_core = gradient_map(core_amp);
|
||||||
|
|
||||||
|
vec3 col = mix(bg_color, col_ring, ring_alpha);
|
||||||
|
col = mix(col, col_core, core_alpha);
|
||||||
|
fragColor = vec4(col, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
|
||||||
|
// Input vertex data, different for all executions of this shader.
|
||||||
|
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||||
|
|
||||||
|
// Output data ; will be interpolated for each fragment.
|
||||||
|
out vec2 fragCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(vertexPosition_modelspace,1);
|
||||||
|
fragCoord = (vertexPosition_modelspace.xy+vec2(1,1))/2.0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// bar values. defaults to left channels first (low to high), then right (high
|
||||||
|
// to low).
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count; // number of bars (left + right) (configurable)
|
||||||
|
uniform int bar_width; // bar width (configurable), not used here
|
||||||
|
uniform int bar_spacing; // space bewteen bars (configurable)
|
||||||
|
|
||||||
|
uniform vec3 u_resolution; // window resolution
|
||||||
|
|
||||||
|
// colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
|
||||||
|
uniform vec3 bg_color; // background color
|
||||||
|
uniform vec3 fg_color; // foreground color
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8]; // gradient colors
|
||||||
|
|
||||||
|
uniform sampler2D inputTexture; // Texture from the last render pass
|
||||||
|
|
||||||
|
vec3 normalize_C(float y, vec3 col_1, vec3 col_2, float y_min, float y_max) {
|
||||||
|
// create color based on fraction of this color and next color
|
||||||
|
float yr = (y - y_min) / (y_max - y_min);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// find which bar to use based on where we are on the y axis
|
||||||
|
int bar = int(bars_count * fragCoord.y);
|
||||||
|
float y = bars[bar];
|
||||||
|
float band_size = 1.0 / float(bars_count);
|
||||||
|
float current_band_min = bar * band_size;
|
||||||
|
float current_band_max = (bar + 1) * band_size;
|
||||||
|
|
||||||
|
int hist_length = 512;
|
||||||
|
float win_size = 1.0 / hist_length;
|
||||||
|
|
||||||
|
if (fragCoord.x > 1.0 - win_size) {
|
||||||
|
|
||||||
|
if (fragCoord.y > current_band_min && fragCoord.y < current_band_max) {
|
||||||
|
|
||||||
|
fragColor = vec4(fg_color * y, 1.0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec2 offsetCoord = fragCoord;
|
||||||
|
offsetCoord.x += float(win_size);
|
||||||
|
fragColor = texture(inputTexture, offsetCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Emulate the "line style" spectrum analyzer from Winamp 2.
|
||||||
|
// Try this config for a demonstration:
|
||||||
|
|
||||||
|
/*
|
||||||
|
[general]
|
||||||
|
bar_width = 2
|
||||||
|
bar_spacing = 0
|
||||||
|
higher_cutoff_freq = 22000
|
||||||
|
|
||||||
|
[output]
|
||||||
|
method = sdl_glsl
|
||||||
|
channels = mono
|
||||||
|
fragment_shader = winamp_line_style_spectrum.frag
|
||||||
|
|
||||||
|
[color]
|
||||||
|
background = '#000000'
|
||||||
|
gradient = 1
|
||||||
|
gradient_color_1 = '#319C08'
|
||||||
|
gradient_color_2 = '#29CE10'
|
||||||
|
gradient_color_3 = '#BDDE29'
|
||||||
|
gradient_color_4 = '#DEA518'
|
||||||
|
gradient_color_5 = '#D66600'
|
||||||
|
gradient_color_6 = '#CE2910'
|
||||||
|
|
||||||
|
[smoothing]
|
||||||
|
noise_reduction = 10
|
||||||
|
*/
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
// bar values. defaults to left channels first (low to high), then right (high to low).
|
||||||
|
uniform float bars[512];
|
||||||
|
|
||||||
|
uniform int bars_count; // number of bars (left + right) (configurable)
|
||||||
|
uniform int bar_width; // bar width (configurable), not used here
|
||||||
|
uniform int bar_spacing; // space bewteen bars (configurable)
|
||||||
|
|
||||||
|
uniform vec3 u_resolution; // window resolution
|
||||||
|
|
||||||
|
//colors, configurable in cava config file (r,g,b) (0.0 - 1.0)
|
||||||
|
uniform vec3 bg_color; // background color
|
||||||
|
uniform vec3 fg_color; // foreground color
|
||||||
|
|
||||||
|
uniform int gradient_count;
|
||||||
|
uniform vec3 gradient_colors[8]; // gradient colors
|
||||||
|
|
||||||
|
vec3 normalize_C(float y,vec3 col_1, vec3 col_2, float y_min, float y_max)
|
||||||
|
{
|
||||||
|
//create color based on fraction of this color and next color
|
||||||
|
float yr = (y - y_min) / (y_max - y_min);
|
||||||
|
return col_1 * (1.0 - yr) + col_2 * yr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// find which bar to use based on where we are on the x axis
|
||||||
|
float x = u_resolution.x * fragCoord.x;
|
||||||
|
int bar = int(bars_count * fragCoord.x);
|
||||||
|
|
||||||
|
//calculate a bar size
|
||||||
|
float bar_size = u_resolution.x / bars_count;
|
||||||
|
|
||||||
|
//the y coordinate is stretched by 4X to resemble Winamp
|
||||||
|
float y = min(bars[bar] * 4.0, 1.0);
|
||||||
|
|
||||||
|
// make sure there is a thin line at bottom
|
||||||
|
if (y * u_resolution.y < 1.0)
|
||||||
|
{
|
||||||
|
y = 1.0 / u_resolution.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 bar_color;
|
||||||
|
|
||||||
|
if (gradient_count == 0)
|
||||||
|
{
|
||||||
|
bar_color = vec4(fg_color,1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//find color in the configured gradient for the top of the bar
|
||||||
|
int color = int((gradient_count - 1) * y);
|
||||||
|
|
||||||
|
//find where on y this and next color is supposed to be
|
||||||
|
float y_min = float(color) / (gradient_count - 1.0);
|
||||||
|
float y_max = float(color + 1) / (gradient_count - 1.0);
|
||||||
|
|
||||||
|
//make a solid color for the entire bar
|
||||||
|
bar_color = vec4(normalize_C(y, gradient_colors[color], gradient_colors[color + 1], y_min, y_max), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//draw the bar up to current height
|
||||||
|
if (y > fragCoord.y)
|
||||||
|
{
|
||||||
|
//make some space between bars based on settings
|
||||||
|
if (x > (bar + 1) * (bar_size) - bar_spacing)
|
||||||
|
{
|
||||||
|
fragColor = vec4(bg_color,1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fragColor = bar_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fragColor = vec4(bg_color,1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
[color]
|
||||||
|
background = '#001e26'
|
||||||
|
foreground = '#708183'
|
||||||
|
|
||||||
|
gradient = 1
|
||||||
|
gradient_color_1 = '#268bd2'
|
||||||
|
gradient_color_2 = '#6c71c4'
|
||||||
|
gradient_color_3 = '#cb4b16'
|
||||||
|
|
||||||
|
horizontal_gradient = 1
|
||||||
|
horizontal_gradient_color_1 = '#586e75'
|
||||||
|
horizontal_gradient_color_2 = '#b58900'
|
||||||
|
horizontal_gradient_color_3 = '#839496'
|
||||||
|
|
||||||
|
blend_direction = 'up'
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[color]
|
||||||
|
horizontal_gradient = 1
|
||||||
|
horizontal_gradient_color_1 = '#c45161'
|
||||||
|
horizontal_gradient_color_2 = '#e094a0'
|
||||||
|
horizontal_gradient_color_3 = '#f2b6c0'
|
||||||
|
horizontal_gradient_color_4 = '#f2dde1'
|
||||||
|
horizontal_gradient_color_5 = '#cbc7d8'
|
||||||
|
horizontal_gradient_color_6 = '#8db7d2'
|
||||||
|
horizontal_gradient_color_7 = '#5e62a9'
|
||||||
|
horizontal_gradient_color_8 = '#434279'
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"workspace": {
|
||||||
|
"library": ["/usr/share/hypr/stubs"]
|
||||||
|
},
|
||||||
|
"diagnostics": {
|
||||||
|
"globals": ["hl"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Animations Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Animations/
|
|
||||||
|
|
||||||
animations {
|
|
||||||
enabled = yes
|
|
||||||
bezier = overshot, 0.13, 0.99, 0.29, 1.1
|
|
||||||
animation = windowsIn, 1, 4, overshot, popin
|
|
||||||
animation = windowsOut, 1, 5, default, popin 80%
|
|
||||||
animation = border, 1, 5, default
|
|
||||||
animation = workspacesIn, 1, 6, overshot, slide
|
|
||||||
animation = workspacesOut, 1, 6, overshot, slidefade 80%
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Autostart Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
source = ~/.config/hypr/config/defaults.conf
|
|
||||||
|
|
||||||
# Autostart wiki https://wiki.hyprland.org/0.45.0/Configuring/Keywords/#executing #
|
|
||||||
exec-once = qs
|
|
||||||
exec-once = blueman-applet &
|
|
||||||
exec-once = fcitx5 -d &
|
|
||||||
exec-once = mako &
|
|
||||||
exec-once = nm-applet --indicator &
|
|
||||||
exec-once = bash -c "mkfifo /tmp/$HYPRLAND_INSTANCE_SIGNATURE.wob && tail -f /tmp/$HYPRLAND_INSTANCE_SIGNATURE.wob | wob -c ~/.config/hypr/wob.ini & disown" &
|
|
||||||
exec-once = /usr/lib/polkit-kde-authentication-agent-1 &
|
|
||||||
|
|
||||||
exec-once = waypaper --restore
|
|
||||||
|
|
||||||
# ## Slow app launch fix
|
|
||||||
exec-once = systemctl --user import-environment &
|
|
||||||
exec-once = hash dbus-update-activation-environment 2>/dev/null &
|
|
||||||
exec-once = dbus-update-activation-environment --systemd &
|
|
||||||
|
|
||||||
# ## Idle configuration
|
|
||||||
exec-once = $idlehandler
|
|
||||||
|
|
||||||
# Clipboard fix
|
|
||||||
# https://github.com/hyprwm/Hyprland/issues/2319#issuecomment-2409983376
|
|
||||||
#exec-once = wl-paste -t text -w xclip -selection clipboard &
|
|
||||||
#exec-once = wl-paste --watch cliphist store &
|
|
||||||
|
|
||||||
# vicinae
|
|
||||||
exec-once = vicinae server
|
|
||||||
|
|
||||||
# pyprland
|
|
||||||
exec-once = /usr/bin/pypr --debug /tmp/pypr.log
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
-- config/autostart.lua
|
||||||
|
-- Replaces exec-once lines. In Lua config use hl.exec_once().
|
||||||
|
|
||||||
|
local function once(cmd)
|
||||||
|
hl.exec_cmd(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
hl.exec_cmd("systemctl --user start hyprland-session.target")
|
||||||
|
-- ── Bar (QuickShell, replaces waybar) ────────────────────────
|
||||||
|
hl.exec_cmd("quickshell -c own")
|
||||||
|
hl.exec_cmd("awww-daemon")
|
||||||
|
-- ── System tray / applets ────────────────────────────────────
|
||||||
|
hl.exec_cmd("blueman-applet")
|
||||||
|
once("fcitx5 -d")
|
||||||
|
once("mako")
|
||||||
|
once("nm-applet --indicator")
|
||||||
|
once("/usr/lib/polkit-kde-authentication-agent-1")
|
||||||
|
-- ── Wallpaper ─────────────────────────────────────────────────
|
||||||
|
once("waypaper --restore")
|
||||||
|
-- ── DBus / env plumbing ───────────────────────────────────────
|
||||||
|
once("systemctl --user import-environment")
|
||||||
|
once("dbus-update-activation-environment --systemd")
|
||||||
|
-- ── Idle daemon ───────────────────────────────────────────────
|
||||||
|
once(idlehandler)
|
||||||
|
-- ── Other tools ───────────────────────────────────────────────
|
||||||
|
once("vicinae server")
|
||||||
|
once("/usr/bin/pypr --debug /tmp/pypr.log")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hl.on("config.reloaded", function()
|
||||||
|
hl.exec_cmd("killall quickshell ")
|
||||||
|
hl.exec_cmd("sleep 0.2; quickshell -c own")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hl.on("hyprland.shutdown", function()
|
||||||
|
os.execute("systemctl --user stop hyprland-session.target && sleep 0.1")
|
||||||
|
-- uses a blocking exec function and sleeps a bit to give things time to close
|
||||||
|
-- you might also want to kill troublesome/crashing non-systemd background services here:
|
||||||
|
-- os.execute("pkill wallpaperthing; systemctl --user stop hyprland-session.target && sleep 0.1")
|
||||||
|
end)
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
$wallpaper = /usr/share/wallpapers/downloaded/pexels-8kspain-21564213.jpg
|
|
||||||
$background = rgb(252425)
|
|
||||||
$foreground = rgb(F9F1D9)
|
|
||||||
$color0 = rgb(505051)
|
|
||||||
$color1 = rgb(9C604E)
|
|
||||||
$color2 = rgb(807A52)
|
|
||||||
$color3 = rgb(908BAB)
|
|
||||||
$color4 = rgb(B7815F)
|
|
||||||
$color5 = rgb(B9BECA)
|
|
||||||
$color6 = rgb(EED793)
|
|
||||||
$color7 = rgb(EEE3C1)
|
|
||||||
$color8 = rgb(A79F87)
|
|
||||||
$color9 = rgb(9C604E)
|
|
||||||
$color10 = rgb(807A52)
|
|
||||||
$color11 = rgb(908BAB)
|
|
||||||
$color12 = rgb(B7815F)
|
|
||||||
$color13 = rgb(B9BECA)
|
|
||||||
$color14 = rgb(EED793)
|
|
||||||
$color15 = rgb(EEE3C1)
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- config/colors.lua
|
||||||
|
-- Centralised palette
|
||||||
|
-- Use these Lua globals throughout every other module.
|
||||||
|
background = "rgb(080701)"
|
||||||
|
foreground = "rgb(AEADA7)"
|
||||||
|
color0 = "rgb(463E37)"
|
||||||
|
color1 = "rgb(3B3E4D)"
|
||||||
|
color2 = "rgb(5D8071)"
|
||||||
|
color3 = "rgb(464E89)"
|
||||||
|
color4 = "rgb(1982B3)"
|
||||||
|
color5 = "rgb(609D75)"
|
||||||
|
color6 = "rgb(73BAFF)"
|
||||||
|
color7 = "rgb(848279)"
|
||||||
|
color8 = "rgb(5C5B54)"
|
||||||
|
color9 = "rgb(3B3E4D)"
|
||||||
|
color10 = "rgb(5D8071)"
|
||||||
|
color11 = "rgb(464E89)"
|
||||||
|
color12 = "rgb(1982B3)"
|
||||||
|
color13 = "rgb(609D75)"
|
||||||
|
color14 = "rgb(73BAFF)"
|
||||||
|
color15 = "rgb(848279)"
|
||||||
|
wallpaper = "/usr/share/wallpapers/downloaded/pexels-jplenio-1102908.jpg"
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Decorations Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
source = ~/.config/hypr/config/colors.conf
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#decoration
|
|
||||||
|
|
||||||
decoration {
|
|
||||||
active_opacity = 1
|
|
||||||
inactive_opacity = 0.7
|
|
||||||
rounding = 1
|
|
||||||
dim_inactive = true
|
|
||||||
dim_strength = 0.1
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#blur
|
|
||||||
blur {
|
|
||||||
size = 30
|
|
||||||
passes = 2 # more passes = more resource intensive.
|
|
||||||
xray = true
|
|
||||||
noise = 0.01
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#shadow
|
|
||||||
shadow {
|
|
||||||
enabled = true
|
|
||||||
range = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Defaults Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
$filemanager = nemo
|
|
||||||
#$applauncher = tofi-drun --drun-launch=true
|
|
||||||
#$applauncher = rofi -show combi -modi window,run,combi -combi-modi window,run
|
|
||||||
$dmenu = vicinae dmenu --placeholder
|
|
||||||
$applauncher = vicinae toggle
|
|
||||||
$terminal = kitty
|
|
||||||
$idlehandler = swayidle -w timeout 300 'swaylock -f -c 000000' before-sleep 'swaylock -f -c 000000'
|
|
||||||
$capturing = grim -g "$(slurp)" - | swappy -f -
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Enviroment Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
envd = HYPRCURSOR_THEME,vimix-kanagawa-hyprcursors-lotus
|
|
||||||
envd = HYPRCURSOR_SIZE,34
|
|
||||||
env = XCURSOR_THEME,vimix-kanagawa-hyprcursors-lotus
|
|
||||||
envd = XCURSOR_SIZE,34
|
|
||||||
envd = QT_CURSOR_SIZE,34
|
|
||||||
|
|
||||||
# Force electron apps to use wayland
|
|
||||||
env = ELECTRON_OZONE_PLATFORM_HINT,wayland
|
|
||||||
env = QT_QPA_PLATFORM, wayland
|
|
||||||
env = SDL_VIDEODRIVER, wayland
|
|
||||||
env = CLUTTER_BACKEND, wayland
|
|
||||||
env = XDG_SESSION_TYPE, wayland
|
|
||||||
env = MOZ_ENABLE_WAYLAND, 1
|
|
||||||
env = NIXOS_OZONE_WL, 1
|
|
||||||
|
|
||||||
# fixes full screen flickering
|
|
||||||
env = WLR_DRM_NO_DIRECT_SCANOUT,1
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
-- config/environment.lua
|
||||||
|
-- Wayland / cursor env vars (mirrors environment.conf)
|
||||||
|
-- NOTE: In 0.55 Lua config these are set via hl.config({ env = ... })
|
||||||
|
-- or directly sourced into the Lua environment before hl.exec_once.
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
env = {
|
||||||
|
HYPRCURSOR_THEME = "vimix-kanagawa-hyprcursors-lotus",
|
||||||
|
HYPRCURSOR_SIZE = "34",
|
||||||
|
XCURSOR_THEME = "vimix-kanagawa-hyprcursors-lotus",
|
||||||
|
XCURSOR_SIZE = "34",
|
||||||
|
QT_CURSOR_SIZE = "34",
|
||||||
|
ELECTRON_OZONE_PLATFORM_HINT = "wayland",
|
||||||
|
QT_QPA_PLATFORM = "wayland",
|
||||||
|
SDL_VIDEODRIVER = "wayland",
|
||||||
|
CLUTTER_BACKEND = "wayland",
|
||||||
|
XDG_SESSION_TYPE = "wayland",
|
||||||
|
MOZ_ENABLE_WAYLAND = "1",
|
||||||
|
NIXOS_OZONE_WL = "1",
|
||||||
|
WLR_DRM_NO_DIRECT_SCANOUT = "1",
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Input Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
# Input wiki https://wiki.hyprland.org/0.45.0/Configuring/Variables/#input
|
|
||||||
|
|
||||||
input {
|
|
||||||
kb_layout = de
|
|
||||||
follow_mouse = 2 # 0|1|2|3
|
|
||||||
float_switch_override_focus = 2
|
|
||||||
numlock_by_default = true
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- config/input.lua
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
input = {
|
||||||
|
kb_layout = "de",
|
||||||
|
follow_mouse = 2,
|
||||||
|
float_switch_override_focus = 2,
|
||||||
|
numlock_by_default = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Per-device config for the G502
|
||||||
|
hl.device({
|
||||||
|
name = "logitech-gaming-mouse-g502",
|
||||||
|
sensitivity = 0.9,
|
||||||
|
})
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
# 0. Includes
|
|
||||||
source = ~/.config/hypr/config/defaults.conf
|
|
||||||
|
|
||||||
# 1. Applications
|
|
||||||
# https://wiki.hyprland.org/Configuring/Binds/
|
|
||||||
bindd = $mainMod, RETURN, Opens your preferred terminal emulator ($terminal), exec, $terminal
|
|
||||||
bindd = $mainMod, E, Opens your preferred filemanager ($filemanager), exec, $filemanager
|
|
||||||
#bindd = $mainMod, A, Screen capture selection, exec, $capturing
|
|
||||||
bindd = $mainMod, Q, Closes (not kill) current window, killactive,
|
|
||||||
bindd = $mainMod SHIFT, M, Exits Hyprland by terminating the user sessions, exec, loginctl terminate-user ""
|
|
||||||
bindd = $mainMod, V, Switches current window between floating and tiling mode, togglefloating,
|
|
||||||
bindd = $mainMod, SPACE, Runs your application launcher, exec, $applauncher
|
|
||||||
bindd = $mainMod, F, Toggles current window fullscreen mode, fullscreen
|
|
||||||
bindd = $mainMod, Y, Pin current window (shows on all workspaces),pin
|
|
||||||
bindd = $mainMod, J, Toggles curren window split mode, togglesplit, # dwindle
|
|
||||||
|
|
||||||
# ======= Grouping Windows =======
|
|
||||||
bindd = $mainMod, K, Toggles current window group mode (ungroup all related), togglegroup,
|
|
||||||
bindd = $mainMod, Tab, Switches to the next window in the group, changegroupactive, f
|
|
||||||
|
|
||||||
# ======= Volume Control =======
|
|
||||||
|
|
||||||
bindel = , XF86AudioRaiseVolume, exec, ~/.config/hypr/scripts/volume-helper.sh up
|
|
||||||
bindel = , XF86AudioLowerVolume, exec, ~/.config/hypr/scripts/volume-helper.sh down
|
|
||||||
bindel = , XF86AudioMute, exec, ~/.config/hypr/scripts/volume-helper.sh toggle
|
|
||||||
|
|
||||||
# ======= Playback Control =======
|
|
||||||
|
|
||||||
bindd = , XF86AudioPlay, Toggles play/pause, exec, playerctl play-pause
|
|
||||||
bindd = , XF86AudioNext, Next track, exec, playerctl next
|
|
||||||
bindd = , XF86AudioPrev, Previous track, exec, playerctl previous
|
|
||||||
|
|
||||||
# ======= Screen Brightness =======
|
|
||||||
|
|
||||||
bindel = , XF86MonBrightnessUp, exec, brightnessctl s +5% #Increases brightness 5%
|
|
||||||
bindel = , XF86MonBrightnessDown, exec, brightnessctl s 5%- #Decreases brightness 5%
|
|
||||||
bindd = $mainMod SHIFT, P, Runs the calculator application, exec, gnome-calculator
|
|
||||||
bindd = $mainMod, L, Lock the screen, exec, ~/.config/swaylock/lockscript.sh
|
|
||||||
bindd = $mainMod, O, Reload/restarts Waybar, exec, killall -SIGUSR2 waybar
|
|
||||||
|
|
||||||
# ======= Window Actions =======
|
|
||||||
|
|
||||||
## Move window with mainMod + LMB/RMB and dragging
|
|
||||||
bindd = $mainMod, mouse:272, Move the window towards a direction, movewindow
|
|
||||||
## Move window towards a direction
|
|
||||||
bindd = $mainMod SHIFT, left, Move active window to the left, movewindow, l
|
|
||||||
bindd = $mainMod SHIFT, right, Move active window to the right, movewindow, r
|
|
||||||
bindd = $mainMod SHIFT, up, Move active window upwards, movewindow, u
|
|
||||||
bindd = $mainMod SHIFT, down, Move active window downwards, movewindow, d
|
|
||||||
## Move focus with mainMod + arrow keys
|
|
||||||
bindd = $mainMod, left, Move focus to the left, movefocus, l
|
|
||||||
bindd = $mainMod, right, Move focus to the right, movefocus, r
|
|
||||||
bindd = $mainMod, up, Move focus upwards, movefocus, u
|
|
||||||
bindd = $mainMod, down, Move focus downwards, movefocus, d
|
|
||||||
## Resizing windows
|
|
||||||
# Activate keyboard window resize mode
|
|
||||||
# https://wiki.hyprland.org/Configuring/Binds/#submaps
|
|
||||||
bindd = $mainMod, R, Activates window resizing mode, submap, resize
|
|
||||||
submap = resize
|
|
||||||
bindd = , right, Resize to the right (resizing mode), resizeactive, 15 0
|
|
||||||
bindd = , left, Resize to the left (resizing mode), resizeactive, -15 0
|
|
||||||
bindd = , up, Resize upwards (resizing mode), resizeactive, 0 -15
|
|
||||||
bindd = , down, Resize downwards (resizing mode), resizeactive, 0 15
|
|
||||||
bindd = , l, Resize to the right (resizing mode), resizeactive, 15 0
|
|
||||||
bindd = , h, Resize to the left (resizing mode), resizeactive, -15 0
|
|
||||||
bindd = , k, Resize upwards (resizing mode), resizeactive, 0 -15
|
|
||||||
bindd = , j, Resize downwards (resizing mode), resizeactive, 0 15
|
|
||||||
bindd = , escape, Ends window resizing mode, submap, reset
|
|
||||||
submap = reset
|
|
||||||
# Quick resize window with keyboard
|
|
||||||
# !!! added $mainMod here because CTRL + SHIFT is used for word selection in various text editors
|
|
||||||
bindd = $mainMod CTRL SHIFT, right, Resize to the right, resizeactive, 25 0
|
|
||||||
bindd = $mainMod CTRL SHIFT, left, Resize to the left, resizeactive, -25 0
|
|
||||||
bindd = $mainMod CTRL SHIFT, up, Resize upwards, resizeactive, 0 -25
|
|
||||||
bindd = $mainMod CTRL SHIFT, down, Resize downwards, resizeactive, 0 25
|
|
||||||
bindd = $mainMod CTRL SHIFT, l, Resize to the right, resizeactive, 15 0
|
|
||||||
bindd = $mainMod CTRL SHIFT, h, Resize to the left, resizeactive, -15 0
|
|
||||||
bindd = $mainMod CTRL SHIFT, k, Resize upwards, resizeactive, 0 -15
|
|
||||||
bindd = $mainMod CTRL SHIFT, j, Resize downwards, resizeactive, 0 15
|
|
||||||
# Resize window with mainMod + LMB/RMB and dragging
|
|
||||||
bindm = $mainMod, mouse:273, resizewindow #Resize the window towards a direction
|
|
||||||
bindm = $mainMod, mouse:272, movewindow #Drag window
|
|
||||||
## Resizing Windows End #
|
|
||||||
## Move active window to a workspace with $mainMod + CTRL + [0-9]
|
|
||||||
bindd = $mainMod CTRL, 1, Move window and switch to workspace 1, movetoworkspace, 1
|
|
||||||
bindd = $mainMod CTRL, 2, Move window and switch to workspace 2, movetoworkspace, 2
|
|
||||||
bindd = $mainMod CTRL, 3, Move window and switch to workspace 3, movetoworkspace, 3
|
|
||||||
bindd = $mainMod CTRL, 4, Move window and switch to workspace 4, movetoworkspace, 4
|
|
||||||
bindd = $mainMod CTRL, 5, Move window and switch to workspace 5, movetoworkspace, 5
|
|
||||||
bindd = $mainMod CTRL, 6, Move window and switch to workspace 6, movetoworkspace, 6
|
|
||||||
bindd = $mainMod CTRL, 7, Move window and switch to workspace 7, movetoworkspace, 7
|
|
||||||
bindd = $mainMod CTRL, 8, Move window and switch to workspace 8, movetoworkspace, 8
|
|
||||||
bindd = $mainMod CTRL, 9, Move window and switch to workspace 9, movetoworkspace, 9
|
|
||||||
bindd = $mainMod CTRL, 0, Move window and switch to workspace 10, movetoworkspace, 10
|
|
||||||
bindd = $mainMod CTRL, left, Move window and switch to the next workspace, movetoworkspace, -1
|
|
||||||
bindd = $mainMod CTRL, right, Move window and switch to the previous workspace, movetoworkspace, +1
|
|
||||||
## Same as above, but doesn't switch to the workspace
|
|
||||||
bindd = $mainMod SHIFT, 1, Move window silently to workspace 1, movetoworkspacesilent, 1
|
|
||||||
bindd = $mainMod SHIFT, 2, Move window silently to workspace 2, movetoworkspacesilent, 2
|
|
||||||
bindd = $mainMod SHIFT, 3, Move window silently to workspace 3, movetoworkspacesilent, 3
|
|
||||||
bindd = $mainMod SHIFT, 4, Move window silently to workspace 4, movetoworkspacesilent, 4
|
|
||||||
bindd = $mainMod SHIFT, 5, Move window silently to workspace 5, movetoworkspacesilent, 5
|
|
||||||
bindd = $mainMod SHIFT, 6, Move window silently to workspace 6, movetoworkspacesilent, 6
|
|
||||||
bindd = $mainMod SHIFT, 7, Move window silently to workspace 7, movetoworkspacesilent, 7
|
|
||||||
bindd = $mainMod SHIFT, 8, Move window silently to workspace 8, movetoworkspacesilent, 8
|
|
||||||
bindd = $mainMod SHIFT, 9, Move window silently to workspace 9, movetoworkspacesilent, 9
|
|
||||||
bindd = $mainMod SHIFT, 0, Move window silently to workspace 10, movetoworkspacesilent, 10
|
|
||||||
# Window actions End #
|
|
||||||
# ======= Workspace Actions =======
|
|
||||||
|
|
||||||
# Switch workspaces with mainMod + [0-9]
|
|
||||||
bindd = $mainMod, 1, Switch to workspace 1, workspace, 1
|
|
||||||
bindd = $mainMod, 2, Switch to workspace 2, workspace, 2
|
|
||||||
bindd = $mainMod, 3, Switch to workspace 3, workspace, 3
|
|
||||||
bindd = $mainMod, 4, Switch to workspace 4, workspace, 4
|
|
||||||
bindd = $mainMod, 5, Switch to workspace 5, workspace, 5
|
|
||||||
bindd = $mainMod, 6, Switch to workspace 6, workspace, 6
|
|
||||||
bindd = $mainMod, 7, Switch to workspace 7, workspace, 7
|
|
||||||
bindd = $mainMod, 8, Switch to workspace 8, workspace, 8
|
|
||||||
bindd = $mainMod, 9, Switch to workspace 9, workspace, 9
|
|
||||||
bindd = $mainMod, 0, Switch to workspace 10, workspace, 10
|
|
||||||
# Scroll through existing workspaces with mainMod + , or .
|
|
||||||
bindd = $mainMod, PERIOD, Scroll through workspaces incrementally, workspace, e+1
|
|
||||||
bindd = $mainMod, COMMA, Scroll through workspaces decrementally, workspace, e-1
|
|
||||||
# With $mainMod + Alt + Left / Right
|
|
||||||
bindd = $mainMod ALT, left, Switch to the previous workspace, workspace, e-1
|
|
||||||
bindd = $mainMod ALT, right, Switch to the next workspace, workspace, e+1
|
|
||||||
# With $mainMod + scroll
|
|
||||||
bindd = $mainMod, mouse_down, Scroll through workspaces incrementally, workspace, e+1
|
|
||||||
bindd = $mainMod, mouse_up, Scroll through workspaces decrementally, workspace, e-1
|
|
||||||
bindd = $mainMod, slash, Switch to the previous workspace, workspace, previous
|
|
||||||
# Special workspaces (scratchpads)
|
|
||||||
bindd = $mainMod, minus, Move active window to Special workspace, movetoworkspace,special
|
|
||||||
bindd = $mainMod, equal, Toggles the Special workspace, togglespecialworkspace, special
|
|
||||||
bindd = $mainMod, F1, Call special workspace scratchpad, togglespecialworkspace, scratchpad
|
|
||||||
bindd = $mainMod ALT SHIFT, F1, Move active window to s~/.config/hypr/scripts/pecial workspace scratchpad, movetoworkspacesilent, special:scratchpad
|
|
||||||
|
|
||||||
# ======= Screenshot =======
|
|
||||||
# Screenshot a window
|
|
||||||
bind = $mainMod, PRINT, exec, hyprshot -m window
|
|
||||||
# Screenshot a monitor
|
|
||||||
bind = , PRINT, exec, hyprshot -m output
|
|
||||||
# Screenshot a region
|
|
||||||
bind = $shiftMod, PRINT, exec, hyprshot -m region
|
|
||||||
# Screenrec
|
|
||||||
bind = $mainMod, S, exec, ~/.config/hypr/scripts/record-or-screenshot.sh -d "$dmenu"
|
|
||||||
|
|
||||||
# ======= Color Picker =======
|
|
||||||
bindd = $mainMod, P, Launch hyprpicker to pick a color, exec, hyprpicker -a
|
|
||||||
|
|
||||||
# ======= Additional Settings =======
|
|
||||||
#bind = $mainMod, c, exec, cliphist list | tofi --prompt-text="clip:" | cliphist decode | wl-copy
|
|
||||||
bind = $mainMod, c, exec, vicinae vicinae://extensions/vicinae/clipboard/history
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Binds
|
|
||||||
binds {
|
|
||||||
allow_workspace_cycles = 1
|
|
||||||
workspace_back_and_forth = 1
|
|
||||||
workspace_center_on = 1
|
|
||||||
movefocus_cycles_fullscreen = true
|
|
||||||
window_direction_monitor_fallback = true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Toggle Waybar
|
|
||||||
bind = $mainMod, W, exec, killall -SIGUSR1 waybar
|
|
||||||
bind = $mainMod SHIFT, W, exec, killall -SIGUSR2 waybar # restart
|
|
||||||
|
|
||||||
# ======= Monitors =======
|
|
||||||
# Toggle Monitor Flip
|
|
||||||
bind = $mainMod, F7, exec, ~/.config/hypr/scripts/rotate_current_screen.sh
|
|
||||||
|
|
||||||
bind = $mainMod, F8, exec, ~/.config/hypr/scripts/monitor-toggle.sh toggle-externals
|
|
||||||
# bind = $mainMod, F8, exec, ~/.config/hypr/scripts/monitor-toggle.sh laptop
|
|
||||||
bind = $mainMod, F9, exec, ~/.config/hypr/scripts/monitor-toggle.sh dual
|
|
||||||
bind = $mainMod, F10, exec, ~/.config/hypr/scripts/monitor-toggle.sh triple
|
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
-- config/keybinds.lua
|
||||||
|
-- All keybinds, translated from keybinds.conf to the 0.55 Lua API.
|
||||||
|
-- Modifier shorthand used: "SUPER" = $mainMod.
|
||||||
|
|
||||||
|
local S = "SUPER"
|
||||||
|
|
||||||
|
-- ── Applications ─────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + RETURN", hl.dsp.exec_cmd(terminal))
|
||||||
|
hl.bind(S .. " + E", hl.dsp.exec_cmd(filemanager))
|
||||||
|
hl.bind(S .. " + Q", hl.dsp.window.close())
|
||||||
|
hl.bind(S .. " + SHIFT + M", hl.dsp.exec_cmd('loginctl terminate-user ""'))
|
||||||
|
hl.bind(S .. " + V", hl.dsp.window.float({ action = "toggle" }))
|
||||||
|
hl.bind(S .. " + SPACE", hl.dsp.exec_cmd(applauncher))
|
||||||
|
hl.bind(S .. " + F", hl.dsp.window.fullscreen())
|
||||||
|
hl.bind(S .. " + Y", hl.dsp.window.pin())
|
||||||
|
hl.bind(S .. " + J", hl.dsp.layout("togglesplit")) -- dwindle only
|
||||||
|
|
||||||
|
-- ── Groups ───────────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + K", hl.dsp.group.toggle())
|
||||||
|
-- hl.bind(S .. " + Tab", hl.dsp.group.next( window ))
|
||||||
|
|
||||||
|
hl.bind(S .. " + Tab", function()
|
||||||
|
local w = hl.get_active_window()
|
||||||
|
if w and w.group then
|
||||||
|
hl.dispatch(hl.dsp.group.next())
|
||||||
|
else
|
||||||
|
hl.dispatch(hl.dsp.exec_cmd("qs ipc -c own call overview toggle"))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- ── Volume ───────────────────────────────────────────────────
|
||||||
|
local vol = "~/.config/hypr/scripts/volume-helper.sh"
|
||||||
|
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd(vol .. " up"), { repeating = true })
|
||||||
|
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd(vol .. " down"), { repeating = true })
|
||||||
|
hl.bind("XF86AudioMute", hl.dsp.exec_cmd(vol .. " toggle"), { repeating = true })
|
||||||
|
|
||||||
|
-- ── Playback ─────────────────────────────────────────────────
|
||||||
|
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"))
|
||||||
|
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"))
|
||||||
|
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"))
|
||||||
|
|
||||||
|
-- ── Brightness ───────────────────────────────────────────────
|
||||||
|
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s +5%"), { repeating = true })
|
||||||
|
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 5%-"), { repeating = true })
|
||||||
|
|
||||||
|
-- ── Utilities ────────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + SHIFT + P", hl.dsp.exec_cmd("gnome-calculator"))
|
||||||
|
hl.bind(S .. " + L", hl.dsp.exec_cmd("~/.config/swaylock/lockscript.sh"))
|
||||||
|
hl.bind(S .. " + P", hl.dsp.exec_cmd("hyprpicker -a"))
|
||||||
|
hl.bind(S .. " + C", hl.dsp.exec_cmd("vicinae vicinae://extensions/vicinae/clipboard/history"))
|
||||||
|
|
||||||
|
-- ── Screenshots ──────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + PRINT", hl.dsp.exec_cmd("hyprshot -m window"))
|
||||||
|
hl.bind("PRINT", hl.dsp.exec_cmd("hyprshot -m output"))
|
||||||
|
hl.bind("SHIFT + PRINT", hl.dsp.exec_cmd("hyprshot -m region"))
|
||||||
|
hl.bind(S .. " + S", hl.dsp.exec_cmd('~/.config/hypr/scripts/record-or-screenshot.sh -d "' .. dmenu .. '"'))
|
||||||
|
|
||||||
|
-- ── Window focus ─────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + left", hl.dsp.focus({ direction = "l" }))
|
||||||
|
hl.bind(S .. " + right", hl.dsp.focus({ direction = "r" }))
|
||||||
|
hl.bind(S .. " + up", hl.dsp.focus({ direction = "u" }))
|
||||||
|
hl.bind(S .. " + down", hl.dsp.focus({ direction = "d" }))
|
||||||
|
|
||||||
|
-- ── Window move ──────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + SHIFT + left", hl.dsp.window.move({ direction = "l" }))
|
||||||
|
hl.bind(S .. " + SHIFT + right", hl.dsp.window.move({ direction = "r" }))
|
||||||
|
hl.bind(S .. " + SHIFT + up", hl.dsp.window.move({ direction = "u" }))
|
||||||
|
hl.bind(S .. " + SHIFT + down", hl.dsp.window.move({ direction = "d" }))
|
||||||
|
|
||||||
|
-- Mouse drag / resize
|
||||||
|
hl.bind(S .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
|
||||||
|
hl.bind(S .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
|
||||||
|
|
||||||
|
-- ── Workspace Move ───────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + SHIFT + TAB", hl.dsp.workspace.move({ monitor = "+1" }))
|
||||||
|
|
||||||
|
-- ── Resize submap ────────────────────────────────────────────
|
||||||
|
hl.bind(S .. " + SHIFT + R", hl.dsp.submap("resize"))
|
||||||
|
hl.define_submap("resize", function()
|
||||||
|
-- Set repeating binds for resizing the active window.
|
||||||
|
hl.bind("right", hl.dsp.window.resize({ x = 10, y = 0, relative = true }), { repeating = true })
|
||||||
|
hl.bind("left", hl.dsp.window.resize({ x = -10, y = 0, relative = true }), { repeating = true })
|
||||||
|
hl.bind("up", hl.dsp.window.resize({ x = 0, y = 10, relative = true }), { repeating = true })
|
||||||
|
hl.bind("down", hl.dsp.window.resize({ x = 0, y = -10, relative = true }), { repeating = true })
|
||||||
|
|
||||||
|
-- Use `reset` to go back to the global submap
|
||||||
|
hl.bind("escape", hl.dsp.submap("reset"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- ── Workspace switching ───────────────────────────────────────
|
||||||
|
for i = 1, 10 do
|
||||||
|
local key = tostring(i % 10)
|
||||||
|
local ws = tostring(i)
|
||||||
|
hl.bind(S .. " + " .. key, hl.dsp.focus({ workspace = ws }))
|
||||||
|
hl.bind(S .. " + CTRL + " .. key, hl.dsp.window.move({ workspace = ws, follow = true }))
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.bind(S .. " + PERIOD", hl.dsp.focus({ workspace = "e+1" }))
|
||||||
|
hl.bind(S .. " + COMMA", hl.dsp.focus({ workspace = "e-1" }))
|
||||||
|
hl.bind(S .. " + ALT_L + right", hl.dsp.focus({ workspace = "e+1" }))
|
||||||
|
hl.bind(S .. " + ALT_L + left", hl.dsp.focus({ workspace = "e-1" }))
|
||||||
|
hl.bind(S .. " + CTRL + right", hl.dsp.window.move({ workspace = "+1", follow = true }))
|
||||||
|
hl.bind(S .. " + CTRL + left", hl.dsp.window.move({ workspace = "-1", follow = true }))
|
||||||
|
hl.bind(S .. " + slash", hl.dsp.focus({ workspace = "previous" }))
|
||||||
|
hl.bind(S .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }), { mouse = true })
|
||||||
|
hl.bind(S .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }), { mouse = true })
|
||||||
|
|
||||||
|
-- Special / scratchpad
|
||||||
|
hl.bind(S .. " + minus", hl.dsp.window.move({ workspace = "special" }))
|
||||||
|
hl.bind(S .. " + equal", hl.dsp.workspace.toggle_special())
|
||||||
|
hl.bind(S .. " + F1", hl.dsp.workspace.toggle_special({ name = "scratchpad" }))
|
||||||
|
hl.bind(S .. " + SHIFT + ALT_L + F1", hl.dsp.window.move({ workspace = "special:scratchpad", follow = false }))
|
||||||
|
|
||||||
|
-- Quickshell overview
|
||||||
|
-- hl.bind(S .. " + TAB", hl.dsp.exec_cmd("qs ipc -c overview call overview toggle"))
|
||||||
|
|
||||||
|
-- ── Monitors ─────────────────────────────────────────────────
|
||||||
|
-- hl.bind(S .. " + F8", function() monitors.solo() end)
|
||||||
|
-- hl.bind(S .. " + F9", function() monitors.dual() end)
|
||||||
|
-- hl.bind(S .. " + F10", function() monitors.triple() end)
|
||||||
|
|
||||||
|
-- QuickShell
|
||||||
|
hl.bind(S .. " + W", hl.dsp.exec_cmd("echo 1 > /tmp/qs-wallpaper-ipc"))
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Monitor Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
# Monitor wiki https://wiki.hyprland.org/0.45.0/Configuring/Monitors/
|
|
||||||
|
|
||||||
monitor = eDP-2, highres@highrr, 0x0, 1, vrr, 1
|
|
||||||
|
|
||||||
#monitor = HDMI-A-1, highres@highrr, auto-right, 1, transform, 1
|
|
||||||
#monitor = desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1
|
|
||||||
monitor = desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, 1920x0, 1#, transform, 1
|
|
||||||
|
|
||||||
# monitor = HDMI-A-1, highres@highrr, auto-right, 1, vrr, 0
|
|
||||||
|
|
||||||
# If you need to scale things like steam etc, please uncomment these lines.
|
|
||||||
# Adjust GDK_SCALE accordingly to your liking.
|
|
||||||
#xwayland {
|
|
||||||
# force_zero_scaling = true # Unscale XWayland
|
|
||||||
#}
|
|
||||||
|
|
||||||
#env = GDK_SCALE, 1.25 # GDK Scaling Factor
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
-- config/monitors.lua
|
||||||
|
--
|
||||||
|
-- Monitor layout switching via shell script (wlr-randr).
|
||||||
|
-- hl.monitor({ disabled=false }) cannot re-enable monitors at runtime,
|
||||||
|
-- so all switching is delegated to monitor-toggle.sh.
|
||||||
|
|
||||||
|
--[[ local layouts = {}
|
||||||
|
local script = os.getenv("HOME") .. "/.config/hypr/scripts/monitor-toggle.sh"
|
||||||
|
|
||||||
|
function layouts.solo() hl.exec_cmd(script .. " laptop") end
|
||||||
|
function layouts.dual() hl.exec_cmd(script .. " dual") end
|
||||||
|
function layouts.triple() hl.exec_cmd(script .. " triple") end
|
||||||
|
|
||||||
|
-- Apply default at startup
|
||||||
|
layouts.dual()
|
||||||
|
|
||||||
|
-- Expose for keybinds.lua
|
||||||
|
return layouts
|
||||||
|
]]
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "eDP-2",
|
||||||
|
mode = "1920x1080@144",
|
||||||
|
position = "0x0",
|
||||||
|
scale = 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.monitor({
|
||||||
|
output = "HDMI-A-1",
|
||||||
|
mode = "1920x1080@60",
|
||||||
|
position = "auto-left",
|
||||||
|
scale = 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- hl.monitor({
|
||||||
|
-- output = "DP-2",
|
||||||
|
-- mode = "1920x1080@60",
|
||||||
|
-- position = "auto-right",
|
||||||
|
-- scale = 1.2,
|
||||||
|
-- transform = 1
|
||||||
|
--})
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# https://github.com/sandwichfarm/hyprexpo-plus
|
|
||||||
# currently not working
|
|
||||||
plugin {
|
|
||||||
hyprexpo {
|
|
||||||
columns = 3
|
|
||||||
gap_size = 5
|
|
||||||
workspace_method = center current # [center/first] [workspace] e.g. first 1 or center m+1
|
|
||||||
keynav_enable = 1
|
|
||||||
keynav_wrap_h = 1 # wrap horizontally at row edges
|
|
||||||
keynav_wrap_v = 1 # wrap vertically at column edges
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
hl.gesture({ fingers = 3, direction = "horizontal", action = "workspace" })
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Variables Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
source = ~/.config/hypr/config/colors.conf
|
|
||||||
|
|
||||||
# https://wiki.hypr.land/Configuring/Variables/#general
|
|
||||||
general {
|
|
||||||
gaps_in = 2
|
|
||||||
gaps_out = 1
|
|
||||||
border_size = 1
|
|
||||||
col.active_border = $color4
|
|
||||||
col.inactive_border = $color2
|
|
||||||
layout = dwindle # master|dwindle
|
|
||||||
resize_on_border = true
|
|
||||||
extend_border_grab_area = 10
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#snap
|
|
||||||
snap {
|
|
||||||
enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#group
|
|
||||||
group {
|
|
||||||
col.border_active = $color6
|
|
||||||
col.border_inactive = $color2
|
|
||||||
col.border_locked_active = $color3
|
|
||||||
col.border_locked_inactive = $color0
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#groupbar
|
|
||||||
groupbar {
|
|
||||||
font_size = 13
|
|
||||||
height = 1
|
|
||||||
indicator_height = 16
|
|
||||||
rounding = 2
|
|
||||||
font_family = "Roboto Sanss"
|
|
||||||
text_color = $foreground
|
|
||||||
text_offset = -8
|
|
||||||
font_weight_active = ultraheavy
|
|
||||||
font_weight_inactive = semibold
|
|
||||||
col.active = $color6
|
|
||||||
col.inactive = $color2
|
|
||||||
col.locked_active = $color3
|
|
||||||
col.locked_inactive = $color0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#misc
|
|
||||||
misc {
|
|
||||||
font_family = "Roboto Sans"
|
|
||||||
splash_font_family = "Roboto Sans"
|
|
||||||
disable_hyprland_logo = true
|
|
||||||
col.splash = $color2
|
|
||||||
background_color = $background
|
|
||||||
enable_swallow = true
|
|
||||||
swallow_regex = ^(cachy-browser|firefox|nautilus|nemo|thunar|btrfs-assistant.)$
|
|
||||||
focus_on_activate = true
|
|
||||||
vrr = 0
|
|
||||||
# Hypridle cmatrix fix:
|
|
||||||
session_lock_xray = true
|
|
||||||
middle_click_paste = false
|
|
||||||
}
|
|
||||||
|
|
||||||
# https://wiki.hyprland.org/0.45.0/Configuring/Variables/#render
|
|
||||||
render {
|
|
||||||
direct_scanout = true
|
|
||||||
}
|
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/0.45.0/Configuring/Dwindle-Layout/ for more
|
|
||||||
dwindle {
|
|
||||||
special_scale_factor = 0.8
|
|
||||||
pseudotile = true # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
|
||||||
preserve_split = true
|
|
||||||
}
|
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/0.45.0/Configuring/Master-Layout/ for more
|
|
||||||
master {
|
|
||||||
new_status = master
|
|
||||||
special_scale_factor = 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor {
|
|
||||||
no_hardware_cursors = true
|
|
||||||
}
|
|
||||||
|
|
||||||
device {
|
|
||||||
name = logitech-gaming-mouse-g502
|
|
||||||
sensitivity=0.9
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
-- config/variables.lua
|
||||||
|
-- Mirrors: variables.conf + decorations.conf + animations.conf
|
||||||
|
|
||||||
|
-- ── Application shortcuts ─────────────────────────────────────
|
||||||
|
terminal = "kitty"
|
||||||
|
filemanager = "nemo"
|
||||||
|
applauncher = "vicinae toggle"
|
||||||
|
dmenu = "vicinae dmenu --placeholder"
|
||||||
|
-- applauncher = "rofi -show run"
|
||||||
|
-- dmenu = "rofi -show drun -theme-str 'window {width: 100%;}'"
|
||||||
|
idlehandler = "hypridle" -- switched to hypridle; adjust if needed
|
||||||
|
|
||||||
|
-- ── General ───────────────────────────────────────────────────
|
||||||
|
hl.config({
|
||||||
|
general = {
|
||||||
|
gaps_in = 2,
|
||||||
|
gaps_out = 1,
|
||||||
|
border_size = 1,
|
||||||
|
["col.active_border"] = color4,
|
||||||
|
["col.inactive_border"] = color2,
|
||||||
|
layout = "dwindle",
|
||||||
|
resize_on_border = true,
|
||||||
|
extend_border_grab_area = 10,
|
||||||
|
snap = { enabled = true },
|
||||||
|
},
|
||||||
|
|
||||||
|
group = {
|
||||||
|
["col.border_active"] = color9,
|
||||||
|
["col.border_inactive"] = color2,
|
||||||
|
["col.border_locked_active"] = color3,
|
||||||
|
["col.border_locked_inactive"] = color0,
|
||||||
|
groupbar = {
|
||||||
|
font_size = 12,
|
||||||
|
height = 1,
|
||||||
|
indicator_height = 16,
|
||||||
|
rounding = 5,
|
||||||
|
font_family = "Roboto Sans",
|
||||||
|
text_color = background,
|
||||||
|
text_offset = -10,
|
||||||
|
font_weight_active = "bold",
|
||||||
|
font_weight_inactive = "normal",
|
||||||
|
["col.active"] = color4,
|
||||||
|
["col.inactive"] = color2,
|
||||||
|
["col.locked_active"] = color3,
|
||||||
|
["col.locked_inactive"] = color0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
-- ── Decorations ───────────────────────────────────────────
|
||||||
|
decoration = {
|
||||||
|
active_opacity = 1.0,
|
||||||
|
inactive_opacity = 0.7,
|
||||||
|
rounding = 5,
|
||||||
|
dim_inactive = true,
|
||||||
|
dim_strength = 0.1,
|
||||||
|
blur = {
|
||||||
|
size = 30,
|
||||||
|
passes = 2,
|
||||||
|
xray = true,
|
||||||
|
noise = 0.01,
|
||||||
|
},
|
||||||
|
shadow = {
|
||||||
|
enabled = true,
|
||||||
|
range = 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
-- ── Misc ──────────────────────────────────────────────────
|
||||||
|
misc = {
|
||||||
|
font_family = "Roboto Sans",
|
||||||
|
splash_font_family = "Roboto Sans",
|
||||||
|
disable_hyprland_logo = true,
|
||||||
|
["col.splash"] = color2,
|
||||||
|
background_color = background,
|
||||||
|
enable_swallow = false,
|
||||||
|
swallow_regex = "^(cachy-browser|firefox|nautilus|nemo|thunar|btrfs-assistant.)$",
|
||||||
|
focus_on_activate = true,
|
||||||
|
vrr = 0,
|
||||||
|
session_lock_xray = true,
|
||||||
|
middle_click_paste = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
render = {
|
||||||
|
direct_scanout = false,
|
||||||
|
},
|
||||||
|
|
||||||
|
dwindle = {
|
||||||
|
special_scale_factor = 0.8,
|
||||||
|
preserve_split = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
master = {
|
||||||
|
new_status = "master",
|
||||||
|
special_scale_factor = 0.8,
|
||||||
|
},
|
||||||
|
|
||||||
|
cursor = {
|
||||||
|
no_hardware_cursors = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
binds = {
|
||||||
|
allow_workspace_cycles = true,
|
||||||
|
workspace_back_and_forth = true,
|
||||||
|
workspace_center_on = 1,
|
||||||
|
movefocus_cycles_fullscreen = true,
|
||||||
|
window_direction_monitor_fallback = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.curve("smooth", { type = "bezier", points = { { 0, 0.05 }, { 0.44, 0.99 } } })
|
||||||
|
hl.animation({ leaf = "workspaces", enabled = true, speed = 3, bezier = "smooth", style = "slidefade" })
|
||||||
|
hl.animation({ leaf = "windowsIn", enabled = true, speed = 3, bezier = "smooth", style = "slidefade" })
|
||||||
|
hl.animation({ leaf = "windowsOut", enabled = true, speed = 3, bezier = "default", style = "slidefade" })
|
||||||
|
hl.animation({ leaf = "windowsMove", enabled = true, speed = 3, bezier = "default", style = "slidefade" })
|
||||||
|
hl.animation({ leaf = "border", enabled = true, speed = 5, bezier = "default" })
|
||||||
@@ -1,315 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Windowrules Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
# Windows Rules https://wiki.hyprland.org/0.45.0/Configuring/Window-Rules/ #
|
|
||||||
|
|
||||||
# Float Necessary Windows
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-1
|
|
||||||
float = on
|
|
||||||
match:title = Rofi
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-2
|
|
||||||
float = on
|
|
||||||
match:class = ^(org.pulseaudio.pavucontrol)
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-3
|
|
||||||
float = on
|
|
||||||
match:class = ^()$
|
|
||||||
match:title = ^(Picture in picture)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-4
|
|
||||||
float = on
|
|
||||||
match:class = ^()$
|
|
||||||
match:title = ^(Save File)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-5
|
|
||||||
float = on
|
|
||||||
match:class = ^()$
|
|
||||||
match:title = ^(Open File)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-6
|
|
||||||
float = on
|
|
||||||
match:class = ^(LibreWolf)$
|
|
||||||
match:title = ^(Picture-in-Picture)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-7
|
|
||||||
float = on
|
|
||||||
match:class = ^(blueman-manager)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-8
|
|
||||||
float = on
|
|
||||||
match:class = ^(xdg-desktop-portal-gtk|xdg-desktop-portal-kde|xdg-desktop-portal-hyprland)(.*)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-9
|
|
||||||
float = on
|
|
||||||
size = 260 340
|
|
||||||
match:class = ^(pomodorolm)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-10
|
|
||||||
float = on
|
|
||||||
match:title = ^(Extension:.*)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-11
|
|
||||||
float = on
|
|
||||||
match:class = ^(polkit-gnome-authentication-agent-1|hyprpolkitagent|org.org.kde.polkit-kde-authentication-agent-1)(.*)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-12
|
|
||||||
float = on
|
|
||||||
match:class = ^(CachyOSHello)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-13
|
|
||||||
float = on
|
|
||||||
match:class = ^(zenity)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-14
|
|
||||||
float = on
|
|
||||||
match:class = ^()$
|
|
||||||
match:title = ^(Steam - Self Updater)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-15
|
|
||||||
float = on
|
|
||||||
match:class = ^(Zotero)$
|
|
||||||
match:title = ^(Progress)$
|
|
||||||
}
|
|
||||||
|
|
||||||
# Increase the opacity
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-16
|
|
||||||
opacity = 0.92
|
|
||||||
match:class = ^(thunar|nemo|dolphin)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-17
|
|
||||||
opacity = 0.96
|
|
||||||
match:class = ^(discord|armcord|webcord)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-18
|
|
||||||
opacity = 0.95
|
|
||||||
match:title = ^(QQ|Telegram)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-19
|
|
||||||
opacity = 0.95
|
|
||||||
match:title = ^(NetEase Cloud Music Gtk4)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-20
|
|
||||||
opacity = 1
|
|
||||||
match:class = ^(kitty)$
|
|
||||||
}
|
|
||||||
|
|
||||||
# General window rules
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-21
|
|
||||||
float = on
|
|
||||||
size = 960 540
|
|
||||||
move = ((monitor_w*0.25)-)
|
|
||||||
match:title = ^(Picture-in-Picture)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-22
|
|
||||||
float = on
|
|
||||||
match:title = ^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp|nwg-look|nwg-displays)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-23
|
|
||||||
move = ((monitor_w*0.25)-)
|
|
||||||
size = 960 540
|
|
||||||
match:title = ^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-24
|
|
||||||
pin = on
|
|
||||||
match:title = ^(danmufloat)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-25
|
|
||||||
rounding = 5
|
|
||||||
match:title = ^(danmufloat|termfloat)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-26
|
|
||||||
animation = slide right
|
|
||||||
match:class = ^(kitty|Alacritty)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-27
|
|
||||||
no_blur = on
|
|
||||||
match:class = ^(org.mozilla.firefox)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-28
|
|
||||||
no_dim = on
|
|
||||||
match:class = ^(zen)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-29
|
|
||||||
float = on
|
|
||||||
match:title = ^(Zotero Settings)$
|
|
||||||
}
|
|
||||||
|
|
||||||
# Decorations related to floating windows on workspaces 1 to 10
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-30
|
|
||||||
border_size = 2
|
|
||||||
border_color = $color4
|
|
||||||
rounding = 8
|
|
||||||
match:float = 1
|
|
||||||
match:workspace = w[fv1-10]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Decorations related to tiling windows on workspaces 1 to 10
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-31
|
|
||||||
border_size = 3
|
|
||||||
rounding = 4
|
|
||||||
match:float = 0
|
|
||||||
match:workspace = f[1-10]
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-32
|
|
||||||
opacity = 0.9
|
|
||||||
match:title = ^(.+nvim$)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Windows Rules End #
|
|
||||||
|
|
||||||
# Workspaces Rules https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/ #
|
|
||||||
# workspace = 1, default:true, monitor:$priMon
|
|
||||||
# workspace = 6, default:true, monitor:$secMon
|
|
||||||
# Workspace selectors https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/#workspace-selectors
|
|
||||||
# workspace = r[1-5], monitor:$priMon
|
|
||||||
# workspace = r[6-10], monitor:$secMon
|
|
||||||
# workspace = special:scratchpad, on-created-empty:$applauncher
|
|
||||||
# no_gaps_when_only deprecated instead workspaces rules with selectors can do the same
|
|
||||||
# Smart gaps from 0.45.0 https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/#smart-gaps
|
|
||||||
#workspace = w[tv1-10], gapsout:5, gapsin:3
|
|
||||||
#workspace = f[1], gapsout:5, gapsin:3
|
|
||||||
# Workspaces Rules End #
|
|
||||||
|
|
||||||
# Layers Rules #
|
|
||||||
layerrule {
|
|
||||||
name = layerrule-1
|
|
||||||
animation = slide top
|
|
||||||
match:namespace = logout_dialog
|
|
||||||
}
|
|
||||||
|
|
||||||
# layerrule = animation popin 50%, waybar
|
|
||||||
layerrule {
|
|
||||||
name = layerrule-2
|
|
||||||
animation = slide down
|
|
||||||
match:namespace = waybar
|
|
||||||
}
|
|
||||||
|
|
||||||
layerrule {
|
|
||||||
name = layerrule-3
|
|
||||||
animation = fade 50%
|
|
||||||
match:namespace = wallpaper
|
|
||||||
}
|
|
||||||
|
|
||||||
# vicinae
|
|
||||||
layerrule {
|
|
||||||
name = vicinae-blur
|
|
||||||
blur = on
|
|
||||||
animation = popin
|
|
||||||
dim_around = on
|
|
||||||
ignore_alpha = 1
|
|
||||||
match:namespace = vicinae
|
|
||||||
}
|
|
||||||
|
|
||||||
# Layers Rules End #
|
|
||||||
|
|
||||||
# Zotero Libreoffice
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-33
|
|
||||||
float = on
|
|
||||||
center = on
|
|
||||||
no_anim = on
|
|
||||||
match:class = ^(Zotero)$
|
|
||||||
match:title = ^(Citation Dialog)$
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = libreoffice-opaque
|
|
||||||
match:class = ^(libreoffice.*)$
|
|
||||||
|
|
||||||
# active, inactive, fullscreen (all forced to 1.0)
|
|
||||||
opacity = 1.0 override 1.0 override 1.0 override
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# gnome calendar
|
|
||||||
windowrule {
|
|
||||||
name = windowrule-cal
|
|
||||||
float = on
|
|
||||||
center = on
|
|
||||||
match:class = ^(org.gnome.Calendar)$
|
|
||||||
match:title = ^(Calendar)$
|
|
||||||
animation = popin
|
|
||||||
opacity = 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = waypaper-fix
|
|
||||||
match:class = ^(waypaper)$
|
|
||||||
float = on
|
|
||||||
center = on
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = calcure
|
|
||||||
match:title = ^(calcure)$
|
|
||||||
float = on
|
|
||||||
center = on
|
|
||||||
}
|
|
||||||
|
|
||||||
windowrule {
|
|
||||||
name = gnome-calculator-fix
|
|
||||||
match:class = ^(org.gnome.Calculator)$
|
|
||||||
float = on
|
|
||||||
center = on
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Windowrules Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
# Windows Rules https://wiki.hyprland.org/0.45.0/Configuring/Window-Rules/ #
|
|
||||||
|
|
||||||
# Float Necessary Windows
|
|
||||||
windowrule = float, title:Rofi
|
|
||||||
windowrule = float, class:^(org.pulseaudio.pavucontrol)
|
|
||||||
windowrule = float, class:^()$,title:^(Picture in picture)$
|
|
||||||
windowrule = float, class:^()$,title:^(Save File)$
|
|
||||||
windowrule = float, class:^()$,title:^(Save File)$
|
|
||||||
windowrule = float, class:^()$,title:^(Open File)$
|
|
||||||
windowrule = float, class:^(LibreWolf)$,title:^(Picture-in-Picture)$
|
|
||||||
windowrule = float, class:^(blueman-manager)$
|
|
||||||
windowrule = float, class:^(xdg-desktop-portal-gtk|xdg-desktop-portal-kde|xdg-desktop-portal-hyprland)(.*)$
|
|
||||||
windowrule = float, class:^(pomodorolm)$
|
|
||||||
windowrule = float, title:^(Extension:.*)$
|
|
||||||
windowrule = float, class:^(polkit-gnome-authentication-agent-1|hyprpolkitagent|org.org.kde.polkit-kde-authentication-agent-1)(.*)$
|
|
||||||
windowrule = float, class:^(CachyOSHello)$
|
|
||||||
windowrule = float, class:^(zenity)$
|
|
||||||
windowrule = float, class:^()$,title:^(Steam - Self Updater)$
|
|
||||||
windowrule = float, class:^(Zotero)$,title:^(Progress)$
|
|
||||||
# Increase the opacity
|
|
||||||
windowrule = opacity 0.92, class:^(thunar|nemo|dolphin)$
|
|
||||||
windowrule = opacity 0.96, class:^(discord|armcord|webcord)$
|
|
||||||
windowrule = opacity 0.95, title:^(QQ|Telegram)$
|
|
||||||
windowrule = opacity 0.95, title:^(NetEase Cloud Music Gtk4)$
|
|
||||||
windowrule = opacity 1, class:^(kitty)$
|
|
||||||
# General window rules
|
|
||||||
windowrule = size 260 340, class:^(pomodorolm)$
|
|
||||||
windowrule = float, title:^(Picture-in-Picture)$
|
|
||||||
windowrule = size 960 540, title:^(Picture-in-Picture)$
|
|
||||||
windowrule = move 25%-, title:^(Picture-in-Picture)$
|
|
||||||
windowrule = float, title:^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp|nwg-look|nwg-displays)$
|
|
||||||
windowrule = move 25%-, title:^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp)$
|
|
||||||
windowrule = size 960 540, title:^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp)$
|
|
||||||
windowrule = pin, title:^(danmufloat)$
|
|
||||||
windowrule = rounding 5, title:^(danmufloat|termfloat)$
|
|
||||||
windowrule = animation slide right, class:^(kitty|Alacritty)$
|
|
||||||
windowrule = noblur, class:^(org.mozilla.firefox)$
|
|
||||||
windowrule = nodim, class:^(zen)$
|
|
||||||
windowrule = float, title:^(Zotero Settings)$
|
|
||||||
# Decorations related to floating windows on workspaces 1 to 10
|
|
||||||
windowrule = bordersize 2, floating:1, onworkspace:w[fv1-10]
|
|
||||||
windowrule = bordercolor $color4, floating:1, onworkspace:w[fv1-10]
|
|
||||||
windowrule = rounding 8, floating:1, onworkspace:w[fv1-10]
|
|
||||||
# Decorations related to tiling windows on workspaces 1 to 10
|
|
||||||
windowrule = bordersize 3, floating:0, onworkspace:f[1-10]
|
|
||||||
windowrule = rounding 4, floating:0, onworkspace:f[1-10]
|
|
||||||
windowrule = opacity 0.9, title:^(.+nvim$)
|
|
||||||
# Windows Rules End #
|
|
||||||
|
|
||||||
# Workspaces Rules https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/ #
|
|
||||||
# workspace = 1, default:true, monitor:$priMon
|
|
||||||
# workspace = 6, default:true, monitor:$secMon
|
|
||||||
# Workspace selectors https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/#workspace-selectors
|
|
||||||
# workspace = r[1-5], monitor:$priMon
|
|
||||||
# workspace = r[6-10], monitor:$secMon
|
|
||||||
# workspace = special:scratchpad, on-created-empty:$applauncher
|
|
||||||
# no_gaps_when_only deprecated instead workspaces rules with selectors can do the same
|
|
||||||
# Smart gaps from 0.45.0 https://wiki.hyprland.org/0.45.0/Configuring/Workspace-Rules/#smart-gaps
|
|
||||||
#workspace = w[tv1-10], gapsout:5, gapsin:3
|
|
||||||
#workspace = f[1], gapsout:5, gapsin:3
|
|
||||||
# Workspaces Rules End #
|
|
||||||
|
|
||||||
# Layers Rules #
|
|
||||||
layerrule = animation slide top, logout_dialog
|
|
||||||
# layerrule = animation popin 50%, waybar
|
|
||||||
layerrule = animation slide down, waybar
|
|
||||||
layerrule = animation fade 50%, wallpaper
|
|
||||||
# Layers Rules End #
|
|
||||||
|
|
||||||
# Zotero Libreoffice
|
|
||||||
windowrule = float,class:^(Zotero)$,title:^(Citation Dialog)$
|
|
||||||
windowrule = center,class:^(Zotero)$,title:^(Citation Dialog)$
|
|
||||||
windowrule = noanim,class:^(Zotero)$,title:^(Citation Dialog)$
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
-- config/windowrules.lua
|
||||||
|
-- Translated from config_windowrules.conf to hl.window_rule() / hl.layer_rule().
|
||||||
|
|
||||||
|
-- ── Float rules ───────────────────────────────────────────────
|
||||||
|
local float_classes = {
|
||||||
|
"org.pulseaudio.pavucontrol",
|
||||||
|
"blueman-manager",
|
||||||
|
"zenity",
|
||||||
|
"pomodorolm",
|
||||||
|
"CachyOSHello",
|
||||||
|
"Rofi",
|
||||||
|
"Picture in picture",
|
||||||
|
"Save File",
|
||||||
|
"Open File",
|
||||||
|
"Steam - Self Updater",
|
||||||
|
"^Extension:.*",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cls in ipairs(float_classes) do
|
||||||
|
hl.window_rule({ match = { class = cls }, float = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
hl.window_rule({ match = { class = "LibreWolf", title = "Picture-in-Picture" }, float = true })
|
||||||
|
hl.window_rule({
|
||||||
|
match = { class = "^(xdg-desktop-portal-gtk|xdg-desktop-portal-kde|xdg-desktop-portal-hyprland).*" },
|
||||||
|
float = true,
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
match = {
|
||||||
|
class = "^(polkit-gnome-authentication-agent-1|hyprpolkitagent|org.kde.polkit-kde-authentication-agent-1).*",
|
||||||
|
},
|
||||||
|
float = true,
|
||||||
|
})
|
||||||
|
hl.window_rule({ match = { class = "Zotero", title = "Progress" }, float = true })
|
||||||
|
hl.window_rule({ match = { class = "Zotero", title = "Zotero Settings" }, float = true })
|
||||||
|
hl.window_rule({ match = { class = "Zotero", title = "Citation Dialog" }, float = true, center = true, no_anim = true })
|
||||||
|
hl.window_rule({ match = { class = "waypaper" }, float = true, center = true })
|
||||||
|
hl.window_rule({ match = { title = "calcure" }, float = true, center = true })
|
||||||
|
hl.window_rule({ match = { class = "org.gnome.Calculator" }, float = true, center = true })
|
||||||
|
hl.window_rule({
|
||||||
|
match = { class = "org.gnome.Calendar", title = "Calendar" },
|
||||||
|
float = true,
|
||||||
|
center = true,
|
||||||
|
animation = "popin",
|
||||||
|
opacity = "0.8",
|
||||||
|
})
|
||||||
|
hl.window_rule({ match = { class = "steam", title = "Freundesliste" }, float = true, size = { 400, 600 } })
|
||||||
|
|
||||||
|
-- ── PiP ──────────────────────────────────────────────────────
|
||||||
|
hl.window_rule({
|
||||||
|
match = { title = "Picture-in-Picture" },
|
||||||
|
float = true,
|
||||||
|
size = { 960, 540 },
|
||||||
|
move = { "(monitor_w*0.25)", "0" },
|
||||||
|
})
|
||||||
|
|
||||||
|
-- ── Media / misc float + position ────────────────────────────
|
||||||
|
hl.window_rule({
|
||||||
|
match = { title = "^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp|nwg-look|nwg-displays)$" },
|
||||||
|
float = true,
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
match = { title = "^(imv|mpv|danmufloat|termfloat|nemo|ncmpcpp)$" },
|
||||||
|
move = { "(monitor_w*0.25)", "0" },
|
||||||
|
size = { 960, 540 },
|
||||||
|
})
|
||||||
|
hl.window_rule({ match = { title = "danmufloat" }, pin = true })
|
||||||
|
hl.window_rule({ match = { title = "^(danmufloat|termfloat)$" }, rounding = 5 })
|
||||||
|
|
||||||
|
-- ── Opacity ───────────────────────────────────────────────────
|
||||||
|
hl.window_rule({ match = { class = "^(thunar|nemo|dolphin)$" }, opacity = "0.92" })
|
||||||
|
hl.window_rule({ match = { class = "^(discord|armcord|webcord)$" }, opacity = "0.96" })
|
||||||
|
hl.window_rule({ match = { title = "^(QQ|Telegram)$" }, opacity = "0.95" })
|
||||||
|
hl.window_rule({ match = { title = "NetEase Cloud Music Gtk4" }, opacity = "0.95" })
|
||||||
|
hl.window_rule({ match = { class = "kitty" }, opacity = "1.0" })
|
||||||
|
hl.window_rule({ match = { class = "^(libreoffice.*)$" }, opacity = "1.0 override 1.0 override 1.0" })
|
||||||
|
hl.window_rule({ match = { title = "^(.+nvim$)" }, opacity = "0.9" })
|
||||||
|
hl.window_rule({ match = { class = "^code$" }, opacity = "0.93" })
|
||||||
|
|
||||||
|
-- ── Blur / dim exceptions ─────────────────────────────────────
|
||||||
|
hl.window_rule({ match = { class = "org.mozilla.firefox" }, no_blur = true })
|
||||||
|
hl.window_rule({ match = { class = "zen" }, no_dim = true })
|
||||||
|
|
||||||
|
-- ── Animations ───────────────────────────────────────────────
|
||||||
|
hl.window_rule({ match = { class = "^(kitty|Alacritty)$" }, animation = "slide right" })
|
||||||
|
|
||||||
|
-- ── Floating decoration overrides ────────────────────────────
|
||||||
|
hl.window_rule({
|
||||||
|
match = { float = true, workspace = "w[fv1-10]" },
|
||||||
|
border_size = 2,
|
||||||
|
border_color = color4,
|
||||||
|
rounding = 8,
|
||||||
|
})
|
||||||
|
hl.window_rule({
|
||||||
|
match = { float = false, workspace = "f[1-10]" },
|
||||||
|
border_size = 3,
|
||||||
|
rounding = 4,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- ── Pomodorolm size ──────────────────────────────────────────
|
||||||
|
hl.window_rule({ match = { class = "pomodorolm" }, size = { 260, 340 } })
|
||||||
|
|
||||||
|
-- ── Layer rules ───────────────────────────────────────────────
|
||||||
|
hl.layer_rule({ match = { namespace = "logout_dialog" }, animation = "slide top" })
|
||||||
|
-- QuickShell bar (replaces waybar layer rule)
|
||||||
|
hl.layer_rule({ match = { namespace = "quickshell" }, animation = "slide down" })
|
||||||
|
hl.layer_rule({ match = { namespace = "wallpaper" }, animation = "fade 50%" })
|
||||||
|
-- vicinae
|
||||||
|
hl.layer_rule({
|
||||||
|
match = { namespace = "vicinae" },
|
||||||
|
blur = true,
|
||||||
|
animation = "popin",
|
||||||
|
dim_around = true,
|
||||||
|
ignore_alpha = 1,
|
||||||
|
})
|
||||||
|
-- screenshare fix
|
||||||
|
hl.window_rule({
|
||||||
|
match = { class = "^(xwaylandvideobridge)$" },
|
||||||
|
opacity = "0.0 override 0.0",
|
||||||
|
no_anim = true,
|
||||||
|
no_initial_focus = true,
|
||||||
|
no_focus = true,
|
||||||
|
no_blur = true,
|
||||||
|
max_size = { 1, 1 },
|
||||||
|
})
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Monitor Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
workspace = 1, monitor:eDP-2
|
|
||||||
workspace = 2, monitor:eDP-2
|
|
||||||
workspace = 3, monitor:eDP-2
|
|
||||||
workspace = 4, monitor:eDP-2
|
|
||||||
workspace = 5, monitor:eDP-2
|
|
||||||
workspace = 6, monitor:eDP-2
|
|
||||||
workspace = 7, monitor:eDP-2
|
|
||||||
|
|
||||||
workspace = 8, monitor:DP-2
|
|
||||||
workspace = 9, monitor:HDMI-A-1
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- config/windowrules.lua
|
||||||
|
--hl.workspace_rule({ workspace = "r[0-6]", monitor = "eDP-2" })
|
||||||
|
--hl.workspace_rule({ workspace = "r[7-8]", monitor = "HDMI-A-1" })
|
||||||
|
--hl.workspace_rule({ workspace = "9", layout = "scrolling", monitor = "DP-2" })
|
||||||
|
|
||||||
|
for i = 7, 9 do
|
||||||
|
hl.workspace_rule({ workspace = tostring(i), monitor = "eDP-2", default = true })
|
||||||
|
end
|
||||||
|
for i = 0, 6 do
|
||||||
|
hl.workspace_rule({ workspace = tostring(i), monitor = "HDMI-A-1", default = true })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Workspace 9: Scolling layout
|
||||||
|
hl.workspace_rule({
|
||||||
|
workspace = "9",
|
||||||
|
monitor = "DP-2",
|
||||||
|
layout = "scrolling",
|
||||||
|
default = true,
|
||||||
|
layout_opts = {
|
||||||
|
direction = "down",
|
||||||
|
column_width = 0.7,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
general {
|
|
||||||
lock_cmd = pidof swaylock || swaylock # avoid starting multiple hyprlock instances.
|
|
||||||
before_sleep_cmd = brightnessctl -r # lock before suspend.
|
|
||||||
after_sleep_cmd = hyprctl dispatch dpms on # to avoid having to press a key twice to turn on the display.
|
|
||||||
}
|
|
||||||
|
|
||||||
listener {
|
|
||||||
timeout = 7200 # 2.5min.
|
|
||||||
on-timeout = brightnessctl -s set 10 # set monitor backlight to minimum, avoid 0 on OLED monitor.
|
|
||||||
on-resume = brightnessctl -r # monitor backlight restore.
|
|
||||||
}
|
|
||||||
|
|
||||||
# turn off keyboard backlight, comment out this section if you dont have a keyboard backlight.
|
|
||||||
listener {
|
|
||||||
timeout = 7200 # 2.5min.
|
|
||||||
on-timeout = brightnessctl -sd asus::kbd_backlight set 0 # turn off keyboard backlight.
|
|
||||||
on-resume = brightnessctl -rd asus::kbd_backlight # turn on keyboard backlight.
|
|
||||||
}
|
|
||||||
|
|
||||||
listener {
|
|
||||||
timeout = 7200 # 5min
|
|
||||||
on-timeout = ~/.config/swaylock/lockscript.sh # lock screen when timeout has passed
|
|
||||||
}
|
|
||||||
|
|
||||||
# listener {
|
|
||||||
# timeout = 900 # 15min
|
|
||||||
# on-timeout = hyprctl dispatch dpms off 5.5 # screen off when timeout has passed
|
|
||||||
# on-resume = hyprctl dispatch dpms on && brightnessctl -r # screen on when activity is detected after timeout has fired.
|
|
||||||
# }
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ CachyOS Hyprland Configuration ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
|
|
||||||
$mainMod = SUPER
|
|
||||||
|
|
||||||
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
||||||
# ┃ Source Files ┃
|
|
||||||
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
||||||
source = ~/.config/hypr/config/animations.conf
|
|
||||||
source = ~/.config/hypr/config/autostart.conf
|
|
||||||
source = ~/.config/hypr/config/decorations.conf
|
|
||||||
source = ~/.config/hypr/config/environment.conf
|
|
||||||
source = ~/.config/hypr/config/input.conf
|
|
||||||
source = ~/.config/hypr/config/keybinds.conf
|
|
||||||
source = ~/.config/hypr/config/monitor.conf
|
|
||||||
source = ~/.config/hypr/config/variables.conf
|
|
||||||
source = ~/.config/hypr/config/windowrules.conf
|
|
||||||
source = ~/.config/hypr/config/workspaces.conf
|
|
||||||
source = ~/.config/hypr/config/plugins.conf
|
|
||||||
|
|
||||||
# Modifying these configs can be done by creating a user defined config in the home directory, e.g.
|
|
||||||
## ~/.config/hypr/config/user-config.conf
|
|
||||||
# source ~/.config/hypr/config/user-config.conf
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
-- ┌──────────────────────────────────────────────────────────────┐
|
||||||
|
-- │ hyprland.lua — entry point (0.55+) │
|
||||||
|
-- └──────────────────────────────────────────────────────────────┘
|
||||||
|
-- Each require() is a separate Lua scope: errors in one file won't
|
||||||
|
-- kill the others. Keep this file lean — just source the modules.
|
||||||
|
|
||||||
|
require("config.colors") -- color variables (sourced first)
|
||||||
|
require("config.variables") -- general, decoration, animations…
|
||||||
|
require("config.monitors") -- monitor layout + workspace pinning
|
||||||
|
monitors = require("config.monitors")
|
||||||
|
require("config.input") -- keyboard / mouse / device config
|
||||||
|
require("config.keybinds") -- all hl.bind() calls
|
||||||
|
require("config.windowrules") -- hl.window_rule() calls
|
||||||
|
require("config.autostart") -- exec-once equivalents
|
||||||
|
require("config.workspaces") -- workspace configuration
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Generated by nwg-displays on 2026-02-04 at 14:45:07. Do not edit manually.
|
|
||||||
|
|
||||||
monitor=desc:BOE NE156FHM-NX6,1920x1080@144.0,0x0,1.0
|
|
||||||
monitor=desc:Samsung Electric Company S24F350 H4ZK111233,1920x1080@60.0,1920x0,1.0
|
|
||||||
monitor=desc:Samsung Electric Company S24F350 H4ZK111233,transform,1
|
|
||||||
monitor=desc:Samsung Electric Company S24F350 H4ZR302705,1920x1080@60.0,3000x0,1.0
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
[pyprland]
|
|
||||||
plugins = [
|
|
||||||
"shift_monitors",
|
|
||||||
]
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# start waybar if not started
|
|
||||||
if ! pgrep -x "waybar" > /dev/null; then
|
|
||||||
waybar &
|
|
||||||
fi
|
|
||||||
|
|
||||||
# current checksums
|
|
||||||
current_checksum_config=$(md5sum ~/.config/waybar/config)
|
|
||||||
current_checksum_style=$(md5sum ~/.config/waybar/style.css)
|
|
||||||
current_checksum_colors=$(md5sum ~/.config/waybar/colors-wallust.css)
|
|
||||||
|
|
||||||
# loop forever
|
|
||||||
while true; do
|
|
||||||
# new checksums
|
|
||||||
new_checksum_config=$(md5sum ~/.config/waybar/config)
|
|
||||||
new_checksum_style=$(md5sum ~/.config/waybar/style.css)
|
|
||||||
new_checksum_colors==$(md5sum ~/.config/waybar/colors-wallust.css)
|
|
||||||
|
|
||||||
# if checksums are different
|
|
||||||
if [ "$current_checksum_config" != "$new_checksum_config" ] || [ "$current_checksum_style" != "$new_checksum_style" ] || [ "$current_checksum_colors" != "$new_checksum_colors" ]; then
|
|
||||||
# kill waybar
|
|
||||||
killall waybar
|
|
||||||
|
|
||||||
# start waybar
|
|
||||||
waybar &
|
|
||||||
|
|
||||||
# update checksums
|
|
||||||
current_checksum_config=$new_checksum_config
|
|
||||||
current_checksum_style=$new_checksum_style
|
|
||||||
current_checksum_colors=$new_checksum_colors
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
@@ -2,256 +2,65 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
PRIMARY="eDP-2"
|
PRIMARY="eDP-2"
|
||||||
RIGHT_EXTERNAL_NAME="DP-2"
|
DP_OUT="DP-2"
|
||||||
MIDDLE_EXTERNAL_NAME="HDMI-A-1"
|
HDMI_OUT="HDMI-A-1"
|
||||||
|
|
||||||
# Exact enable lines (from your config)
|
# eDP-2 at scale 1.5 → logical width 1280px → externals start at x=1280
|
||||||
EXT1='desc:Samsung Electric Company S24F350 H4ZR302705, highres@highrr, auto-right, 1'
|
# DP-2 at 1920px wide → HDMI starts at x=1280+1920=3200
|
||||||
EXT2='desc:Samsung Electric Company S24F350 H4ZK111233, highres@highrr, auto-right, 1, transform, 1'
|
|
||||||
|
|
||||||
# In dual mode, which one do we prefer?
|
|
||||||
DUAL_MAIN="$EXT1"
|
|
||||||
|
|
||||||
# Optional: restart Waybar after layout changes (0=off, 1=on)
|
|
||||||
RESTART_WAYBAR=1
|
|
||||||
|
|
||||||
# Dock/MST settle timing (tune if needed)
|
|
||||||
SETTLE_SECS=1.0
|
|
||||||
DPMS_KICK_RETRIES=2
|
|
||||||
DPMS_KICK_SLEEP=0.35
|
|
||||||
|
|
||||||
notify() {
|
notify() {
|
||||||
if command -v notify-send >/dev/null 2>&1; then
|
notify-send -a "monitor-toggle" "$1" "${2:-}" 2>/dev/null || true
|
||||||
notify-send -a "Hyprland" "$1" "${2:-}"
|
|
||||||
else
|
|
||||||
[ -n "${2:-}" ] && printf '%s: %s\n' "$1" "$2" >&2 || printf '%s\n' "$1" >&2
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hypr() {
|
active_monitors() {
|
||||||
local out
|
hyprctl -j monitors | jq -r '.[] | select(.disabled != true) | .name'
|
||||||
if ! out="$(hyprctl "$@" 2>&1)"; then
|
|
||||||
notify "hyprctl failed" "$out"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
printf '%s' "$out"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hypr_batch() {
|
has_external() {
|
||||||
local out
|
active_monitors | grep -qvx "$PRIMARY"
|
||||||
if ! out="$(hyprctl --batch "$1" 2>&1)"; then
|
|
||||||
notify "hyprctl --batch failed" "$out"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
printf '%s' "$out"
|
|
||||||
}
|
|
||||||
|
|
||||||
restart_waybar() {
|
|
||||||
[ "${RESTART_WAYBAR:-0}" -eq 1 ] || return 0
|
|
||||||
if pgrep -x waybar >/dev/null 2>&1; then
|
|
||||||
pkill -SIGUSR2 waybar >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
jqok() { command -v jq >/dev/null 2>&1; }
|
|
||||||
monjson() { hyprctl -j monitors 2>/dev/null || true; }
|
|
||||||
|
|
||||||
sleep_s() { python - <<PY 2>/dev/null || sleep 1
|
|
||||||
import time
|
|
||||||
time.sleep(float("$1"))
|
|
||||||
PY
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- monitor discovery helpers ---
|
|
||||||
|
|
||||||
enabled_monitor_names() {
|
|
||||||
if jqok; then
|
|
||||||
monjson | jq -r '.[] | select(.disabled != true) | .name'
|
|
||||||
else
|
|
||||||
# Fallback is weaker; jq is strongly recommended
|
|
||||||
hyprctl monitors | awk '
|
|
||||||
/^Monitor /{name=$2}
|
|
||||||
/disabled: false/{print name}
|
|
||||||
'
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
focused_monitor_name() {
|
|
||||||
if jqok; then
|
|
||||||
monjson | jq -r '.[] | select(.focused==true) | .name // empty'
|
|
||||||
else
|
|
||||||
hyprctl monitors | awk '
|
|
||||||
/^Monitor /{name=$2}
|
|
||||||
/focused: yes/{print name; exit}
|
|
||||||
'
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
external_enabled() {
|
|
||||||
local n
|
|
||||||
while IFS= read -r n; do
|
|
||||||
[ "$n" != "$PRIMARY" ] && return 0
|
|
||||||
done < <(enabled_monitor_names)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# We don’t try to parse desc lines; we just treat them as “enable rules”
|
|
||||||
enable_rule() {
|
|
||||||
local rule="$1"
|
|
||||||
[ -n "$rule" ] && printf 'keyword monitor %s; ' "$rule"
|
|
||||||
}
|
|
||||||
|
|
||||||
disable_by_name() {
|
|
||||||
local name="$1"
|
|
||||||
[ -n "$name" ] && printf 'keyword monitor %s, disable; ' "$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Disable all *currently enabled* externals by name (minimal churn)
|
|
||||||
batch_disable_enabled_externals() {
|
|
||||||
local batch="" n=""
|
|
||||||
while IFS= read -r n; do
|
|
||||||
[ "$n" = "$PRIMARY" ] && continue
|
|
||||||
batch+="$(disable_by_name "$n")"
|
|
||||||
done < <(enabled_monitor_names)
|
|
||||||
printf '%s' "$batch"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Dock settle wait: give MST/alt-mode time to re-enumerate before we apply rules / kick DPMS
|
|
||||||
dock_settle() {
|
|
||||||
sleep_s "$SETTLE_SECS"
|
|
||||||
}
|
|
||||||
|
|
||||||
# DPMS kick for all enabled externals (works well for dock hotplug weirdness)
|
|
||||||
kick_externals() {
|
|
||||||
local names=() n=""
|
|
||||||
if jqok; then
|
|
||||||
mapfile -t names < <(monjson | jq -r --arg P "$PRIMARY" '
|
|
||||||
.[] | select(.name != $P and (.disabled != true)) | .name
|
|
||||||
')
|
|
||||||
else
|
|
||||||
mapfile -t names < <(enabled_monitor_names | awk -v P="$PRIMARY" '$0!=P')
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ "${#names[@]}" -eq 0 ] && return 0
|
|
||||||
|
|
||||||
for _ in $(seq 1 "$DPMS_KICK_RETRIES"); do
|
|
||||||
for n in "${names[@]}"; do hypr dispatch dpms off "$n" >/dev/null 2>&1 || true; done
|
|
||||||
sleep_s "$DPMS_KICK_SLEEP"
|
|
||||||
for n in "${names[@]}"; do hypr dispatch dpms on "$n" >/dev/null 2>&1 || true; done
|
|
||||||
sleep_s "$DPMS_KICK_SLEEP"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
enforce_triple_order() {
|
|
||||||
# Keep HDMI between the laptop panel and DP-2, with DP-2 always on the right.
|
|
||||||
enabled_monitor_names | grep -qx "$MIDDLE_EXTERNAL_NAME" || return 0
|
|
||||||
enabled_monitor_names | grep -qx "$RIGHT_EXTERNAL_NAME" || return 0
|
|
||||||
|
|
||||||
local py=0 px=0 pspan=1920 mspan=1920 mx rx
|
|
||||||
if jqok; then
|
|
||||||
read -r px py pspan < <(monjson | jq -r --arg N "$PRIMARY" '
|
|
||||||
.[] | select(.name == $N) |
|
|
||||||
(.transform // 0) as $t |
|
|
||||||
"\(.x // 0) \(.y // 0) \(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)"
|
|
||||||
')
|
|
||||||
read -r mspan < <(monjson | jq -r --arg N "$MIDDLE_EXTERNAL_NAME" '
|
|
||||||
.[] | select(.name == $N) |
|
|
||||||
(.transform // 0) as $t |
|
|
||||||
"\(if (($t % 2) == 1) then (.height // 1080) else (.width // 1920) end)"
|
|
||||||
')
|
|
||||||
fi
|
|
||||||
|
|
||||||
mx=$((px + pspan))
|
|
||||||
rx=$((mx + mspan))
|
|
||||||
|
|
||||||
hypr keyword monitor "$MIDDLE_EXTERNAL_NAME, highres@highrr, ${mx}x${py}, 1, transform, 1" >/dev/null 2>&1 || true
|
|
||||||
hypr keyword monitor "$RIGHT_EXTERNAL_NAME, highres@highrr, ${rx}x${py}, 1" >/dev/null 2>&1 || true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply_profile() {
|
|
||||||
local label="$1"
|
|
||||||
local batch="$2"
|
|
||||||
dock_settle
|
|
||||||
[ -n "$batch" ] && hypr_batch "$batch" >/dev/null
|
|
||||||
dock_settle
|
|
||||||
kick_externals
|
|
||||||
notify "Profile: $label" ""
|
|
||||||
restart_waybar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profile_laptop() {
|
profile_laptop() {
|
||||||
# Minimal: disable only currently enabled externals
|
wlr-randr --output "$DP_OUT" --off 2>/dev/null || true
|
||||||
local batch=""
|
wlr-randr --output "$HDMI_OUT" --off 2>/dev/null || true
|
||||||
batch+="$(batch_disable_enabled_externals)"
|
notify "Laptop only"
|
||||||
apply_profile "Laptop-only" "$batch"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profile_dual() {
|
profile_dual() {
|
||||||
# Disable enabled externals, then enable preferred main external rule
|
wlr-randr --output "$PRIMARY" --on --mode 1920x1080@144 --pos 0,0 --scale 1
|
||||||
local batch=""
|
wlr-randr --output "$HDMI_OUT" --on --mode 1920x1080@60 --pos 1920,0 --scale 1
|
||||||
batch+="$(batch_disable_enabled_externals)"
|
wlr-randr --output "$DP_OUT" --off 2>/dev/null || true
|
||||||
batch+="$(enable_rule "$DUAL_MAIN")"
|
notify "Dual"
|
||||||
apply_profile "Dual" "$batch"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profile_triple() {
|
profile_triple() {
|
||||||
# Disable enabled externals, then enable both rules
|
wlr-randr --output "$PRIMARY" --on --mode 1920x1080@144 --pos 0,0 --scale 1
|
||||||
# Order: EXT2 first so it tends to appear “middle” with auto-right
|
wlr-randr --output "$HDMI_OUT" --on --mode 1920x1080@60 --pos 1920,0 --scale 1
|
||||||
local batch=""
|
wlr-randr --output "$DP_OUT" --on --mode 1920x1080@60 --pos 3840,0 --scale 1 --transform 90
|
||||||
batch+="$(batch_disable_enabled_externals)"
|
notify "Triple"
|
||||||
batch+="$(enable_rule "$EXT2")"
|
|
||||||
batch+="$(enable_rule "$EXT1")"
|
|
||||||
apply_profile "Triple" "$batch"
|
|
||||||
dock_settle
|
|
||||||
enforce_triple_order
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_externals() {
|
toggle() {
|
||||||
if external_enabled; then
|
if has_external; then
|
||||||
profile_laptop
|
profile_laptop
|
||||||
else
|
else
|
||||||
if [ -n "$EXT1" ] && [ -n "$EXT2" ]; then
|
profile_triple
|
||||||
profile_triple
|
|
||||||
else
|
|
||||||
profile_dual
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
dpms_toggle_focused() {
|
|
||||||
local name
|
|
||||||
name="$(focused_monitor_name)"
|
|
||||||
[ -z "$name" ] && { notify "No focused monitor" ""; exit 2; }
|
|
||||||
hypr dispatch dpms toggle "$name" >/dev/null
|
|
||||||
notify "DPMS toggle" "$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
status() {
|
status() {
|
||||||
echo "Enabled monitors:"
|
echo "Active monitors:"
|
||||||
enabled_monitor_names | sed 's/^/ - /'
|
active_monitors | sed 's/^/ - /'
|
||||||
echo
|
|
||||||
echo "Focused: $(focused_monitor_name || true)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
laptop) profile_laptop ;;
|
laptop) profile_laptop ;;
|
||||||
dual) profile_dual ;;
|
dual) profile_dual ;;
|
||||||
triple) profile_triple ;;
|
triple) profile_triple ;;
|
||||||
toggle-externals) toggle_externals ;;
|
toggle) toggle ;;
|
||||||
dpms-toggle-focused) dpms_toggle_focused ;;
|
status) status ;;
|
||||||
kick-externals) dock_settle; kick_externals; notify "Kicked externals (DPMS)" "" ;;
|
|
||||||
status) status ;;
|
|
||||||
*)
|
*)
|
||||||
cat <<EOF
|
echo "Usage: $(basename "$0") laptop|dual|triple|toggle|status"
|
||||||
Usage:
|
exit 1
|
||||||
$(basename "$0") laptop # eDP-2 only (disable enabled externals)
|
|
||||||
$(basename "$0") dual # eDP-2 + preferred external
|
|
||||||
$(basename "$0") triple # eDP-2 + both externals
|
|
||||||
$(basename "$0") toggle-externals # laptop-only <-> (dual/triple)
|
|
||||||
$(basename "$0") dpms-toggle-focused # blank/unblank focused output (layout unchanged)
|
|
||||||
$(basename "$0") kick-externals # DPMS off/on for enabled externals
|
|
||||||
$(basename "$0") status # print enabled + focused
|
|
||||||
EOF
|
|
||||||
exit 2
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# CLI options
|
# CLI options
|
||||||
DMENU_CMD=""
|
DMENU_CMD=""
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ else
|
|||||||
elif [[ $line =~ ^[[:space:]]*description:[[:space:]]*(.*)$ ]]; then
|
elif [[ $line =~ ^[[:space:]]*description:[[:space:]]*(.*)$ ]]; then
|
||||||
DESC="${BASH_REMATCH[1]}"
|
DESC="${BASH_REMATCH[1]}"
|
||||||
elif [[ $line =~ ^[[:space:]]*transform:[[:space:]]*([0-9]+)$ ]]; then
|
elif [[ $line =~ ^[[:space:]]*transform:[[:space:]]*([0-9]+)$ ]]; then
|
||||||
TFORM="${BASHREMATCH[1]}"
|
TFORM="${BASH_REMATCH[1]}"
|
||||||
elif [[ $line =~ ^[[:space:]]*focused:[[:space:]]*yes$ ]]; then
|
elif [[ $line =~ ^[[:space:]]*focused:[[:space:]]*yes$ ]]; then
|
||||||
FOC=1
|
FOC=1
|
||||||
elif [[ -z $line ]]; then
|
elif [[ -z $line ]]; then
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
include colors.conf
|
include colors.conf
|
||||||
background_opacity .3
|
background_opacity .5
|
||||||
background_blur 16
|
background_blur 16
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
// Bar.qml - top panel
|
|
||||||
// Neighbouring types (Pill, ClockWidget, Theme, etc.) are auto-imported by QuickShell.
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import "./modules"
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
WlrLayershell.namespace: "quickshell-bar"
|
|
||||||
WlrLayershell.layer: WlrLayer.Top
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
|
|
||||||
margins {
|
|
||||||
left: 2
|
|
||||||
right: 2
|
|
||||||
bottom: 1
|
|
||||||
top: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitHeight: Theme.barHeight
|
|
||||||
exclusiveZone: Theme.barHeight
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: 4
|
|
||||||
anchors.rightMargin: 4
|
|
||||||
spacing: Theme.spacing
|
|
||||||
|
|
||||||
// ─── LEFT ──────────────────────────────────────────
|
|
||||||
ClockWidget {}
|
|
||||||
WeatherWidget {}
|
|
||||||
SysTrayWidget {}
|
|
||||||
WorkspacesWidget { screen: root.screen }
|
|
||||||
MediaWidget {}
|
|
||||||
WindowTitleWidget { screen: root.screen }
|
|
||||||
|
|
||||||
Item { Layout.fillWidth: true }
|
|
||||||
|
|
||||||
// ─── RIGHT ─────────────────────────────────────────
|
|
||||||
CavaWidget {}
|
|
||||||
AudioWidget {}
|
|
||||||
MemoryWidget {}
|
|
||||||
CpuWidget {}
|
|
||||||
TemperatureWidget {}
|
|
||||||
BatteryWidget {}
|
|
||||||
BluetoothWidget {}
|
|
||||||
PowerProfilesWidget {}
|
|
||||||
PowerMenuWidget {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Theme.qml - global palette & dimensions
|
|
||||||
// QuickShell auto-discovers this; access from any file as `Theme.colorN` etc.
|
|
||||||
pragma Singleton
|
|
||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
// ── Wallust palette ──────────────────────────────────────
|
|
||||||
readonly property color background: "#252425"
|
|
||||||
readonly property color foreground: "#F9F1D9"
|
|
||||||
readonly property color color0: "#505051"
|
|
||||||
readonly property color color1: "#9C604E"
|
|
||||||
readonly property color color2: "#807A52"
|
|
||||||
readonly property color color3: "#908BAB"
|
|
||||||
readonly property color color4: "#B7815F"
|
|
||||||
readonly property color color5: "#B9BECA"
|
|
||||||
readonly property color color6: "#EED793"
|
|
||||||
readonly property color color7: "#EEE3C1"
|
|
||||||
readonly property color color8: "#A79F87"
|
|
||||||
|
|
||||||
// ── Derived / semantic ────────────────────────────────────
|
|
||||||
readonly property color pill: Qt.rgba(0.976, 0.945, 0.851, 0.15)
|
|
||||||
readonly property color pillHover: color2
|
|
||||||
readonly property color wsActive: color3
|
|
||||||
readonly property color wsUrgent: color1
|
|
||||||
|
|
||||||
// ── Typography ────────────────────────────────────────────
|
|
||||||
readonly property string fontSans: "Fira Sans Condensed"
|
|
||||||
readonly property string fontMono: "FiraCode Nerd Font"
|
|
||||||
readonly property int fontSize: 12
|
|
||||||
|
|
||||||
// ── Bar geometry ─────────────────────────────────────────
|
|
||||||
readonly property int barHeight: 28
|
|
||||||
readonly property int barPadding: 2
|
|
||||||
readonly property int radius: 5
|
|
||||||
readonly property int pillPadH: 10
|
|
||||||
readonly property int spacing: 4
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// modules/AudioWidget.qml - wireplumber volume, matches waybar style.
|
|
||||||
// Icon set mirrors waybar wireplumber format-icons (NerdFont).
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
import ".."
|
|
||||||
|
|
||||||
Pill {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var node: Pipewire.defaultAudioSink
|
|
||||||
property bool muted: node?.audio.muted ?? false
|
|
||||||
property real vol: node?.audio.volume ?? 0
|
|
||||||
|
|
||||||
property string icon: {
|
|
||||||
if (muted || vol === 0) return " "
|
|
||||||
if (vol < 0.34) return " "
|
|
||||||
if (vol < 0.67) return " "
|
|
||||||
return " "
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: (m) => {
|
|
||||||
if (m.button === Qt.LeftButton) Exec.run(["pavucontrol"])
|
|
||||||
if (m.button === Qt.RightButton && node)
|
|
||||||
node.audio.muted = !node.audio.muted
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrolled: (w) => {
|
|
||||||
if (!node) return
|
|
||||||
var delta = w.angleDelta.y > 0 ? 0.04 : -0.04
|
|
||||||
node.audio.volume = Math.max(0, Math.min(1.5, node.audio.volume + delta))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cava feeds into this on the left → right border only
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: Theme.pillPadH
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.icon
|
|
||||||
font { family: Theme.fontMono; pixelSize: Theme.fontSize; }
|
|
||||||
color: "#fab387" // peach accent matching waybar foreground color for icon
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
text: root.muted ? "muted" : Math.round(root.vol * 100) + "%"
|
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
|
||||||
color: Theme.foreground
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
// modules/PowerMenuWidget.qml - ⏻ button with inline popup menu.
|
|
||||||
// Matches waybar custom/power with menu-actions.
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Io
|
|
||||||
import ".."
|
|
||||||
|
|
||||||
Pill {
|
|
||||||
id: root
|
|
||||||
onClicked: (m) => {
|
|
||||||
if (m.button === Qt.LeftButton) menu.visible = !menu.visible
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: "⏻ "
|
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
|
||||||
color: Theme.foreground
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline drop-up popup - appears above the bar
|
|
||||||
Rectangle {
|
|
||||||
id: menu
|
|
||||||
visible: false
|
|
||||||
z: 100
|
|
||||||
|
|
||||||
width: 130
|
|
||||||
height: menuCol.implicitHeight + 16
|
|
||||||
radius: 5
|
|
||||||
color: Qt.rgba(0.086, 0.075, 0.125, 0.85)
|
|
||||||
border.color: Qt.rgba(1, 1, 1, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
// Anchor above the pill
|
|
||||||
parent: root.parent // reparent to bar so z-ordering works
|
|
||||||
x: root.x + root.width - width
|
|
||||||
y: root.y - height - 4
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: menuCol
|
|
||||||
anchors { fill: parent; margins: 8 }
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: [
|
|
||||||
{ label: "Suspend", cmd: ["systemctl", "suspend"] },
|
|
||||||
{ label: "Hibernate", cmd: ["systemctl", "hibernate"] },
|
|
||||||
{ label: "Logout", cmd: ["hyprctl", "dispatch", "exit"] },
|
|
||||||
{ label: "Reboot", cmd: ["reboot"] },
|
|
||||||
{ label: "Shutdown", cmd: ["shutdown", "now"] },
|
|
||||||
]
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
required property var modelData
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 26
|
|
||||||
radius: 5
|
|
||||||
color: itemHover.containsMouse
|
|
||||||
? Theme.pillHover
|
|
||||||
: "transparent"
|
|
||||||
Behavior on color { ColorAnimation { duration: 100 } }
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors { left: parent.left; verticalCenter: parent.verticalCenter; leftMargin: 8 }
|
|
||||||
text: modelData.label
|
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
|
||||||
color: Theme.foreground
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: itemHover
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: {
|
|
||||||
menu.visible = false
|
|
||||||
Exec.run(modelData.cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// modules/WindowTitleWidget.qml - hyprland/window equivalent
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import ".."
|
|
||||||
|
|
||||||
Pill {
|
|
||||||
id: root
|
|
||||||
required property var screen
|
|
||||||
|
|
||||||
property string title: {
|
|
||||||
var ws = Hyprland.focusedWorkspace
|
|
||||||
if (!ws) return ""
|
|
||||||
var win = ws.lastWindow
|
|
||||||
if (!win) return ""
|
|
||||||
var t = win.title ?? ""
|
|
||||||
return t.length > 60 ? t.substring(0, 60) + "..." : t
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: title !== ""
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: root.title
|
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
|
||||||
color: Theme.foreground
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
// modules/WorkspacesWidget.qml
|
|
||||||
// Kanji workspace labels, per-monitor, matches waybar hyprland/workspaces.
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import ".."
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: root
|
|
||||||
required property var screen
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: wsRow.implicitWidth
|
|
||||||
implicitHeight: Theme.barHeight
|
|
||||||
|
|
||||||
// Filter workspaces that belong to this screen's monitor
|
|
||||||
property string monitorName: {
|
|
||||||
for (var i = 0; i < Hyprland.monitors.values.length; i++) {
|
|
||||||
var m = Hyprland.monitors.values[i]
|
|
||||||
if (m.name === screen.name) return m.name
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: wsRow
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Theme.spacing
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: {
|
|
||||||
// sort visible workspaces for this monitor
|
|
||||||
var all = Hyprland.workspaces.values
|
|
||||||
return all.filter(ws => ws.monitor && ws.monitor.name === root.monitorName)
|
|
||||||
.sort((a, b) => a.id - b.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
required property var modelData
|
|
||||||
property bool isActive: modelData.id === (Hyprland.focusedWorkspace?.id ?? -1)
|
|
||||||
|
|
||||||
width: 32
|
|
||||||
height: Theme.barHeight - 4
|
|
||||||
radius: Theme.radius
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
color: isActive
|
|
||||||
? Theme.wsActive
|
|
||||||
: (wsBtn.containsMouse ? Theme.pillHover : Theme.pill)
|
|
||||||
|
|
||||||
Behavior on color { ColorAnimation { duration: 200 } }
|
|
||||||
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: Math.min(modelData.id - 1, 10 - 1)
|
|
||||||
font { family: Theme.fontSans; pixelSize: 11 }
|
|
||||||
color: Theme.foreground
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: wsBtn
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: Hyprland.dispatch("workspace " + modelData.id)
|
|
||||||
onWheel: (w) => Hyprland.dispatch(
|
|
||||||
"workspace " + (w.angleDelta.y > 0 ? "e+1" : "e-1"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// Bar.qml - top panel
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import "./modules"
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "quickshell-bar"
|
||||||
|
WlrLayershell.layer: WlrLayer.Top
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
margins {
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function setColorAlpha(color, alpha) {
|
||||||
|
return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitHeight: Theme.barHeight + Theme.barPadding * 2
|
||||||
|
exclusiveZone: Theme.barHeight + Theme.barPadding * 2
|
||||||
|
color: setColorAlpha(Theme.background, 0)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.barPadding
|
||||||
|
anchors.topMargin: Theme.barPadding
|
||||||
|
anchors.bottomMargin: Theme.barPadding
|
||||||
|
anchors.rightMargin: Theme.barPadding
|
||||||
|
spacing: Theme.spacing
|
||||||
|
|
||||||
|
// ─── LEFT ──────────────────────────────────────────
|
||||||
|
SysTrayWidget { parentWindow: root }
|
||||||
|
ClockWidget {}
|
||||||
|
WeatherWidget {}
|
||||||
|
WorkspacesWidget { screen: root.screen }
|
||||||
|
WindowTitleWidget { screen: root.screen }
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
|
// ─── RIGHT ─────────────────────────────────────────
|
||||||
|
MediaCavaWidget {}
|
||||||
|
MemoryWidget {}
|
||||||
|
CpuWidget {}
|
||||||
|
TemperatureWidget {}
|
||||||
|
BatteryWidget {}
|
||||||
|
//BluetoothWidget {}
|
||||||
|
PowerProfilesWidget {}
|
||||||
|
PowerMenuWidget {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// colors.qml - Generated by wallust
|
||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
readonly property color background: "#0E0E14"
|
||||||
|
readonly property color foreground: "#B6B6BC"
|
||||||
|
readonly property color cursor: "#769BBF"
|
||||||
|
|
||||||
|
readonly property color color0: "#3A453E"
|
||||||
|
readonly property color color1: "#6A6C52"
|
||||||
|
readonly property color color2: "#42617B"
|
||||||
|
readonly property color color3: "#744246"
|
||||||
|
readonly property color color4: "#638D4F"
|
||||||
|
readonly property color color5: "#C26E26"
|
||||||
|
readonly property color color6: "#4380BA"
|
||||||
|
readonly property color color7: "#8E8E98"
|
||||||
|
readonly property color color8: "#64636A"
|
||||||
|
readonly property color color9: "#6A6C52"
|
||||||
|
readonly property color color10: "#42617B"
|
||||||
|
readonly property color color11: "#744246"
|
||||||
|
readonly property color color12: "#638D4F"
|
||||||
|
readonly property color color13: "#C26E26"
|
||||||
|
readonly property color color14: "#4380BA"
|
||||||
|
readonly property color color15: "#8E8E98"
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Exec.qml - fire-and-forget process launcher
|
// Exec.qml - process launcher
|
||||||
// Usage from any file: Exec.run(["kitty", "-e", "btop"])
|
// Usage from any file: Exec.run(["kitty", "-e", "btop"])
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// GlobalStates.qml - a place to store global state variables accessible from any QML file.
|
||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Used by the merged overview module (own/modules/overview) — see own/modules/overview/CREDITS.md
|
||||||
|
property bool overviewOpen: false
|
||||||
|
|
||||||
|
readonly property QtObject popups: QtObject {
|
||||||
|
property string active: ""
|
||||||
|
property real anchorX: 0
|
||||||
|
|
||||||
|
function open(name, ax) { popups.anchorX = ax ?? 0; popups.active = name }
|
||||||
|
function close() { popups.active = "" }
|
||||||
|
function toggle(name, ax) {
|
||||||
|
if (popups.active === name) { popups.active = "" }
|
||||||
|
else { popups.anchorX = ax ?? 0; popups.active = name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
// Popup.qml - a reusable popup component for Quickshell.
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var screen
|
||||||
|
required property string popupName
|
||||||
|
|
||||||
|
property int rightMargin: 10
|
||||||
|
property int topMargin: 1
|
||||||
|
property int autoHideDelay: Theme.popupAutoHideDelay
|
||||||
|
|
||||||
|
default property alias content: contentItem.data
|
||||||
|
|
||||||
|
// ── Visibility ────────────────────────────────────────────
|
||||||
|
property bool logicalOpen: GlobalStates.popups.active === popupName
|
||||||
|
property bool _keepVisible: false
|
||||||
|
|
||||||
|
visible: logicalOpen || _keepVisible
|
||||||
|
|
||||||
|
onLogicalOpenChanged: {
|
||||||
|
if (logicalOpen) {
|
||||||
|
closeTimer.stop()
|
||||||
|
_keepVisible = false
|
||||||
|
openY.start()
|
||||||
|
openOpacity.start()
|
||||||
|
} else {
|
||||||
|
hideTimer.stop()
|
||||||
|
_keepVisible = true
|
||||||
|
closeY.start()
|
||||||
|
closeOpacity.start()
|
||||||
|
closeTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps the Overlay window alive for the duration of the close animation
|
||||||
|
Timer {
|
||||||
|
id: closeTimer
|
||||||
|
interval: 280
|
||||||
|
onTriggered: root._keepVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.namespace: "quickshell-popup-" + popupName
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
|
||||||
|
anchors { top: true; left: true; right: true; bottom: true }
|
||||||
|
color: Qt.rgba(0, 0, 0, 0)
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: hideTimer
|
||||||
|
interval: root.autoHideDelay
|
||||||
|
repeat: false
|
||||||
|
onTriggered: GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Slide animations ──────────────────────────────────────
|
||||||
|
NumberAnimation { id: openY; target: box; property: "y"; to: root.topMargin; duration: 250; easing.type: Easing.OutCubic }
|
||||||
|
NumberAnimation { id: closeY; target: box; property: "y"; to: root.topMargin - 200; duration: 200; easing.type: Easing.InCubic }
|
||||||
|
NumberAnimation { id: openOpacity; target: box; property: "opacity"; to: Theme.popupOpacity; duration: 200 }
|
||||||
|
NumberAnimation { id: closeOpacity;target: box; property: "opacity"; to: 0.0; duration: 200 }
|
||||||
|
|
||||||
|
// ── Popup box ─────────────────────────────────────────────
|
||||||
|
Rectangle {
|
||||||
|
id: box
|
||||||
|
|
||||||
|
x: {
|
||||||
|
var ax = GlobalStates.popups.anchorX
|
||||||
|
var centered = ax > 0 ? ax - width / 2 : parent.width - width - root.rightMargin
|
||||||
|
return Math.max(4, Math.min(parent.width - width - 4, centered))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state: closed (above the bar, transparent)
|
||||||
|
y: root.topMargin - 40
|
||||||
|
opacity: 0.0
|
||||||
|
|
||||||
|
width: contentItem.implicitWidth + 10
|
||||||
|
height: contentItem.implicitHeight + 10
|
||||||
|
|
||||||
|
color: Theme.popupBackground
|
||||||
|
radius: Theme.radius * 2
|
||||||
|
border.color: Theme.popupBorderColor
|
||||||
|
border.width: Theme.popupBorderWidth
|
||||||
|
|
||||||
|
// Absorb clicks so the outer close-on-click doesn't fire inside the box
|
||||||
|
MouseArea { anchors.fill: parent }
|
||||||
|
|
||||||
|
// HoverHandler doesn't lose hover when a child MouseArea grabs a press,
|
||||||
|
// so clicking buttons won't accidentally start the hide timer.
|
||||||
|
HoverHandler {
|
||||||
|
onHoveredChanged: hovered ? hideTimer.stop() : hideTimer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: contentItem
|
||||||
|
x: 5; y: 5
|
||||||
|
implicitWidth: childrenRect.width
|
||||||
|
implicitHeight: childrenRect.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Wallpaper switcher: echo 1 > /tmp/qs-wallpaper-ipc
|
||||||
|
In Hyprland: ctrl t
|
||||||
|
|
||||||
|
Workspace overview: qs ipc -c own call overview toggle
|
||||||
|
Merged from Shanu-Kumawat/quickshell-overview (originally extracted from
|
||||||
|
end-4/dots-hyprland). See modules/overview/CREDITS.md for details and
|
||||||
|
the untouched upstream clone at ../overview.
|
||||||
|
|
||||||
|
Wallpaper switcher trigger from Hyprland keybind: echo 1 > /tmp/qs-wallpaper-ipc
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Theme.qml - global palette & dimensions
|
||||||
|
// QuickShell auto-discovers this; access from any file as `Theme.colorN` etc.
|
||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
|
||||||
|
function setColorAlpha(color, alpha) {
|
||||||
|
return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Wallust palette ──────────────────────────────────────
|
||||||
|
readonly property color background: Colors.foreground
|
||||||
|
readonly property color foreground: Colors.background
|
||||||
|
readonly property color color0: Colors.color0
|
||||||
|
readonly property color color1: Colors.color1
|
||||||
|
readonly property color color2: Colors.color2
|
||||||
|
readonly property color color3: Colors.color3
|
||||||
|
readonly property color color4: Colors.color4
|
||||||
|
readonly property color color5: Colors.color5
|
||||||
|
readonly property color color6: Colors.color6
|
||||||
|
readonly property color color7: Colors.color7
|
||||||
|
|
||||||
|
readonly property color color8: Colors.color8
|
||||||
|
// ── Derived / semantic ────────────────────────────────────
|
||||||
|
readonly property color pill: setColorAlpha(Colors.background, 0.8)
|
||||||
|
readonly property color pillText: setColorAlpha(Colors.foreground, 1)
|
||||||
|
readonly property color pillHover: color2
|
||||||
|
readonly property color wsActive: color3
|
||||||
|
readonly property color wsUrgent: color1
|
||||||
|
|
||||||
|
// ── Typography ────────────────────────────────────────────
|
||||||
|
readonly property string fontSans: "Fira Sans Condensed"
|
||||||
|
readonly property string fontMono: "FiraCode Nerd Font"
|
||||||
|
readonly property int fontSize: 11
|
||||||
|
readonly property int fontSizeSecondary: 10
|
||||||
|
|
||||||
|
// ── Bar geometry ─────────────────────────────────────────
|
||||||
|
readonly property int barHeight: 24
|
||||||
|
readonly property int barPadding: 2
|
||||||
|
readonly property int radius: 5
|
||||||
|
readonly property int pillPadH: 10
|
||||||
|
readonly property int spacing: 4
|
||||||
|
|
||||||
|
// ── Popup ───────────────────────────────────────────────
|
||||||
|
readonly property color popupBackground: setColorAlpha(Colors.background, 0.75)
|
||||||
|
readonly property int popupBorderWidth: 0
|
||||||
|
readonly property color popupBorderColor: setColorAlpha(Colors.foreground, 0.1)
|
||||||
|
readonly property int popupPadding: 10
|
||||||
|
readonly property double popupOpacity: 0.7
|
||||||
|
|
||||||
|
// ── Animations ───────────────────────────────────────────
|
||||||
|
readonly property int cavaDissapearTime: 500
|
||||||
|
readonly property int workspaceSlideTime: 250
|
||||||
|
readonly property int popupAutoHideDelay: 500
|
||||||
|
|
||||||
|
// ── Other constants ───────────────────────────────────────
|
||||||
|
readonly property int titleLength: 75
|
||||||
|
}
|
||||||
@@ -0,0 +1,360 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import "functions"
|
||||||
|
import "." as Common
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
property string colorSource: Common.Config.options.appearance.colorSource
|
||||||
|
property string caelestiaAccentProfile: Common.Config.options.appearance.caelestia.accentProfile
|
||||||
|
property string lastCaelestiaPayload: ""
|
||||||
|
property QtObject m3colors: {
|
||||||
|
if (colorSource === "matugen" && matugenLoader.item)
|
||||||
|
return matugenLoader.item;
|
||||||
|
if (colorSource === "caelestia" && caelestiaPaletteLoaded)
|
||||||
|
return caelestiaColors;
|
||||||
|
return defaultColors;
|
||||||
|
}
|
||||||
|
property QtObject animation
|
||||||
|
property QtObject animationCurves
|
||||||
|
property QtObject colors
|
||||||
|
property QtObject rounding
|
||||||
|
property QtObject font
|
||||||
|
property QtObject sizes
|
||||||
|
property bool caelestiaPaletteLoaded: false
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: matugenLoader
|
||||||
|
active: root.colorSource === "matugen"
|
||||||
|
source: "Appearance.colors.qml"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retuned to pull from this shell's own wallust palette (Theme/Colors) instead of the
|
||||||
|
// upstream stock M3 purple palette, so the overview matches the bar's look. See
|
||||||
|
// ../modules/overview/CREDITS.md.
|
||||||
|
property QtObject defaultColors: QtObject {
|
||||||
|
property bool darkmode: true
|
||||||
|
property color m3primary: Theme.wsActive
|
||||||
|
property color m3onPrimary: Colors.background
|
||||||
|
property color m3primaryContainer: ColorUtils.mix(Theme.wsActive, Colors.background, 0.35)
|
||||||
|
property color m3onPrimaryContainer: Colors.foreground
|
||||||
|
property color m3secondary: Theme.wsActive
|
||||||
|
property color m3onSecondary: Colors.background
|
||||||
|
property color m3secondaryContainer: ColorUtils.mix(Theme.wsActive, Colors.background, 0.55)
|
||||||
|
property color m3onSecondaryContainer: Colors.foreground
|
||||||
|
property color m3background: Colors.background
|
||||||
|
property color m3onBackground: Colors.foreground
|
||||||
|
property color m3surface: Colors.background
|
||||||
|
property color m3surfaceContainerLow: ColorUtils.mix(Colors.foreground, Colors.background, 0.06)
|
||||||
|
property color m3surfaceContainer: ColorUtils.mix(Colors.foreground, Colors.background, 0.10)
|
||||||
|
property color m3surfaceContainerHigh: ColorUtils.mix(Colors.foreground, Colors.background, 0.15)
|
||||||
|
property color m3surfaceContainerHighest: ColorUtils.mix(Colors.foreground, Colors.background, 0.20)
|
||||||
|
property color m3onSurface: Colors.foreground
|
||||||
|
property color m3surfaceVariant: ColorUtils.mix(Colors.foreground, Colors.background, 0.25)
|
||||||
|
property color m3onSurfaceVariant: Colors.foreground
|
||||||
|
property color m3inverseSurface: Colors.foreground
|
||||||
|
property color m3inverseOnSurface: Colors.background
|
||||||
|
property color m3outline: Colors.color8
|
||||||
|
property color m3outlineVariant: ColorUtils.mix(Colors.foreground, Colors.background, 0.30)
|
||||||
|
property color m3shadow: "#000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject caelestiaColors: QtObject {
|
||||||
|
property bool darkmode: defaultColors.darkmode
|
||||||
|
property color m3primary: defaultColors.m3primary
|
||||||
|
property color m3onPrimary: defaultColors.m3onPrimary
|
||||||
|
property color m3primaryContainer: defaultColors.m3primaryContainer
|
||||||
|
property color m3onPrimaryContainer: defaultColors.m3onPrimaryContainer
|
||||||
|
property color m3secondary: defaultColors.m3secondary
|
||||||
|
property color m3onSecondary: defaultColors.m3onSecondary
|
||||||
|
property color m3secondaryContainer: defaultColors.m3secondaryContainer
|
||||||
|
property color m3onSecondaryContainer: defaultColors.m3onSecondaryContainer
|
||||||
|
property color m3background: defaultColors.m3background
|
||||||
|
property color m3onBackground: defaultColors.m3onBackground
|
||||||
|
property color m3surface: defaultColors.m3surface
|
||||||
|
property color m3surfaceContainerLow: defaultColors.m3surfaceContainerLow
|
||||||
|
property color m3surfaceContainer: defaultColors.m3surfaceContainer
|
||||||
|
property color m3surfaceContainerHigh: defaultColors.m3surfaceContainerHigh
|
||||||
|
property color m3surfaceContainerHighest: defaultColors.m3surfaceContainerHighest
|
||||||
|
property color m3onSurface: defaultColors.m3onSurface
|
||||||
|
property color m3surfaceVariant: defaultColors.m3surfaceVariant
|
||||||
|
property color m3onSurfaceVariant: defaultColors.m3onSurfaceVariant
|
||||||
|
property color m3inverseSurface: defaultColors.m3inverseSurface
|
||||||
|
property color m3inverseOnSurface: defaultColors.m3inverseOnSurface
|
||||||
|
property color m3outline: defaultColors.m3outline
|
||||||
|
property color m3outlineVariant: defaultColors.m3outlineVariant
|
||||||
|
property color m3shadow: defaultColors.m3shadow
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCaelestiaPalette() {
|
||||||
|
getCaelestiaScheme.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function relativeLuminance(color) {
|
||||||
|
const c = Qt.color(color);
|
||||||
|
function channel(v) {
|
||||||
|
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||||
|
}
|
||||||
|
return (0.2126 * channel(c.r)) + (0.7152 * channel(c.g)) + (0.0722 * channel(c.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
function bestOnColor(backgroundColor) {
|
||||||
|
return relativeLuminance(backgroundColor) > 0.5 ? "#121212" : "#f5f5f5";
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstColor(palette, keys, fallback) {
|
||||||
|
for (const key of keys) {
|
||||||
|
if (palette[key])
|
||||||
|
return palette[key];
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyCaelestiaPalette(palette, mode) {
|
||||||
|
if (caelestiaAccentProfile === "vibrant") {
|
||||||
|
const primary = firstColor(palette, ["blue", "klink", "term12", "primary"], defaultColors.m3primary);
|
||||||
|
const secondary = firstColor(palette, ["mauve", "lavender", "term13", "secondary"], defaultColors.m3secondary);
|
||||||
|
const tertiary = firstColor(palette, ["pink", "rosewater", "term11", "tertiary"], defaultColors.m3secondaryContainer);
|
||||||
|
const primaryContainer = firstColor(palette, ["sapphire", "klinkSelection", "primaryContainer"], defaultColors.m3primaryContainer);
|
||||||
|
const secondaryContainer = firstColor(palette, ["surface2", "secondaryContainer"], defaultColors.m3secondaryContainer);
|
||||||
|
|
||||||
|
caelestiaColors.m3primary = primary;
|
||||||
|
caelestiaColors.m3onPrimary = bestOnColor(primary);
|
||||||
|
caelestiaColors.m3primaryContainer = primaryContainer;
|
||||||
|
caelestiaColors.m3onPrimaryContainer = bestOnColor(primaryContainer);
|
||||||
|
caelestiaColors.m3secondary = secondary;
|
||||||
|
caelestiaColors.m3onSecondary = bestOnColor(secondary);
|
||||||
|
caelestiaColors.m3secondaryContainer = secondaryContainer;
|
||||||
|
caelestiaColors.m3onSecondaryContainer = bestOnColor(secondaryContainer);
|
||||||
|
// Preserve a stronger accent presence across UI mixes.
|
||||||
|
caelestiaColors.m3surfaceVariant = firstColor(palette, ["surface1", "surfaceVariant"], defaultColors.m3surfaceVariant);
|
||||||
|
caelestiaColors.m3outline = firstColor(palette, ["overlay2", "outline"], defaultColors.m3outline);
|
||||||
|
caelestiaColors.m3outlineVariant = firstColor(palette, ["overlay0", "outlineVariant"], defaultColors.m3outlineVariant);
|
||||||
|
if (tertiary)
|
||||||
|
caelestiaColors.m3secondaryContainer = ColorUtils.mix(secondaryContainer, tertiary, 0.7);
|
||||||
|
} else {
|
||||||
|
const map = {
|
||||||
|
"primary": "m3primary",
|
||||||
|
"onPrimary": "m3onPrimary",
|
||||||
|
"primaryContainer": "m3primaryContainer",
|
||||||
|
"onPrimaryContainer": "m3onPrimaryContainer",
|
||||||
|
"secondary": "m3secondary",
|
||||||
|
"onSecondary": "m3onSecondary",
|
||||||
|
"secondaryContainer": "m3secondaryContainer",
|
||||||
|
"onSecondaryContainer": "m3onSecondaryContainer",
|
||||||
|
"surfaceVariant": "m3surfaceVariant",
|
||||||
|
"outline": "m3outline",
|
||||||
|
"outlineVariant": "m3outlineVariant"
|
||||||
|
};
|
||||||
|
for (const key in map) {
|
||||||
|
if (palette[key])
|
||||||
|
caelestiaColors[map[key]] = palette[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep foundational tones from Material keys for readability.
|
||||||
|
const baseMap = {
|
||||||
|
"background": "m3background",
|
||||||
|
"onBackground": "m3onBackground",
|
||||||
|
"surface": "m3surface",
|
||||||
|
"surfaceContainerLow": "m3surfaceContainerLow",
|
||||||
|
"surfaceContainer": "m3surfaceContainer",
|
||||||
|
"surfaceContainerHigh": "m3surfaceContainerHigh",
|
||||||
|
"surfaceContainerHighest": "m3surfaceContainerHighest",
|
||||||
|
"onSurface": "m3onSurface",
|
||||||
|
"inverseSurface": "m3inverseSurface",
|
||||||
|
"inverseOnSurface": "m3inverseOnSurface",
|
||||||
|
"shadow": "m3shadow"
|
||||||
|
};
|
||||||
|
for (const key in baseMap) {
|
||||||
|
if (palette[key])
|
||||||
|
caelestiaColors[baseMap[key]] = palette[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (palette["onSurfaceVariant"])
|
||||||
|
caelestiaColors.m3onSurfaceVariant = palette["onSurfaceVariant"];
|
||||||
|
|
||||||
|
if (mode === "light")
|
||||||
|
caelestiaColors.darkmode = false;
|
||||||
|
else if (mode === "dark")
|
||||||
|
caelestiaColors.darkmode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: getCaelestiaScheme
|
||||||
|
command: ["sh", "-lc", "caelestia scheme get 2>/dev/null || true"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
id: caelestiaCollector
|
||||||
|
onStreamFinished: {
|
||||||
|
const text = caelestiaCollector.text;
|
||||||
|
if (!text || !text.trim()) {
|
||||||
|
root.caelestiaPaletteLoaded = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ansiPattern = /\x1b\[[0-9;]*m/g;
|
||||||
|
const lines = text.split("\n");
|
||||||
|
let mode = "";
|
||||||
|
const palette = ({});
|
||||||
|
|
||||||
|
for (const rawLine of lines) {
|
||||||
|
const line = rawLine.replace(ansiPattern, "").trim();
|
||||||
|
|
||||||
|
if (line.startsWith("Mode:")) {
|
||||||
|
mode = line.split(":")[1]?.trim()?.toLowerCase() ?? "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = line.match(/^([A-Za-z0-9_]+):\s*.*?([0-9a-fA-F]{6})$/);
|
||||||
|
if (!match)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
palette[match[1]] = `#${match[2]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = JSON.stringify({ mode: mode, palette: palette, profile: caelestiaAccentProfile });
|
||||||
|
if (normalized === root.lastCaelestiaPayload)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.lastCaelestiaPayload = normalized;
|
||||||
|
applyCaelestiaPalette(palette, mode);
|
||||||
|
root.caelestiaPaletteLoaded = Object.keys(palette).length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: caelestiaRefreshTimer
|
||||||
|
interval: Math.max(500, Common.Config.options.appearance.caelestia.refreshInterval)
|
||||||
|
running: root.colorSource === "caelestia" && Common.Config.options.appearance.caelestia.autoRefresh
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: false
|
||||||
|
onTriggered: root.loadCaelestiaPalette()
|
||||||
|
}
|
||||||
|
|
||||||
|
onColorSourceChanged: {
|
||||||
|
if (colorSource === "caelestia")
|
||||||
|
loadCaelestiaPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCaelestiaAccentProfileChanged: {
|
||||||
|
if (colorSource === "caelestia") {
|
||||||
|
root.lastCaelestiaPayload = "";
|
||||||
|
loadCaelestiaPalette();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (colorSource === "caelestia")
|
||||||
|
loadCaelestiaPalette();
|
||||||
|
}
|
||||||
|
|
||||||
|
colors: QtObject {
|
||||||
|
property color colSubtext: m3colors.m3outline
|
||||||
|
property color colLayer0: m3colors.m3background
|
||||||
|
property color colOnLayer0: m3colors.m3onBackground
|
||||||
|
property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4)
|
||||||
|
property color colLayer1: m3colors.m3surfaceContainerLow
|
||||||
|
property color colOnLayer1: m3colors.m3onSurfaceVariant
|
||||||
|
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45)
|
||||||
|
property color colLayer1Hover: ColorUtils.mix(colLayer1, colOnLayer1, 0.92)
|
||||||
|
property color colLayer1Active: ColorUtils.mix(colLayer1, colOnLayer1, 0.85)
|
||||||
|
property color colLayer2: m3colors.m3surfaceContainer
|
||||||
|
property color colOnLayer2: m3colors.m3onSurface
|
||||||
|
property color colLayer2Hover: ColorUtils.mix(colLayer2, colOnLayer2, 0.90)
|
||||||
|
property color colLayer2Active: ColorUtils.mix(colLayer2, colOnLayer2, 0.80)
|
||||||
|
property color colPrimary: m3colors.m3primary
|
||||||
|
property color colOnPrimary: m3colors.m3onPrimary
|
||||||
|
property color colSecondary: m3colors.m3secondary
|
||||||
|
property color colSecondaryContainer: m3colors.m3secondaryContainer
|
||||||
|
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||||
|
property color colTooltip: m3colors.m3inverseSurface
|
||||||
|
property color colOnTooltip: m3colors.m3inverseOnSurface
|
||||||
|
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
|
||||||
|
property color colOutline: m3colors.m3outline
|
||||||
|
}
|
||||||
|
|
||||||
|
rounding: QtObject {
|
||||||
|
property int unsharpen: Common.Config.options.appearance.rounding.unsharpen
|
||||||
|
property int verysmall: Common.Config.options.appearance.rounding.verysmall
|
||||||
|
property int small: Common.Config.options.appearance.rounding.small
|
||||||
|
property int normal: Common.Config.options.appearance.rounding.normal
|
||||||
|
property int large: Common.Config.options.appearance.rounding.large
|
||||||
|
property int full: Common.Config.options.appearance.rounding.full
|
||||||
|
property int screenRounding: Common.Config.options.appearance.rounding.screenRounding
|
||||||
|
property int windowRounding: Common.Config.options.appearance.rounding.windowRounding
|
||||||
|
}
|
||||||
|
|
||||||
|
font: QtObject {
|
||||||
|
property QtObject family: QtObject {
|
||||||
|
property string main: Common.Config.options.appearance.font.family.main
|
||||||
|
property string title: Common.Config.options.appearance.font.family.title
|
||||||
|
property string expressive: Common.Config.options.appearance.font.family.expressive
|
||||||
|
}
|
||||||
|
property QtObject pixelSize: QtObject {
|
||||||
|
property int smaller: Common.Config.options.appearance.font.pixelSize.smaller
|
||||||
|
property int small: Common.Config.options.appearance.font.pixelSize.small
|
||||||
|
property int normal: Common.Config.options.appearance.font.pixelSize.normal
|
||||||
|
property int larger: Common.Config.options.appearance.font.pixelSize.larger
|
||||||
|
property int huge: Common.Config.options.appearance.font.pixelSize.huge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animationCurves: QtObject {
|
||||||
|
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1]
|
||||||
|
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||||
|
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
|
||||||
|
readonly property real expressiveDefaultSpatialDuration: Common.Config.options.appearance.animation.duration.elementMove
|
||||||
|
readonly property real expressiveEffectsDuration: Common.Config.options.appearance.animation.duration.elementMoveFast
|
||||||
|
}
|
||||||
|
|
||||||
|
animation: QtObject {
|
||||||
|
property QtObject elementMove: QtObject {
|
||||||
|
property int duration: animationCurves.expressiveDefaultSpatialDuration
|
||||||
|
property int type: Easing.BezierSpline
|
||||||
|
property list<real> bezierCurve: animationCurves.expressiveDefaultSpatial
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMove.duration
|
||||||
|
easing.type: root.animation.elementMove.type
|
||||||
|
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject elementMoveEnter: QtObject {
|
||||||
|
property int duration: Common.Config.options.appearance.animation.duration.elementMoveEnter
|
||||||
|
property int type: Easing.BezierSpline
|
||||||
|
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMoveEnter.duration
|
||||||
|
easing.type: root.animation.elementMoveEnter.type
|
||||||
|
easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject elementMoveFast: QtObject {
|
||||||
|
property int duration: animationCurves.expressiveEffectsDuration
|
||||||
|
property int type: Easing.BezierSpline
|
||||||
|
property list<real> bezierCurve: animationCurves.expressiveEffects
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMoveFast.duration
|
||||||
|
easing.type: root.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sizes: QtObject {
|
||||||
|
property real elevationMargin: Common.Config.options.appearance.sizes.elevationMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var userOptions: ({})
|
||||||
|
|
||||||
|
function read(path, fallback) {
|
||||||
|
const parts = path.split(".");
|
||||||
|
let current = userOptions;
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (current === null || current === undefined || typeof current !== "object" || !(part in current)) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
current = current[part];
|
||||||
|
}
|
||||||
|
|
||||||
|
return current === undefined || current === null ? fallback : current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readInt(path, fallback) {
|
||||||
|
const value = read(path, fallback);
|
||||||
|
const parsed = Number(value);
|
||||||
|
return Number.isInteger(parsed) ? parsed : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readReal(path, fallback) {
|
||||||
|
const value = read(path, fallback);
|
||||||
|
const parsed = Number(value);
|
||||||
|
return Number.isFinite(parsed) ? parsed : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBool(path, fallback) {
|
||||||
|
const value = read(path, fallback);
|
||||||
|
return typeof value === "boolean" ? value : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readString(path, fallback) {
|
||||||
|
const value = read(path, fallback);
|
||||||
|
if (typeof value !== "string")
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return trimmed.length > 0 ? trimmed : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject options: QtObject {
|
||||||
|
property QtObject appearance: QtObject {
|
||||||
|
property string colorSource: root.readString(
|
||||||
|
"appearance.colorSource",
|
||||||
|
root.readBool("appearance.useMatugenColors", false) ? "matugen" : "default"
|
||||||
|
)
|
||||||
|
property bool useMatugenColors: colorSource === "matugen"
|
||||||
|
property QtObject caelestia: QtObject {
|
||||||
|
property bool autoRefresh: root.readBool("appearance.caelestia.autoRefresh", true)
|
||||||
|
property int refreshInterval: root.readInt("appearance.caelestia.refreshInterval", 2000)
|
||||||
|
property string accentProfile: root.readString("appearance.caelestia.accentProfile", "vibrant")
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject rounding: QtObject {
|
||||||
|
property int unsharpen: root.readInt("appearance.rounding.unsharpen", 2)
|
||||||
|
property int verysmall: root.readInt("appearance.rounding.verysmall", 8)
|
||||||
|
property int small: root.readInt("appearance.rounding.small", 12)
|
||||||
|
property int normal: root.readInt("appearance.rounding.normal", 17)
|
||||||
|
property int large: root.readInt("appearance.rounding.large", 23)
|
||||||
|
property int full: root.readInt("appearance.rounding.full", 9999)
|
||||||
|
property int screenRounding: root.readInt("appearance.rounding.screenRounding", large)
|
||||||
|
property int windowRounding: root.readInt("appearance.rounding.windowRounding", 18)
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject font: QtObject {
|
||||||
|
property QtObject family: QtObject {
|
||||||
|
property string main: root.readString("appearance.font.family.main", "sans-serif")
|
||||||
|
property string title: root.readString("appearance.font.family.title", "sans-serif")
|
||||||
|
property string expressive: root.readString("appearance.font.family.expressive", "sans-serif")
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject pixelSize: QtObject {
|
||||||
|
property int smaller: root.readInt("appearance.font.pixelSize.smaller", 12)
|
||||||
|
property int small: root.readInt("appearance.font.pixelSize.small", 15)
|
||||||
|
property int normal: root.readInt("appearance.font.pixelSize.normal", 16)
|
||||||
|
property int larger: root.readInt("appearance.font.pixelSize.larger", 19)
|
||||||
|
property int huge: root.readInt("appearance.font.pixelSize.huge", 22)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject animation: QtObject {
|
||||||
|
property QtObject duration: QtObject {
|
||||||
|
property int elementMove: root.readInt("appearance.animation.duration.elementMove", 500)
|
||||||
|
property int elementMoveEnter: root.readInt("appearance.animation.duration.elementMoveEnter", 400)
|
||||||
|
property int elementMoveFast: root.readInt("appearance.animation.duration.elementMoveFast", 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject sizes: QtObject {
|
||||||
|
property real elevationMargin: root.readReal("appearance.sizes.elevationMargin", 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject overview: QtObject {
|
||||||
|
property int rows: root.readInt("overview.rows", 2)
|
||||||
|
property int columns: root.readInt("overview.columns", 5)
|
||||||
|
property real scale: root.readReal("overview.scale", 0.16)
|
||||||
|
property bool enable: root.readBool("overview.enable", true)
|
||||||
|
property bool hideEmptyRows: root.readBool("overview.hideEmptyRows", true)
|
||||||
|
property bool closeOnFocusLoss: root.readBool("overview.closeOnFocusLoss", true)
|
||||||
|
property bool useWorkspaceMap: root.readBool("overview.useWorkspaceMap", false)
|
||||||
|
property var workspaceMap: root.read("overview.workspaceMap", [])
|
||||||
|
property bool orderRightLeft: root.readBool("overview.orderRightLeft", false)
|
||||||
|
property bool orderBottomUp: root.readBool("overview.orderBottomUp", false)
|
||||||
|
property bool previewsEnabled: root.readBool("overview.previewsEnabled", true)
|
||||||
|
property string previewMode: root.readString("overview.previewMode", "live")
|
||||||
|
property bool includeInactiveMonitorPreviews: root.readBool("overview.includeInactiveMonitorPreviews", true)
|
||||||
|
property int previewRecaptureDelayMs: root.readInt("overview.previewRecaptureDelayMs", 60)
|
||||||
|
property bool showSpecialWorkspaces: root.readBool("overview.showSpecialWorkspaces", true)
|
||||||
|
property var specialWorkspaces: root.read("overview.specialWorkspaces", [])
|
||||||
|
property int specialWorkspaceColumns: root.readInt("overview.specialWorkspaceColumns", columns)
|
||||||
|
property string emptyWorkspaceWallpaper: root.readString("overview.emptyWorkspaceWallpaper", "")
|
||||||
|
property string specialEmptyWorkspaceWallpaper: root.readString("overview.specialEmptyWorkspaceWallpaper", "")
|
||||||
|
property real workspaceSpacing: root.readReal("overview.workspaceSpacing", 5)
|
||||||
|
property real backgroundPadding: root.readReal("overview.backgroundPadding", 10)
|
||||||
|
property real workspaceNumberBaseSize: root.readReal("overview.workspaceNumberBaseSize", 250)
|
||||||
|
property QtObject effects: QtObject {
|
||||||
|
property bool enableBackdrop: root.readBool("overview.effects.enableBackdrop", false)
|
||||||
|
property real backdropOpacity: root.readReal("overview.effects.backdropOpacity", 0.28)
|
||||||
|
property real panelOpacity: root.readReal("overview.effects.panelOpacity", 0.92)
|
||||||
|
property real workspaceOpacity: root.readReal("overview.effects.workspaceOpacity", 0.86)
|
||||||
|
property real emptyWorkspaceWallpaperOverlayOpacity: root.readReal("overview.effects.emptyWorkspaceWallpaperOverlayOpacity", 0.18)
|
||||||
|
property real windowOverlayOpacity: root.readReal("overview.effects.windowOverlayOpacity", 0.22)
|
||||||
|
property bool enableBlur: root.readBool("overview.effects.enableBlur", false)
|
||||||
|
property bool glassMode: root.readBool("overview.effects.glassMode", false)
|
||||||
|
property real glassTintStrength: root.readReal("overview.effects.glassTintStrength", 0.35)
|
||||||
|
property real glassBorderOpacity: root.readReal("overview.effects.glassBorderOpacity", 0.72)
|
||||||
|
property real glassShineOpacity: root.readReal("overview.effects.glassShineOpacity", 0.14)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject position: QtObject {
|
||||||
|
property int topMargin: root.readInt("position.topMargin", 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject windowPreview: QtObject {
|
||||||
|
property bool showIcons: root.readBool("windowPreview.showIcons", true)
|
||||||
|
property real iconToWindowRatio: root.readReal("windowPreview.iconToWindowRatio", 0.25)
|
||||||
|
property real iconToWindowRatioCompact: root.readReal("windowPreview.iconToWindowRatioCompact", 0.45)
|
||||||
|
property real xwaylandIndicatorToIconRatio: root.readReal("windowPreview.xwaylandIndicatorToIconRatio", 0.35)
|
||||||
|
property real inactiveMonitorOpacity: root.readReal("windowPreview.inactiveMonitorOpacity", 0.4)
|
||||||
|
property bool cropToFill: root.readBool("windowPreview.cropToFill", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject hacks: QtObject {
|
||||||
|
property int arbitraryRaceConditionDelay: root.readInt("hacks.arbitraryRaceConditionDelay", 150)
|
||||||
|
property int hyprlandEventDebounceMs: root.readInt("hacks.hyprlandEventDebounceMs", 40)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: loadUserConfig
|
||||||
|
command: [
|
||||||
|
"sh",
|
||||||
|
"-lc",
|
||||||
|
"cfg=\"${XDG_CONFIG_HOME:-$HOME/.config}/quickshell/overview/config.json\"; [ -r \"$cfg\" ] && cat \"$cfg\""
|
||||||
|
]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
id: configCollector
|
||||||
|
onStreamFinished: {
|
||||||
|
const payload = configCollector.text.trim();
|
||||||
|
if (!payload)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(payload);
|
||||||
|
if (typeof parsed === "object" && parsed !== null) {
|
||||||
|
root.userOptions = parsed;
|
||||||
|
} else {
|
||||||
|
console.warn("overview: config.json must contain a JSON object; ignoring file");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("overview: failed to parse user config.json; using defaults", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
loadUserConfig.running = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
pragma Singleton
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function colorWithHueOf(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
var hue = c2.hsvHue;
|
||||||
|
var sat = c1.hsvSaturation;
|
||||||
|
var val = c1.hsvValue;
|
||||||
|
var alpha = c1.a;
|
||||||
|
return Qt.hsva(hue, sat, val, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorWithSaturationOf(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
var hue = c1.hsvHue;
|
||||||
|
var sat = c2.hsvSaturation;
|
||||||
|
var val = c1.hsvValue;
|
||||||
|
var alpha = c1.a;
|
||||||
|
return Qt.hsva(hue, sat, val, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorWithLightness(color, lightness) {
|
||||||
|
var c = Qt.color(color);
|
||||||
|
return Qt.hsla(c.hslHue, c.hslSaturation, lightness, c.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorWithLightnessOf(color1, color2) {
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
return colorWithLightness(color1, c2.hslLightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
function adaptToAccent(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
var hue = c2.hslHue;
|
||||||
|
var sat = c2.hslSaturation;
|
||||||
|
var light = c1.hslLightness;
|
||||||
|
var alpha = c1.a;
|
||||||
|
return Qt.hsla(hue, sat, light, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mix(color1, color2, percentage = 0.5) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
return Qt.rgba(
|
||||||
|
percentage * c1.r + (1 - percentage) * c2.r,
|
||||||
|
percentage * c1.g + (1 - percentage) * c2.g,
|
||||||
|
percentage * c1.b + (1 - percentage) * c2.b,
|
||||||
|
percentage * c1.a + (1 - percentage) * c2.a
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transparentize(color, percentage = 1) {
|
||||||
|
var c = Qt.color(color);
|
||||||
|
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAlpha(color, alpha) {
|
||||||
|
var c = Qt.color(color);
|
||||||
|
var a = Math.max(0, Math.min(1, alpha));
|
||||||
|
return Qt.rgba(c.r, c.g, c.b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
singleton ColorUtils 1.0 ColorUtils.qml
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
singleton Appearance 1.0 Appearance.qml
|
||||||
|
singleton Config 1.0 Config.qml
|
||||||
|
singleton ColorUtils 1.0 functions/ColorUtils.qml
|
||||||
|
StyledText 1.0 widgets/StyledText.qml
|
||||||
|
StyledRectangularShadow 1.0 widgets/StyledRectangularShadow.qml
|
||||||
|
StyledToolTip 1.0 widgets/StyledToolTip.qml
|
||||||
|
StyledToolTipContent 1.0 widgets/StyledToolTipContent.qml
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
RectangularShadow {
|
||||||
|
required property var target
|
||||||
|
anchors.fill: target
|
||||||
|
radius: 20
|
||||||
|
blur: 0.9 * Appearance.sizes.elevationMargin
|
||||||
|
offset: Qt.vector2d(0.0, 1.0)
|
||||||
|
spread: 1
|
||||||
|
color: Appearance.colors.colShadow
|
||||||
|
cached: true
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: root
|
||||||
|
property bool animateChange: false
|
||||||
|
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
font {
|
||||||
|
hintingPreference: Font.PreferFullHinting
|
||||||
|
family: Appearance?.font.family.main ?? "sans-serif"
|
||||||
|
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||||
|
}
|
||||||
|
color: Appearance?.m3colors.m3onBackground ?? "white"
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import "."
|
||||||
|
|
||||||
|
ToolTip {
|
||||||
|
id: root
|
||||||
|
property bool extraVisibleCondition: true
|
||||||
|
property bool alternativeVisibleCondition: false
|
||||||
|
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
|
||||||
|
verticalPadding: 5
|
||||||
|
horizontalPadding: 10
|
||||||
|
background: null
|
||||||
|
|
||||||
|
visible: internalVisibleCondition
|
||||||
|
|
||||||
|
contentItem: StyledToolTipContent {
|
||||||
|
id: contentItem
|
||||||
|
text: root.text
|
||||||
|
shown: root.internalVisibleCondition
|
||||||
|
horizontalPadding: root.horizontalPadding
|
||||||
|
verticalPadding: root.verticalPadding
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import QtQuick
|
||||||
|
import "."
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
required property string text
|
||||||
|
property bool shown: false
|
||||||
|
property real horizontalPadding: 10
|
||||||
|
property real verticalPadding: 5
|
||||||
|
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
|
||||||
|
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
|
||||||
|
|
||||||
|
property bool isVisible: backgroundRectangle.implicitHeight > 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backgroundRectangle
|
||||||
|
anchors {
|
||||||
|
bottom: root.bottom
|
||||||
|
horizontalCenter: root.horizontalCenter
|
||||||
|
}
|
||||||
|
color: Appearance?.colors.colTooltip ?? "#3C4043"
|
||||||
|
radius: Appearance?.rounding.verysmall ?? 7
|
||||||
|
opacity: shown ? 1 : 0
|
||||||
|
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
|
||||||
|
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: tooltipTextObject
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.text
|
||||||
|
font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14
|
||||||
|
font.hintingPreference: Font.PreferNoHinting
|
||||||
|
color: Appearance?.colors.colOnTooltip ?? "#FFFFFF"
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
StyledText 1.0 StyledText.qml
|
||||||
|
StyledRectangularShadow 1.0 StyledRectangularShadow.qml
|
||||||
|
StyledToolTip 1.0 StyledToolTip.qml
|
||||||
|
StyledToolTipContent 1.0 StyledToolTipContent.qml
|
||||||
+1
-1
@@ -25,7 +25,7 @@ Pill {
|
|||||||
text: root.icon + " " + root.capacity + "%"
|
text: root.icon + " " + root.capacity + "%"
|
||||||
font.family: Theme.fontSans
|
font.family: Theme.fontSans
|
||||||
font.pixelSize: Theme.fontSize
|
font.pixelSize: Theme.fontSize
|
||||||
color: root.critical ? Theme.color1 : Theme.foreground
|
color: root.critical ? Theme.color1 : Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
+1
-1
@@ -30,7 +30,7 @@ Pill {
|
|||||||
return "ᛒ off"
|
return "ᛒ off"
|
||||||
}
|
}
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll bluetoothctl show + info every 5 s
|
// Poll bluetoothctl show + info every 5 s
|
||||||
+4
-2
@@ -9,13 +9,15 @@ Rectangle {
|
|||||||
|
|
||||||
property var bars: Array(12).fill(0)
|
property var bars: Array(12).fill(0)
|
||||||
property bool silence: bars.every(v => v === 0)
|
property bool silence: bars.every(v => v === 0)
|
||||||
|
property bool allBlank: bars.every(v => v < 28.5)
|
||||||
|
|
||||||
readonly property var blocks: [" ","▁","▂","▃","▄","▅","▆","▇","█"]
|
readonly property var blocks: [" ","▁","▂","▃","▄","▅","▆","▇","█"]
|
||||||
|
|
||||||
|
visible: !allBlank
|
||||||
implicitWidth: cavaRow.implicitWidth + Theme.pillPadH * 2
|
implicitWidth: cavaRow.implicitWidth + Theme.pillPadH * 2
|
||||||
implicitHeight: Theme.barHeight
|
implicitHeight: Theme.barHeight
|
||||||
radius: Theme.radius
|
radius: Theme.radius
|
||||||
color: cavaHover.containsMouse ? Theme.pillHover : Theme.pill
|
color: cavaHover.containsMouse ? Theme.pill : Theme.pill
|
||||||
Behavior on color { ColorAnimation { duration: 150 } }
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -32,7 +34,7 @@ Rectangle {
|
|||||||
: root.blocks[Math.min(Math.floor(root.bars[index] / 28.5), 8)]
|
: root.blocks[Math.min(Math.floor(root.bars[index] / 28.5), 8)]
|
||||||
font.family: Theme.fontMono
|
font.family: Theme.fontMono
|
||||||
font.pixelSize: Theme.fontSize + 1
|
font.pixelSize: Theme.fontSize + 1
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -14,7 +14,7 @@ Pill {
|
|||||||
text: " " + Qt.formatDateTime(clock.now, "HH:mm") +
|
text: " " + Qt.formatDateTime(clock.now, "HH:mm") +
|
||||||
" " + Qt.formatDateTime(clock.now, "d MMM")
|
" " + Qt.formatDateTime(clock.now, "d MMM")
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update every 10 s (no need for per-second ticks)
|
// Update every 10 s (no need for per-second ticks)
|
||||||
+1
-1
@@ -19,7 +19,7 @@ Pill {
|
|||||||
text: root.freqGhz.toFixed(1) + "GHz | " + root.usagePct + "%"
|
text: root.freqGhz.toFixed(1) + "GHz | " + root.usagePct + "%"
|
||||||
font.family: Theme.fontSans
|
font.family: Theme.fontSans
|
||||||
font.pixelSize: Theme.fontSize
|
font.pixelSize: Theme.fontSize
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// /proc/stat - first line is total CPU
|
// /proc/stat - first line is total CPU
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
// MediaCavaWidget.qml - cava visualiser + media info + volume in one pill
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// ── Cava ──────────────────────────────────────────────────
|
||||||
|
property var bars: Array(12).fill(0)
|
||||||
|
property bool silence: bars.every(v => v === 0)
|
||||||
|
property bool allBlank: bars.every(v => v < 28.5)
|
||||||
|
property bool silentMode: false
|
||||||
|
readonly property var blocks: [" ","▁","▂","▃","▄","▅","▆","▇","█"]
|
||||||
|
|
||||||
|
// ── Audio (wpctl - avoids PipeWire binding issues) ────────
|
||||||
|
property real vol: 0.0
|
||||||
|
property bool muted: false
|
||||||
|
property string volIcon: {
|
||||||
|
if (muted || vol === 0) return " "
|
||||||
|
if (vol < 0.34) return " "
|
||||||
|
if (vol < 0.67) return " "
|
||||||
|
return " "
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Media ─────────────────────────────────────────────────
|
||||||
|
property MprisPlayer activePlayer: {
|
||||||
|
var players = Mpris.players.values
|
||||||
|
for (var i = 0; i < players.length; i++)
|
||||||
|
if (players[i].identity.toLowerCase() === "spotify") return players[i]
|
||||||
|
return players.length > 0 ? players[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
property string trackText: {
|
||||||
|
if (!activePlayer) return ""
|
||||||
|
var p = activePlayer
|
||||||
|
var info = ""
|
||||||
|
if (p.trackArtists && p.trackTitle)
|
||||||
|
info = Array.from(p.trackArtists).join(", ") + " - " + p.trackTitle
|
||||||
|
else if (p.trackTitle)
|
||||||
|
info = p.trackTitle
|
||||||
|
if (info.length > 45) info = info.substring(0, 45) + "..."
|
||||||
|
if (p.playbackState !== MprisPlaybackState.Playing && info)
|
||||||
|
info = " " + info
|
||||||
|
return info + " "
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Silence delay ─────────────────────────────────────────
|
||||||
|
onAllBlankChanged: {
|
||||||
|
if (allBlank) silenceTimer.start()
|
||||||
|
else { silenceTimer.stop(); silentMode = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: silenceTimer
|
||||||
|
interval: Theme.cavaDissapearTime
|
||||||
|
repeat: false
|
||||||
|
onTriggered: root.silentMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Hover → media popup ───────────────────────────────────
|
||||||
|
property bool hovered: mouseArea.containsMouse
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered) GlobalStates.popups.open("media", mapToItem(null, width / 2, 0).x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Appearance ────────────────────────────────────────────
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
implicitWidth: contentRow.implicitWidth + Theme.pillPadH * 2
|
||||||
|
radius: Theme.radius
|
||||||
|
clip: true
|
||||||
|
color: root.hovered ? Theme.pillHover : Theme.pill
|
||||||
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
// Volume fill - slightly more opaque than background, fills left→right by volume %
|
||||||
|
Rectangle {
|
||||||
|
anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
|
||||||
|
width: parent.width * Math.min(root.vol, 1.0)
|
||||||
|
radius: Theme.radius
|
||||||
|
color: root.hovered ? Theme.setColorAlpha(Theme.pill, 0.3) : Theme.setColorAlpha(Theme.pill, 0.9)
|
||||||
|
Behavior on width { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Content ───────────────────────────────────────────────
|
||||||
|
RowLayout {
|
||||||
|
id: contentRow
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.pillPadH
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
// Cava bars -d slides right and collapses after silence delay
|
||||||
|
Item {
|
||||||
|
id: cavaContainer
|
||||||
|
clip: true
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
implicitWidth: root.silentMode ? 0 : cavaRow.implicitWidth
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
NumberAnimation { duration: 350; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
id: barMetrics
|
||||||
|
font.family: Theme.fontMono
|
||||||
|
font.pixelSize: Theme.fontSize + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: cavaRow
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 1
|
||||||
|
x: root.silentMode ? 16 : 0
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation { duration: 350; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.bars.length
|
||||||
|
Text {
|
||||||
|
required property int index
|
||||||
|
width: barMetrics.advanceWidth("█")
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: root.silence
|
||||||
|
? " "
|
||||||
|
: root.blocks[Math.min(Math.floor(root.bars[index] / 28.5), 8)]
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize + 1 }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track info
|
||||||
|
Text {
|
||||||
|
visible: root.trackText !== ""
|
||||||
|
text: root.trackText
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume icon
|
||||||
|
Text {
|
||||||
|
text: root.volIcon
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize }
|
||||||
|
color: "#fab387"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume percentage
|
||||||
|
Text {
|
||||||
|
text: root.muted ? "muted" : Math.round(root.vol * 100) + "%"
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
|
onClicked: (m) => {
|
||||||
|
if (m.button === Qt.RightButton) {
|
||||||
|
Exec.run(["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"])
|
||||||
|
root.muted = !root.muted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onWheel: (w) => {
|
||||||
|
var step = w.angleDelta.y > 0 ? "4%+" : "4%-"
|
||||||
|
Exec.run(["wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", step])
|
||||||
|
// Optimistic update - poll syncs it within 1s
|
||||||
|
root.vol = Math.max(0, Math.min(1.5, root.vol + (w.angleDelta.y > 0 ? 0.04 : -0.04)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Volume poll (wpctl) ───────────────────────────────────
|
||||||
|
Process {
|
||||||
|
id: volProc
|
||||||
|
running: false
|
||||||
|
command: ["wpctl", "get-volume", "@DEFAULT_AUDIO_SINK@"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
// "Volume: 0.40" or "Volume: 0.40 [MUTED]"
|
||||||
|
var m = /Volume:\s+([\d.]+)(.*)/.exec(line)
|
||||||
|
if (m) {
|
||||||
|
root.vol = parseFloat(m[1])
|
||||||
|
root.muted = m[2].includes("MUTED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: volProc.running = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: volProc.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Cava process ──────────────────────────────────────────
|
||||||
|
Component.onCompleted: writeCfg.running = true
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: writeCfg
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"mkdir -p /tmp/qs-cava && cat > /tmp/qs-cava/cava.ini <<'CFG'\n" +
|
||||||
|
"[general]\n" +
|
||||||
|
"framerate = 30\n" +
|
||||||
|
"bars = 12\n" +
|
||||||
|
"[input]\n" +
|
||||||
|
"method = pipewire\n" +
|
||||||
|
"source = auto\n" +
|
||||||
|
"[smoothing]\n" +
|
||||||
|
"noise_reduction = 77\n" +
|
||||||
|
"monstercat = 1\n" +
|
||||||
|
"[output]\n" +
|
||||||
|
"method = raw\n" +
|
||||||
|
"raw_target = /dev/stdout\n" +
|
||||||
|
"data_format = ascii\n" +
|
||||||
|
"ascii_max_range = 255\n" +
|
||||||
|
"bar_delimiter = 59\n" +
|
||||||
|
"CFG\n"
|
||||||
|
]
|
||||||
|
onExited: cavaProc.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: cavaProc
|
||||||
|
running: false
|
||||||
|
command: ["cava", "-p", "/tmp/qs-cava/cava.ini"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
const parts = line.trim().replace(/;$/, "").split(";")
|
||||||
|
if (parts.length >= root.bars.length)
|
||||||
|
root.bars = parts.slice(0, root.bars.length).map(v => parseInt(v) || 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onExited: cavaProc.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
// MediaCavaWidget.qml - cava visualiser + media info + volume in one pill
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: root
|
||||||
|
popupName: "media"
|
||||||
|
|
||||||
|
property MprisPlayer player: {
|
||||||
|
var players = Mpris.players.values
|
||||||
|
for (var i = 0; i < players.length; i++)
|
||||||
|
if (players[i].identity.toLowerCase() === "spotify") return players[i]
|
||||||
|
return players.length > 0 ? players[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isPlaying: player?.playbackState === MprisPlaybackState.Playing ?? false
|
||||||
|
property real displayPosition: 0
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
running: root.visible && root.player !== null
|
||||||
|
repeat: true
|
||||||
|
triggeredOnStart: true
|
||||||
|
onTriggered: root.displayPosition = root.player?.position ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(secs) {
|
||||||
|
secs = Math.max(0, Math.floor(secs))
|
||||||
|
return Math.floor(secs / 60) + ":" + String(secs % 60).padStart(2, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
component CtrlBtn: Text {
|
||||||
|
property string icon: ""
|
||||||
|
property color baseColor: Theme.pillText
|
||||||
|
property color hoverColor: Theme.pillHover
|
||||||
|
signal activate()
|
||||||
|
text: icon
|
||||||
|
font { family: Theme.fontMono; pixelSize: 20 }
|
||||||
|
color: ma.containsMouse ? hoverColor : baseColor
|
||||||
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
MouseArea {
|
||||||
|
id: ma
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: parent.activate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: 200
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Album art - hidden when no art is available
|
||||||
|
Rectangle {
|
||||||
|
visible: artImage.status === Image.Ready
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredWidth: 180
|
||||||
|
Layout.preferredHeight: artImage.status === Image.Ready ? 180 : 0
|
||||||
|
Layout.topMargin: artImage.status === Image.Ready ? 10 : 0
|
||||||
|
radius: Theme.radius
|
||||||
|
clip: true
|
||||||
|
color: Theme.pill
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: artImage
|
||||||
|
anchors.fill: parent
|
||||||
|
source: root.player?.trackArtUrl ?? ""
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
smooth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (root.player)
|
||||||
|
Exec.run(["hyprctl", "dispatch", "hl.dsp.focus({ window = 'class:" + root.player.identity + "' })"])
|
||||||
|
GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track title with marquee scroll
|
||||||
|
Item {
|
||||||
|
id: titleClip
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 15
|
||||||
|
Layout.rightMargin: 15
|
||||||
|
Layout.topMargin: 4
|
||||||
|
implicitHeight: titleText.implicitHeight
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: titleText
|
||||||
|
text: root.player?.trackTitle ?? "Nothing playing"
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize + 1; bold: true }
|
||||||
|
color: titleMa.containsMouse && root.player ? Qt.lighter(Theme.pillText, 1.3) : Theme.pillText
|
||||||
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: titleMa
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: root.player ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: if (root.player) {
|
||||||
|
Exec.run(["hyprctl", "dispatch", "hl.dsp.focus({ window = 'class:" + root.player.identity + "' })"])
|
||||||
|
GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
marquee.stop()
|
||||||
|
x = 0
|
||||||
|
Qt.callLater(() => {
|
||||||
|
if (implicitWidth > titleClip.width) marquee.start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: Qt.callLater(() => {
|
||||||
|
if (titleText.implicitWidth > width) marquee.start()
|
||||||
|
})
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: marquee
|
||||||
|
loops: Animation.Infinite
|
||||||
|
|
||||||
|
PauseAnimation { duration: 1500 }
|
||||||
|
NumberAnimation {
|
||||||
|
target: titleText; property: "x"
|
||||||
|
from: 0
|
||||||
|
to: -(titleText.implicitWidth - titleClip.width + 10)
|
||||||
|
duration: Math.max(1, titleText.implicitWidth - titleClip.width) * 18
|
||||||
|
easing.type: Easing.Linear
|
||||||
|
}
|
||||||
|
PauseAnimation { duration: 800 }
|
||||||
|
NumberAnimation {
|
||||||
|
target: titleText; property: "x"
|
||||||
|
to: 0; duration: 400; easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 15
|
||||||
|
Layout.rightMargin: 15
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: root.player?.trackArtists ? Array.from(root.player.trackArtists).join(", ") : ""
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress bar
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 15
|
||||||
|
Layout.rightMargin: 15
|
||||||
|
height: 3
|
||||||
|
radius: 2
|
||||||
|
color: Theme.pill
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: root.player && root.player.length > 0
|
||||||
|
? Math.min(1, root.displayPosition / root.player.length) * parent.width
|
||||||
|
: 0
|
||||||
|
height: parent.height
|
||||||
|
radius: parent.radius
|
||||||
|
color: Theme.color6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time labels
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 15
|
||||||
|
Layout.rightMargin: 15
|
||||||
|
Text {
|
||||||
|
text: root.formatTime(root.displayPosition)
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize - 1 }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
Text {
|
||||||
|
text: root.formatTime(root.player?.length ?? 0)
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize - 1 }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
spacing: 20
|
||||||
|
CtrlBtn { text: ""; onActivate: root.player?.previous() }
|
||||||
|
CtrlBtn { text: root.isPlaying ? "" : ""; onActivate: root.player?.togglePlaying() }
|
||||||
|
CtrlBtn { text: ""; onActivate: root.player?.stop() }
|
||||||
|
CtrlBtn { text: ""; onActivate: root.player?.next() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-2
@@ -21,7 +21,7 @@ Pill {
|
|||||||
var p = activePlayer
|
var p = activePlayer
|
||||||
var info = ""
|
var info = ""
|
||||||
if (p.trackArtists && p.trackTitle)
|
if (p.trackArtists && p.trackTitle)
|
||||||
info = p.trackArtists.join(", ") + " - " + p.trackTitle
|
info = Array.from(p.trackArtists).join(", ") + " - " + p.trackTitle
|
||||||
else if (p.trackTitle)
|
else if (p.trackTitle)
|
||||||
info = p.trackTitle
|
info = p.trackTitle
|
||||||
if (info.length > 45) info = info.substring(0, 45) + "..."
|
if (info.length > 45) info = info.substring(0, 45) + "..."
|
||||||
@@ -35,7 +35,7 @@ Pill {
|
|||||||
Text {
|
Text {
|
||||||
text: root.trackText
|
text: root.trackText
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
}
|
}
|
||||||
@@ -51,4 +51,8 @@ Pill {
|
|||||||
if (w.angleDelta.y > 0) root.activePlayer.next()
|
if (w.angleDelta.y > 0) root.activePlayer.next()
|
||||||
else root.activePlayer.previous()
|
else root.activePlayer.previous()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered) GlobalStates.popups.open("media", mapToItem(null, width / 2, 0).x)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -16,7 +16,7 @@ Pill {
|
|||||||
text: " " + root.usedGb.toFixed(2) + " / " + root.totalGb.toFixed(0) + " GB"
|
text: " " + root.usedGb.toFixed(2) + " / " + root.totalGb.toFixed(0) + " GB"
|
||||||
font.family: Theme.fontSans
|
font.family: Theme.fontSans
|
||||||
font.pixelSize: Theme.fontSize
|
font.pixelSize: Theme.fontSize
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// modules/PowerMenuWidget.qml - ⏻ button with inline popup menu.
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (hovered) GlobalStates.popups.toggle("powerMenu", mapToItem(null, width / 2, 0).x)
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
GlobalStates.popups.toggle("powerMenu", mapToItem(null, width / 2, 0).x)
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: " "
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -27,7 +27,7 @@ Pill {
|
|||||||
text: (root.icons[root.profile] ?? "⚡")
|
text: (root.icons[root.profile] ?? "⚡")
|
||||||
font.family: Theme.fontSans
|
font.family: Theme.fontSans
|
||||||
font.pixelSize: Theme.fontSize
|
font.pixelSize: Theme.fontSize
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read current profile periodically
|
// Read current profile periodically
|
||||||
+19
-4
@@ -2,10 +2,12 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
|
import Quickshell
|
||||||
import ".."
|
import ".."
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
required property var parentWindow
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
radius: Theme.radius
|
radius: Theme.radius
|
||||||
|
|
||||||
@@ -21,12 +23,13 @@ Rectangle {
|
|||||||
model: SystemTray.items
|
model: SystemTray.items
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: trayRect
|
||||||
required property SystemTrayItem modelData
|
required property SystemTrayItem modelData
|
||||||
width: 22; height: 22
|
width: Theme.barHeight-Theme.barPadding*2; height: Theme.barHeight-Theme.barPadding*2
|
||||||
radius: Theme.radius
|
radius: Theme.radius
|
||||||
color: trayHover.containsMouse
|
color: trayHover.containsMouse
|
||||||
? Theme.pillHover
|
? Theme.pillHover
|
||||||
: Qt.rgba(0.976, 0.945, 0.851, 0.15)
|
: Theme.pill
|
||||||
|
|
||||||
Behavior on color { ColorAnimation { duration: 150 } }
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
@@ -37,6 +40,18 @@ Rectangle {
|
|||||||
smooth: true
|
smooth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QsMenuAnchor {
|
||||||
|
id: menuAnchor
|
||||||
|
menu: modelData.menu
|
||||||
|
anchor.window: root.parentWindow
|
||||||
|
anchor.rect: Qt.rect(
|
||||||
|
trayRect.mapToItem(null, 0, 0).x,
|
||||||
|
trayRect.mapToItem(null, 0, 0).y,
|
||||||
|
trayRect.width,
|
||||||
|
trayRect.height
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: trayHover
|
id: trayHover
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -45,8 +60,8 @@ Rectangle {
|
|||||||
onClicked: (m) => {
|
onClicked: (m) => {
|
||||||
if (m.button === Qt.LeftButton)
|
if (m.button === Qt.LeftButton)
|
||||||
modelData.activate()
|
modelData.activate()
|
||||||
else
|
else if (modelData.hasMenu)
|
||||||
modelData.contextMenu(mapToGlobal(mouseX, mouseY))
|
menuAnchor.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+1
-1
@@ -18,7 +18,7 @@ Pill {
|
|||||||
text: root.icon + " " + root.tempC + "°C"
|
text: root.icon + " " + root.tempC + "°C"
|
||||||
font.family: Theme.fontSans
|
font.family: Theme.fontSans
|
||||||
font.pixelSize: Theme.fontSize
|
font.pixelSize: Theme.fontSize
|
||||||
color: root.critical ? Theme.color1 : Theme.foreground
|
color: root.critical ? Theme.color1 : Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read first available CPU package sensor - works regardless of hwmon number
|
// Read first available CPU package sensor - works regardless of hwmon number
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
// WallpaperPopup.qml - A popup for browsing and setting wallpapers from /usr/share/wallpapers
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
property bool logicalOpen: GlobalStates.popups.active === "wallpaper"
|
||||||
|
property bool _keepVisible: false
|
||||||
|
visible: logicalOpen || _keepVisible
|
||||||
|
|
||||||
|
onLogicalOpenChanged: {
|
||||||
|
if (logicalOpen) {
|
||||||
|
closeTimer.stop()
|
||||||
|
_keepVisible = false
|
||||||
|
if (wallpaperModel.count === 0) scanProc.running = true
|
||||||
|
openY.start()
|
||||||
|
openOpacity.start()
|
||||||
|
focusTimer.start()
|
||||||
|
} else {
|
||||||
|
_keepVisible = true
|
||||||
|
closeY.start()
|
||||||
|
closeOpacity.start()
|
||||||
|
closeTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer { id: closeTimer; interval: 280; onTriggered: root._keepVisible = false }
|
||||||
|
Timer { id: focusTimer; interval: 50; onTriggered: grid.forceActiveFocus() }
|
||||||
|
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.namespace: "quickshell-popup-wallpaper"
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
anchors { top: true; left: true; right: true; bottom: true }
|
||||||
|
color: Qt.rgba(0, 0, 0, 0)
|
||||||
|
|
||||||
|
MouseArea { anchors.fill: parent; onClicked: GlobalStates.popups.close() }
|
||||||
|
|
||||||
|
NumberAnimation { id: openY; target: box; property: "y"; to: 1; duration: 250; easing.type: Easing.OutCubic }
|
||||||
|
NumberAnimation { id: closeY; target: box; property: "y"; to: -340; duration: 200; easing.type: Easing.InCubic }
|
||||||
|
NumberAnimation { id: openOpacity; target: box; property: "opacity"; to: Theme.popupOpacity; duration: 200 }
|
||||||
|
NumberAnimation { id: closeOpacity; target: box; property: "opacity"; to: 0.0; duration: 200 }
|
||||||
|
|
||||||
|
// ── Named-pipe IPC ────────────────────────────────────────
|
||||||
|
// Trigger from Hyprland keybind: echo 1 > /tmp/qs-wallpaper-ipc
|
||||||
|
// Trigger from another QML popup: Exec.run(["bash", "-c", "echo 1 > /tmp/qs-wallpaper-ipc"])
|
||||||
|
Process {
|
||||||
|
id: ipcReader
|
||||||
|
running: true
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"rm -f /tmp/qs-wallpaper-ipc && " +
|
||||||
|
"mkfifo /tmp/qs-wallpaper-ipc && " +
|
||||||
|
"while true; do cat /tmp/qs-wallpaper-ipc; done"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: () => GlobalStates.popups.toggle("wallpaper", 0)
|
||||||
|
}
|
||||||
|
onExited: running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Wallpaper scanner ─────────────────────────────────────
|
||||||
|
ListModel { id: wallpaperModel }
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: scanProc
|
||||||
|
running: false
|
||||||
|
command: ["bash", "-c",
|
||||||
|
"find /usr/share/wallpapers -type f " +
|
||||||
|
"\\( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \\) " +
|
||||||
|
"| sort"]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: (line) => {
|
||||||
|
var p = line.trim()
|
||||||
|
if (p) wallpaperModel.append({ path: p })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process { id: setProc; running: false }
|
||||||
|
|
||||||
|
// ── Popup box ─────────────────────────────────────────────
|
||||||
|
Rectangle {
|
||||||
|
id: box
|
||||||
|
|
||||||
|
// anchorX = 0 → centered; anchorX > 0 → centered under widget
|
||||||
|
x: {
|
||||||
|
var ax = GlobalStates.popups.anchorX
|
||||||
|
var cx = ax > 0 ? ax - width / 2 : parent.width / 2 - width / 2
|
||||||
|
return Math.max(4, Math.min(parent.width - width - 4, cx))
|
||||||
|
}
|
||||||
|
y: -340
|
||||||
|
opacity: 0.0
|
||||||
|
|
||||||
|
width: 324
|
||||||
|
height: 500
|
||||||
|
|
||||||
|
color: Theme.popupBackground
|
||||||
|
radius: Theme.radius * 2
|
||||||
|
clip: true
|
||||||
|
border.color: Theme.popupBorderColor
|
||||||
|
border.width: Theme.popupBorderWidth
|
||||||
|
|
||||||
|
MouseArea { anchors.fill: parent }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: wallpaperModel.count === 0
|
||||||
|
text: "Scanning..."
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
opacity: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
GridView {
|
||||||
|
id: grid
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: 8
|
||||||
|
topMargin: 8
|
||||||
|
rightMargin: 8
|
||||||
|
bottomMargin: 8
|
||||||
|
}
|
||||||
|
clip: true
|
||||||
|
cellWidth: Math.floor((width - 12) / 2)
|
||||||
|
cellHeight: 104
|
||||||
|
focus: true
|
||||||
|
keyNavigationEnabled: true
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
var item = wallpaperModel.get(grid.currentIndex)
|
||||||
|
if (item) {
|
||||||
|
setProc.command = ["waypaper", "--wallpaper", item.path]
|
||||||
|
setProc.running = true
|
||||||
|
GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Keys.onEscapePressed: GlobalStates.popups.close()
|
||||||
|
|
||||||
|
model: wallpaperModel
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
policy: ScrollBar.AsNeeded
|
||||||
|
width: 12
|
||||||
|
anchors {
|
||||||
|
rightMargin: 0
|
||||||
|
topMargin: 4
|
||||||
|
bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
implicitWidth: 12
|
||||||
|
radius: 6
|
||||||
|
color: Theme.accent ?? Theme.pillText
|
||||||
|
opacity: 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
implicitWidth: 6
|
||||||
|
radius: 3
|
||||||
|
color: Theme.accent ?? Theme.pillText
|
||||||
|
opacity: parent.pressed ? 1.0 : 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight: Rectangle {
|
||||||
|
z: 2
|
||||||
|
radius: Theme.radius + 1
|
||||||
|
color: "transparent"
|
||||||
|
border.color: Theme.accent ?? "#cba6f7"
|
||||||
|
border.width: 2
|
||||||
|
}
|
||||||
|
highlightFollowsCurrentItem: true
|
||||||
|
highlightMoveDuration: 80
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
required property string path
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: grid.cellWidth - 4
|
||||||
|
height: 100
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors { fill: parent; margins: 2 }
|
||||||
|
radius: Theme.radius
|
||||||
|
color: "black"
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "file://" + path
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
sourceSize: Qt.size(152, 96)
|
||||||
|
smooth: true
|
||||||
|
asynchronous: true
|
||||||
|
opacity: status === Image.Ready ? 1.0 : 0.0
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 150 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Theme.radius
|
||||||
|
color: hover.containsMouse ? Qt.rgba(1, 1, 1, 0.2) : "transparent"
|
||||||
|
Behavior on color { ColorAnimation { duration: 100 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: hover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
grid.currentIndex = index
|
||||||
|
setProc.command = ["waypaper", "--wallpaper", path]
|
||||||
|
setProc.running = true
|
||||||
|
GlobalStates.popups.close()
|
||||||
|
}
|
||||||
|
onEntered: grid.currentIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// WallpaperWidget.qml - pill that opens the wallpaper changer popup
|
||||||
|
import QtQuick
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
onClicked: GlobalStates.popups.toggle("wallpaper", mapToItem(null, width / 2, 0).x)
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: " "
|
||||||
|
font { family: Theme.fontMono; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ Pill {
|
|||||||
Text {
|
Text {
|
||||||
text: weatherText
|
text: weatherText
|
||||||
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
color: Theme.foreground
|
color: Theme.pillText
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Fetch via curl ────────────────────────────────────────
|
// ── Fetch via curl ────────────────────────────────────────
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// modules/WindowTitleWidget.qml - hyprland/window equivalent
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Pill {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
NumberAnimation { duration: 250; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string displayTitle: {
|
||||||
|
let rawTitle = Hyprland.activeToplevel ? Hyprland.activeToplevel.title : "";
|
||||||
|
if (rawTitle.length > 45) {
|
||||||
|
return rawTitle.substring(0, Theme.titleLength) + "...";
|
||||||
|
}
|
||||||
|
return rawTitle;
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
text: root.displayTitle
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSize }
|
||||||
|
color: Theme.pillText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// WorkspacesWidget.qml - shows workspaces and allows switching between them
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import ".."
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
required property var screen
|
||||||
|
|
||||||
|
property var sortedWorkspaces: Array.from(Hyprland.workspaces.values).sort((a, b) => a.id - b.id)
|
||||||
|
property int activeIndex: {
|
||||||
|
var fid = Hyprland.focusedWorkspace?.id ?? -1
|
||||||
|
for (var i = 0; i < sortedWorkspaces.length; i++)
|
||||||
|
if (sortedWorkspaces[i].id === fid) return i
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active workspace for this monitor
|
||||||
|
property var monitorActiveWorkspace: {
|
||||||
|
var activeWs = Hyprland.focusedWorkspace
|
||||||
|
// Check if focused workspace is on this monitor
|
||||||
|
if (activeWs && activeWs.monitor?.id === root.screen.id) {
|
||||||
|
return activeWs
|
||||||
|
}
|
||||||
|
// Otherwise find the first workspace on this monitor
|
||||||
|
for (var i = 0; i < sortedWorkspaces.length; i++) {
|
||||||
|
if (sortedWorkspaces[i].monitor?.id === root.screen.id) {
|
||||||
|
return sortedWorkspaces[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWorkspaceOnThisMonitor(workspace) {
|
||||||
|
// Find the monitor in Hyprland.monitors that matches this screen's name
|
||||||
|
for (const monitor of Hyprland.monitors.values) {
|
||||||
|
if (monitor.name === root.screen.name && monitor.id === workspace.monitor.id) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int btnWidth: 32
|
||||||
|
|
||||||
|
color: Theme.pill
|
||||||
|
radius: Theme.radius
|
||||||
|
clip: true
|
||||||
|
implicitWidth: wsRow.implicitWidth
|
||||||
|
implicitHeight: Theme.barHeight
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
NumberAnimation { duration: Theme.workspaceSlideTime; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sliding active indicator - single Rectangle that travels between buttons
|
||||||
|
Rectangle {
|
||||||
|
visible: root.activeIndex >= 0
|
||||||
|
x: root.activeIndex * root.btnWidth
|
||||||
|
width: root.btnWidth
|
||||||
|
height: parent.height
|
||||||
|
color: Theme.wsActive
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
NumberAnimation { duration: Theme.workspaceSlideTime; easing.type: Easing.InOutCubic }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: wsRow
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.sortedWorkspaces
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillHeight: true
|
||||||
|
implicitWidth: root.btnWidth
|
||||||
|
color: wsBtn.containsMouse ? Theme.pillHover : "transparent"
|
||||||
|
// Reduce opacity for workspaces not on this monitor
|
||||||
|
opacity: root.isWorkspaceOnThisMonitor(modelData) ? 1.0 : 0.4
|
||||||
|
Behavior on opacity { NumberAnimation { duration: 150 } }
|
||||||
|
Behavior on color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.id
|
||||||
|
font { family: Theme.fontSans; pixelSize: Theme.fontSizeSecondary }
|
||||||
|
color: Theme.pillText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wsBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: Hyprland.dispatch("hl.dsp.focus({ workspace = '" + modelData.id + "' })")
|
||||||
|
onWheel: (w) => Hyprland.dispatch(
|
||||||
|
"hl.dsp.focus({ workspace = \"" + (w.angleDelta.y > 0 ? "e+1" : "e-1") + "\" })"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Overview module - credits
|
||||||
|
|
||||||
|
The files in this directory (`Overview.qml`, `OverviewWidget.qml`, `OverviewWindow.qml`) and
|
||||||
|
their support singletons (`../../common/Appearance.qml`, `../../common/Config.qml`,
|
||||||
|
`../../common/functions/ColorUtils.qml`, `../../common/widgets/Styled*.qml`,
|
||||||
|
`../../services/HyprlandData.qml`) are copied from
|
||||||
|
[Shanu-Kumawat/quickshell-overview](https://github.com/Shanu-Kumawat/quickshell-overview),
|
||||||
|
which was itself extracted from the overview feature in
|
||||||
|
[end-4/dots-hyprland](https://github.com/end-4/dots-hyprland) ("illogical-impulse") by
|
||||||
|
[end-4](https://github.com/end-4).
|
||||||
|
|
||||||
|
Released under the GPL by the upstream project.
|
||||||
|
|
||||||
|
### Local deviations from upstream
|
||||||
|
|
||||||
|
- `Overview.qml` / `OverviewWidget.qml` / `OverviewWindow.qml`: added one `import "../.."` line
|
||||||
|
each, so `GlobalStates.overviewOpen` (merged into `own/GlobalStates.qml`, see below) resolves
|
||||||
|
from this subdirectory. No other logic changed.
|
||||||
|
- `../../common/Appearance.qml`: `defaultColors` was retuned to pull from this shell's own
|
||||||
|
`Theme`/`Colors` wallust singletons instead of the stock M3 purple palette, so the overview
|
||||||
|
matches the bar's colors (needed one added `import ".."`). This is the module's own documented
|
||||||
|
way to customize its palette (see upstream README, "Theme & Colors").
|
||||||
|
- `../../common/Config.qml`: untouched. Transparency/rounding are instead tuned via
|
||||||
|
`~/.config/quickshell/overview/config.json` (the module's own documented user-override file),
|
||||||
|
kept separate from the upstream defaults it reads.
|
||||||
|
|
||||||
|
## Why a copy instead of a symlink/submodule
|
||||||
|
|
||||||
|
The module was merged into this shell's own process (`own/shell.qml`) so it runs in the same
|
||||||
|
`quickshell -c own` instance as the bar, rather than as a second `qs -c overview` process.
|
||||||
|
|
||||||
|
An untouched clone of the upstream repo is kept side by side at
|
||||||
|
`~/.config/quickshell/overview` - use it as the reference copy when checking for upstream
|
||||||
|
updates (`git -C ~/.config/quickshell/overview pull`) and re-sync the files above by hand if
|
||||||
|
anything changes there.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Runtime options (grid size, scale, effects, etc.) are unaffected by the merge - `Config.qml`
|
||||||
|
still reads user overrides from `$XDG_CONFIG_HOME/quickshell/overview/config.json` regardless
|
||||||
|
of where the QML itself lives. See `~/.config/quickshell/overview/config.example.json` and
|
||||||
|
`~/.config/quickshell/overview/README.md` (in the untouched upstream clone) for the full list
|
||||||
|
of options.
|
||||||
|
|
||||||
|
## Toggling it
|
||||||
|
|
||||||
|
The merged module still registers the same IPC target, just under the `own` shell instance:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
qs ipc -c own call overview toggle
|
||||||
|
qs ipc -c own call overview open
|
||||||
|
qs ipc -c own call overview close
|
||||||
|
```
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import "../../common"
|
||||||
|
import "../../services"
|
||||||
|
import "../.."
|
||||||
|
import "."
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: overviewScope
|
||||||
|
Variants {
|
||||||
|
id: overviewVariants
|
||||||
|
model: Quickshell.screens
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
required property var modelData
|
||||||
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||||
|
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||||
|
property bool blurEnabled: Config.options.overview.effects.enableBlur
|
||||||
|
property bool backdropEnabled: Config.options.overview.effects.enableBackdrop
|
||||||
|
property real backdropOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.backdropOpacity))
|
||||||
|
property bool closeOnFocusLoss: Config.options.overview.closeOnFocusLoss ?? true
|
||||||
|
screen: modelData
|
||||||
|
visible: GlobalStates.overviewOpen
|
||||||
|
|
||||||
|
WlrLayershell.namespace: blurEnabled ? "quickshell:overview-blur" : "quickshell:overview"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
windows: [root]
|
||||||
|
property bool canBeActive: root.monitorIsFocused
|
||||||
|
active: false
|
||||||
|
onCleared: () => {
|
||||||
|
// Only the monitor that owns the grab may close the overview
|
||||||
|
if (root.closeOnFocusLoss && !active && canBeActive)
|
||||||
|
GlobalStates.overviewOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: GlobalStates
|
||||||
|
function onOverviewOpenChanged() {
|
||||||
|
if (GlobalStates.overviewOpen) {
|
||||||
|
delayedGrabTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-evaluate grab ownership when focused monitor changes
|
||||||
|
Connections {
|
||||||
|
target: Hyprland
|
||||||
|
function onFocusedMonitorChanged() {
|
||||||
|
if (!GlobalStates.overviewOpen)
|
||||||
|
return;
|
||||||
|
// Transfer the grab to the newly focused monitor
|
||||||
|
if (root.monitorIsFocused && !grab.active) {
|
||||||
|
grab.active = true;
|
||||||
|
} else if (!root.monitorIsFocused && grab.active) {
|
||||||
|
grab.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: delayedGrabTimer
|
||||||
|
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||||
|
repeat: false
|
||||||
|
onTriggered: {
|
||||||
|
if (!grab.canBeActive)
|
||||||
|
return;
|
||||||
|
grab.active = GlobalStates.overviewOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the layershell surface full-screen so backdrop/blur are not constrained by content size.
|
||||||
|
implicitWidth: screen.width
|
||||||
|
implicitHeight: screen.height
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: keyHandler
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: GlobalStates.overviewOpen
|
||||||
|
focus: GlobalStates.overviewOpen
|
||||||
|
z: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: backdropLayer
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: root.backdropEnabled
|
||||||
|
color: "#000000"
|
||||||
|
opacity: root.backdropOpacity
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: outsideClickCatcher
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
|
enabled: root.closeOnFocusLoss && GlobalStates.overviewOpen
|
||||||
|
z: 0
|
||||||
|
onPressed: mouse => {
|
||||||
|
GlobalStates.overviewOpen = false;
|
||||||
|
mouse.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: event => {
|
||||||
|
// close: Escape or Enter
|
||||||
|
if (event.key === Qt.Key_Escape || event.key === Qt.Key_Return) {
|
||||||
|
GlobalStates.overviewOpen = false;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: compute current group bounds
|
||||||
|
const workspacesPerGroup = Config.options.overview.rows * Config.options.overview.columns;
|
||||||
|
const currentId = Hyprland.focusedMonitor?.activeWorkspace?.id ?? 1;
|
||||||
|
const useWorkspaceMap = Config.options.overview.useWorkspaceMap;
|
||||||
|
const workspaceMap = Config.options.overview.workspaceMap ?? [];
|
||||||
|
const focusedMonitorId = Hyprland.focusedMonitor?.id ?? root.monitor?.id ?? 0;
|
||||||
|
const workspaceOffset = useWorkspaceMap ? Number(workspaceMap[focusedMonitorId] ?? 0) : 0;
|
||||||
|
const currentGroup = Math.floor((currentId - workspaceOffset - 1) / workspacesPerGroup);
|
||||||
|
const minWorkspaceId = currentGroup * workspacesPerGroup + 1 + workspaceOffset;
|
||||||
|
const maxWorkspaceId = minWorkspaceId + workspacesPerGroup - 1;
|
||||||
|
|
||||||
|
const rows = Config.options.overview.rows;
|
||||||
|
const columns = Config.options.overview.columns;
|
||||||
|
const reverseColumns = Config.options.overview.orderRightLeft;
|
||||||
|
const reverseRows = Config.options.overview.orderBottomUp;
|
||||||
|
|
||||||
|
const clampedIndex = Math.max(0, Math.min(workspacesPerGroup - 1, currentId - minWorkspaceId));
|
||||||
|
const currentNormalRow = Math.floor(clampedIndex / columns);
|
||||||
|
const currentNormalColumn = clampedIndex % columns;
|
||||||
|
|
||||||
|
function toVisualRow(normalRow) {
|
||||||
|
return reverseRows ? (rows - normalRow - 1) : normalRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVisualColumn(normalColumn) {
|
||||||
|
return reverseColumns ? (columns - normalColumn - 1) : normalColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNormalRow(visualRow) {
|
||||||
|
return reverseRows ? (rows - visualRow - 1) : visualRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNormalColumn(visualColumn) {
|
||||||
|
return reverseColumns ? (columns - visualColumn - 1) : visualColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetVisualRow = toVisualRow(currentNormalRow);
|
||||||
|
let targetVisualColumn = toVisualColumn(currentNormalColumn);
|
||||||
|
|
||||||
|
let targetId = null;
|
||||||
|
|
||||||
|
// Arrow keys and vim-style hjkl
|
||||||
|
if (event.key === Qt.Key_Left || event.key === Qt.Key_H) {
|
||||||
|
targetVisualColumn = (targetVisualColumn - 1 + columns) % columns;
|
||||||
|
} else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) {
|
||||||
|
targetVisualColumn = (targetVisualColumn + 1) % columns;
|
||||||
|
} else if (event.key === Qt.Key_Up || event.key === Qt.Key_K) {
|
||||||
|
targetVisualRow = (targetVisualRow - 1 + rows) % rows;
|
||||||
|
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_J) {
|
||||||
|
targetVisualRow = (targetVisualRow + 1) % rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number keys: jump to workspace within the current group
|
||||||
|
// 1-9 map to positions 1-9, 0 maps to position 10
|
||||||
|
else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
|
||||||
|
const position = event.key - Qt.Key_0; // 1-9
|
||||||
|
if (position <= workspacesPerGroup) {
|
||||||
|
targetId = minWorkspaceId + position - 1;
|
||||||
|
}
|
||||||
|
} else if (event.key === Qt.Key_0) {
|
||||||
|
// 0 = 10th workspace in the group (if group has 10+ workspaces)
|
||||||
|
if (workspacesPerGroup >= 10) {
|
||||||
|
targetId = minWorkspaceId + 9; // 10th position = offset 9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetId === null && (
|
||||||
|
event.key === Qt.Key_Left || event.key === Qt.Key_H ||
|
||||||
|
event.key === Qt.Key_Right || event.key === Qt.Key_L ||
|
||||||
|
event.key === Qt.Key_Up || event.key === Qt.Key_K ||
|
||||||
|
event.key === Qt.Key_Down || event.key === Qt.Key_J
|
||||||
|
)) {
|
||||||
|
const targetNormalRow = toNormalRow(targetVisualRow);
|
||||||
|
const targetNormalColumn = toNormalColumn(targetVisualColumn);
|
||||||
|
targetId = minWorkspaceId + targetNormalRow * columns + targetNormalColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetId !== null) {
|
||||||
|
const clampedTarget = Math.max(minWorkspaceId, Math.min(maxWorkspaceId, targetId));
|
||||||
|
if (Hyprland.usingLua) {
|
||||||
|
Hyprland.dispatch(`hl.dsp.focus({workspace = '${clampedTarget}'})`);
|
||||||
|
} else {
|
||||||
|
Hyprland.dispatch("workspace " + clampedTarget);
|
||||||
|
}
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
visible: GlobalStates.overviewOpen
|
||||||
|
z: 1
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: Config.options.position.topMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: overviewLoader
|
||||||
|
active: Config?.options.overview.enable ?? true
|
||||||
|
sourceComponent: OverviewWidget {
|
||||||
|
panelWindow: root
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "overview"
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
GlobalStates.overviewOpen = false;
|
||||||
|
}
|
||||||
|
function open() {
|
||||||
|
GlobalStates.overviewOpen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,265 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import "../../common"
|
||||||
|
import "../../common/functions"
|
||||||
|
import "../../services"
|
||||||
|
import "../.."
|
||||||
|
|
||||||
|
Item { // Window
|
||||||
|
id: root
|
||||||
|
property var toplevel
|
||||||
|
property var windowData
|
||||||
|
property var monitorData
|
||||||
|
property var widgetMonitorData
|
||||||
|
property var scale
|
||||||
|
property var availableWorkspaceWidth
|
||||||
|
property var availableWorkspaceHeight
|
||||||
|
property real positionBaseX: (monitorData?.x ?? 0) + (monitorData?.reserved?.[0] ?? 0)
|
||||||
|
property real positionBaseY: (monitorData?.y ?? 0) + (monitorData?.reserved?.[1] ?? 0)
|
||||||
|
property int recaptureToken: 0
|
||||||
|
property bool restrictToWorkspace: true
|
||||||
|
property real widthRatio: {
|
||||||
|
if (!widgetMonitorData || !monitorData)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
const widgetWidth = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.height ?? 1) : (widgetMonitorData.width ?? 1);
|
||||||
|
const sourceWidth = (monitorData.transform % 2 === 1) ? (monitorData.height ?? 1) : (monitorData.width ?? 1);
|
||||||
|
const sourceScale = monitorData.scale ?? 1;
|
||||||
|
const widgetScale = widgetMonitorData.scale ?? 1;
|
||||||
|
return (widgetWidth * sourceScale) / (sourceWidth * widgetScale);
|
||||||
|
}
|
||||||
|
property real heightRatio: {
|
||||||
|
if (!widgetMonitorData || !monitorData)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
const widgetHeight = (widgetMonitorData.transform % 2 === 1) ? (widgetMonitorData.width ?? 1) : (widgetMonitorData.height ?? 1);
|
||||||
|
const sourceHeight = (monitorData.transform % 2 === 1) ? (monitorData.width ?? 1) : (monitorData.height ?? 1);
|
||||||
|
const sourceScale = monitorData.scale ?? 1;
|
||||||
|
const widgetScale = widgetMonitorData.scale ?? 1;
|
||||||
|
return (widgetHeight * sourceScale) / (sourceHeight * widgetScale);
|
||||||
|
}
|
||||||
|
property real initX: Math.max(((windowData?.at[0] ?? 0) - positionBaseX) * root.scale * geometryScaleX, 0) + xOffset
|
||||||
|
property real initY: Math.max(((windowData?.at[1] ?? 0) - positionBaseY) * root.scale * geometryScaleY, 0) + yOffset
|
||||||
|
property real xOffset: 0
|
||||||
|
property real yOffset: 0
|
||||||
|
property int widgetMonitorId: 0
|
||||||
|
property real geometryScaleX: widthRatio
|
||||||
|
property real geometryScaleY: heightRatio
|
||||||
|
|
||||||
|
property var targetWindowWidth: (windowData?.size[0] ?? 100) * scale * geometryScaleX
|
||||||
|
property var targetWindowHeight: (windowData?.size[1] ?? 100) * scale * geometryScaleY
|
||||||
|
property bool hovered: false
|
||||||
|
property bool pressed: false
|
||||||
|
|
||||||
|
property bool showIcons: Config.options.windowPreview.showIcons
|
||||||
|
property var iconToWindowRatio: Config.options.windowPreview.iconToWindowRatio
|
||||||
|
property var xwaylandIndicatorToIconRatio: Config.options.windowPreview.xwaylandIndicatorToIconRatio
|
||||||
|
property var iconToWindowRatioCompact: Config.options.windowPreview.iconToWindowRatioCompact
|
||||||
|
property bool cropToFill: Config.options.windowPreview.cropToFill
|
||||||
|
property bool previewsEnabled: Config.options.overview.previewsEnabled
|
||||||
|
property bool includeInactiveMonitorPreviews: Config.options.overview.includeInactiveMonitorPreviews
|
||||||
|
property int previewRecaptureDelayMs: Config.options.overview.previewRecaptureDelayMs
|
||||||
|
property real windowOverlayOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.windowOverlayOpacity))
|
||||||
|
property bool glassMode: Config.options.overview.effects.glassMode
|
||||||
|
property real glassShineOpacity: Math.max(0, Math.min(1, Config.options.overview.effects.glassShineOpacity))
|
||||||
|
property real effectiveWindowOverlayOpacity: glassMode ? Math.min(windowOverlayOpacity, 0.10) : windowOverlayOpacity
|
||||||
|
property string previewModeRaw: Config.options.overview.previewMode
|
||||||
|
property string previewMode: {
|
||||||
|
const mode = `${previewModeRaw ?? "live"}`.trim().toLowerCase();
|
||||||
|
return (mode === "event" || mode === "snapshot") ? "event" : "live";
|
||||||
|
}
|
||||||
|
property bool livePreviewEnabled: previewsEnabled && previewMode === "live"
|
||||||
|
property bool shouldCapturePreview: {
|
||||||
|
if (!GlobalStates.overviewOpen || !previewsEnabled || !previewCaptureEnabled)
|
||||||
|
return false;
|
||||||
|
if (includeInactiveMonitorPreviews)
|
||||||
|
return true;
|
||||||
|
return (windowData?.monitor ?? -1) === widgetMonitorId;
|
||||||
|
}
|
||||||
|
property var entry: {
|
||||||
|
DesktopEntries.applications.values; // re-run when the entry index updates
|
||||||
|
return DesktopEntries.heuristicLookup(windowData?.class);
|
||||||
|
}
|
||||||
|
property string iconName: {
|
||||||
|
const raw = `${entry?.icon ?? ""}`.trim();
|
||||||
|
const withoutProviderPrefix = raw.replace(/^image:\/\/icon\//, "");
|
||||||
|
const withoutQuery = withoutProviderPrefix.split("?")[0].trim();
|
||||||
|
return withoutQuery.length > 0 ? withoutQuery : "application-x-executable";
|
||||||
|
}
|
||||||
|
property var iconPath: Quickshell.iconPath(iconName, "image-missing")
|
||||||
|
property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth
|
||||||
|
|
||||||
|
property bool indicateXWayland: windowData?.xwayland ?? false
|
||||||
|
property bool previewCaptureEnabled: true
|
||||||
|
property bool initialized: false
|
||||||
|
property bool dragInProgress: false
|
||||||
|
property bool suspendPositionAnimation: false
|
||||||
|
property bool animateSize: true
|
||||||
|
|
||||||
|
x: initX
|
||||||
|
y: initY
|
||||||
|
width: Math.min(targetWindowWidth, availableWorkspaceWidth)
|
||||||
|
height: Math.min(targetWindowHeight, availableWorkspaceHeight)
|
||||||
|
opacity: (windowData?.monitor ?? -1) == widgetMonitorId ? 1 : Config.options.windowPreview.inactiveMonitorOpacity
|
||||||
|
visible: {
|
||||||
|
const thisWsId = windowData?.workspace?.id;
|
||||||
|
const isFullscreen = (windowData?.fullscreen ?? 0) > 0;
|
||||||
|
if (isFullscreen || thisWsId === undefined) return true;
|
||||||
|
return !HyprlandData.windowList.some(w => w.workspace?.id === thisWsId && (w.fullscreen ?? 0) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
Component.onCompleted: Qt.callLater(() => root.initialized = true)
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on y {
|
||||||
|
enabled: root.initialized && !root.dragInProgress && !root.suspendPositionAnimation
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on width {
|
||||||
|
enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on height {
|
||||||
|
enabled: root.initialized && root.animateSize && !root.dragInProgress && !root.suspendPositionAnimation
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opaque background for windows on the active monitor.
|
||||||
|
// The simplest solution for making those windows fully opaque and not interacting with actual
|
||||||
|
// windows behind the overview, e.g., applying blur to them.
|
||||||
|
Rectangle {
|
||||||
|
visible: (root.windowData?.monitor ?? -1) === root.widgetMonitorId
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Appearance.rounding.windowRounding * root.scale
|
||||||
|
color: root.glassMode
|
||||||
|
? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38)
|
||||||
|
: Appearance.colors.colLayer2
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreencopyView {
|
||||||
|
id: windowPreview
|
||||||
|
readonly property real srcAspect: {
|
||||||
|
const w = root.windowData?.size?.[0] ?? 0;
|
||||||
|
const h = root.windowData?.size?.[1] ?? 0;
|
||||||
|
return (w > 0 && h > 0) ? (w / h) : 1;
|
||||||
|
}
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: root.cropToFill
|
||||||
|
? Math.max(parent.width, parent.height * srcAspect)
|
||||||
|
: Math.min(parent.width, parent.height * srcAspect)
|
||||||
|
height: root.cropToFill
|
||||||
|
? Math.max(parent.height, parent.width / srcAspect)
|
||||||
|
: Math.min(parent.height, parent.width / srcAspect)
|
||||||
|
captureSource: shouldCapturePreview ? root.toplevel : null
|
||||||
|
live: livePreviewEnabled
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
maskEnabled: true
|
||||||
|
maskSource: previewMask
|
||||||
|
maskThresholdMin: 0.5
|
||||||
|
maskSpreadAtMin: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Appearance.rounding.windowRounding * root.scale
|
||||||
|
color: pressed ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Active, Math.min(1, root.effectiveWindowOverlayOpacity + 0.30)) :
|
||||||
|
hovered ? ColorUtils.applyAlpha(Appearance.colors.colLayer2Hover, Math.min(1, root.effectiveWindowOverlayOpacity + 0.20)) :
|
||||||
|
ColorUtils.applyAlpha(
|
||||||
|
root.glassMode ? ColorUtils.mix(Appearance.colors.colLayer2, Appearance.colors.colLayer0, 0.38) : Appearance.colors.colLayer2,
|
||||||
|
root.effectiveWindowOverlayOpacity
|
||||||
|
)
|
||||||
|
border.color: root.glassMode
|
||||||
|
? ColorUtils.applyAlpha(Appearance.m3colors.m3outline, 0.62)
|
||||||
|
: ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: root.glassMode
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: parent.radius
|
||||||
|
color: "transparent"
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0.0; color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.24) }
|
||||||
|
GradientStop { position: 0.5; color: ColorUtils.applyAlpha("#FFFFFF", 0.0) }
|
||||||
|
GradientStop { position: 1.0; color: ColorUtils.applyAlpha("#000000", root.glassShineOpacity * 0.14) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: root.glassMode
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1
|
||||||
|
radius: Math.max(parent.radius - 1, 0)
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 1
|
||||||
|
border.color: ColorUtils.applyAlpha("#FFFFFF", root.glassShineOpacity * 0.32)
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: windowIcon
|
||||||
|
visible: root.showIcons
|
||||||
|
property var iconSize: {
|
||||||
|
const renderedSize = Math.min(root.width, root.height);
|
||||||
|
return renderedSize * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) / (root.monitorData?.scale ?? 1);
|
||||||
|
}
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
source: root.iconPath
|
||||||
|
width: iconSize
|
||||||
|
height: iconSize
|
||||||
|
sourceSize: Qt.size(Math.max(1, Math.round(iconSize)), Math.max(1, Math.round(iconSize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: previewMask
|
||||||
|
width: windowPreview.width
|
||||||
|
height: windowPreview.height
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: false
|
||||||
|
layer.enabled: true
|
||||||
|
layer.smooth: true
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: root.width
|
||||||
|
height: root.height
|
||||||
|
radius: Appearance.rounding.windowRounding * root.scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshCapture() {
|
||||||
|
if (!GlobalStates.overviewOpen || livePreviewEnabled || !previewsEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
root.previewCaptureEnabled = false;
|
||||||
|
previewResetTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: previewResetTimer
|
||||||
|
interval: Math.max(1, previewRecaptureDelayMs)
|
||||||
|
repeat: false
|
||||||
|
onTriggered: root.previewCaptureEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecaptureTokenChanged: {
|
||||||
|
if (recaptureToken > 0)
|
||||||
|
root.refreshCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Overview 1.0 Overview.qml
|
||||||
|
OverviewWidget 1.0 OverviewWidget.qml
|
||||||
|
OverviewWindow 1.0 OverviewWindow.qml
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// shell.qml - entry point
|
||||||
|
//@ pragma UseQApplication
|
||||||
|
import Quickshell
|
||||||
|
import "./modules"
|
||||||
|
import "./modules/overview"
|
||||||
|
|
||||||
|
ShellRoot {
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
Bar {
|
||||||
|
required property var modelData
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
PowerMenuPopup {
|
||||||
|
required property var modelData
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
model: Quickshell.screens
|
||||||
|
MediaPopup {
|
||||||
|
required property var modelData
|
||||||
|
screen: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallpaperPopup {
|
||||||
|
screen: Quickshell.screens[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace overview (SUPER+TAB-style popup) — merged from ./overview,
|
||||||
|
// see modules/overview/CREDITS.md for attribution.
|
||||||
|
Overview {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// shell.qml - entry point
|
|
||||||
// QuickShell scans this directory and auto-imports neighbours (Bar, Theme, Exec, ...).
|
|
||||||
// Do NOT create a qmldir file - QuickShell synthesises one automatically.
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
ShellRoot {
|
|
||||||
Variants {
|
|
||||||
model: Quickshell.screens
|
|
||||||
Bar {
|
|
||||||
required property var modelData
|
|
||||||
screen: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya (adi1090x)
|
||||||
|
* Github : @adi1090x
|
||||||
|
*
|
||||||
|
* Configuration For Rofi Version: 1.7.3
|
||||||
|
**/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
/*---------- General setting ----------*/
|
||||||
|
modi: "drun,run,filebrowser,window";
|
||||||
|
case-sensitive: false;
|
||||||
|
cycle: true;
|
||||||
|
filter: "";
|
||||||
|
scroll-method: 0;
|
||||||
|
normalize-match: true;
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
/* cache-dir: ;*/
|
||||||
|
steal-focus: false;
|
||||||
|
/* dpi: -1;*/
|
||||||
|
|
||||||
|
/*---------- Matching setting ----------*/
|
||||||
|
matching: "normal";
|
||||||
|
tokenize: true;
|
||||||
|
|
||||||
|
/*---------- SSH settings ----------*/
|
||||||
|
ssh-client: "ssh";
|
||||||
|
ssh-command: "{terminal} -e {ssh-client} {host} [-p {port}]";
|
||||||
|
parse-hosts: true;
|
||||||
|
parse-known-hosts: true;
|
||||||
|
|
||||||
|
/*---------- Drun settings ----------*/
|
||||||
|
drun-categories: "";
|
||||||
|
drun-match-fields: "name,generic,exec,categories,keywords";
|
||||||
|
drun-display-format: "{name} [<span weight='light' size='small'><i>({generic})</i></span>]";
|
||||||
|
drun-show-actions: false;
|
||||||
|
drun-url-launcher: "xdg-open";
|
||||||
|
drun-use-desktop-cache: false;
|
||||||
|
drun-reload-desktop-cache: false;
|
||||||
|
drun {
|
||||||
|
/** Parse user desktop files. */
|
||||||
|
parse-user: true;
|
||||||
|
/** Parse system desktop files. */
|
||||||
|
parse-system: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------- Run settings ----------*/
|
||||||
|
run-command: "{cmd}";
|
||||||
|
run-list-command: "";
|
||||||
|
run-shell-command: "{terminal} -e {cmd}";
|
||||||
|
|
||||||
|
/*---------- Fallback Icon ----------*/
|
||||||
|
run,drun {
|
||||||
|
fallback-icon: "application-x-addon";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------- Window switcher settings ----------*/
|
||||||
|
window-match-fields: "title,class,role,name,desktop";
|
||||||
|
window-command: "wmctrl -i -R {window}";
|
||||||
|
window-format: "{w} - {c} - {t:0}";
|
||||||
|
window-thumbnail: false;
|
||||||
|
|
||||||
|
/*---------- Combi settings ----------*/
|
||||||
|
/* combi-modi: "window,run";*/
|
||||||
|
/* combi-hide-mode-prefix: false;*/
|
||||||
|
/* combi-display-format: "{mode} {text}";*/
|
||||||
|
|
||||||
|
/*---------- History and Sorting ----------*/
|
||||||
|
disable-history: false;
|
||||||
|
sorting-method: "normal";
|
||||||
|
max-history-size: 25;
|
||||||
|
|
||||||
|
/*---------- Display setting ----------*/
|
||||||
|
display-window: "Windows";
|
||||||
|
display-windowcd: "Window CD";
|
||||||
|
display-run: "Run";
|
||||||
|
display-ssh: "SSH";
|
||||||
|
display-drun: "Apps";
|
||||||
|
display-combi: "Combi";
|
||||||
|
display-keys: "Keys";
|
||||||
|
display-filebrowser: "Files";
|
||||||
|
|
||||||
|
/*---------- Misc setting ----------*/
|
||||||
|
terminal: "rofi-sensible-terminal";
|
||||||
|
font: "Mono 12";
|
||||||
|
sort: false;
|
||||||
|
threads: 0;
|
||||||
|
click-to-exit: true;
|
||||||
|
/* ignored-prefixes: "";*/
|
||||||
|
/* pid: "/run/user/1000/rofi.pid";*/
|
||||||
|
|
||||||
|
/*---------- File browser settings ----------*/
|
||||||
|
filebrowser {
|
||||||
|
/* directory: "/home";*/
|
||||||
|
directories-first: true;
|
||||||
|
sorting-method: "name";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------- Other settings ----------*/
|
||||||
|
timeout {
|
||||||
|
action: "kb-cancel";
|
||||||
|
delay: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------- Keybindings ----------*/
|
||||||
|
/*
|
||||||
|
kb-primary-paste: "Control+V,Shift+Insert";
|
||||||
|
kb-secondary-paste: "Control+v,Insert";
|
||||||
|
kb-clear-line: "Control+w";
|
||||||
|
kb-move-front: "Control+a";
|
||||||
|
kb-move-end: "Control+e";
|
||||||
|
kb-move-word-back: "Alt+b,Control+Left";
|
||||||
|
kb-move-word-forward: "Alt+f,Control+Right";
|
||||||
|
kb-move-char-back: "Left,Control+b";
|
||||||
|
kb-move-char-forward: "Right,Control+f";
|
||||||
|
kb-remove-word-back: "Control+Alt+h,Control+BackSpace";
|
||||||
|
kb-remove-word-forward: "Control+Alt+d";
|
||||||
|
kb-remove-char-forward: "Delete,Control+d";
|
||||||
|
kb-remove-char-back: "BackSpace,Shift+BackSpace,Control+h";
|
||||||
|
kb-remove-to-eol: "Control+k";
|
||||||
|
kb-remove-to-sol: "Control+u";
|
||||||
|
kb-accept-entry: "Control+j,Control+m,Return,KP_Enter";
|
||||||
|
kb-accept-custom: "Control+Return";
|
||||||
|
kb-accept-custom-alt: "Control+Shift+Return";
|
||||||
|
kb-accept-alt: "Shift+Return";
|
||||||
|
kb-delete-entry: "Shift+Delete";
|
||||||
|
kb-mode-next: "Shift+Right,Control+Tab";
|
||||||
|
kb-mode-previous: "Shift+Left,Control+ISO_Left_Tab";
|
||||||
|
kb-mode-complete: "Control+l";
|
||||||
|
kb-row-left: "Control+Page_Up";
|
||||||
|
kb-row-right: "Control+Page_Down";
|
||||||
|
kb-row-down: "Down,Control+n";
|
||||||
|
kb-page-prev: "Page_Up";
|
||||||
|
kb-page-next: "Page_Down";
|
||||||
|
kb-row-first: "Home,KP_Home";
|
||||||
|
kb-row-last: "End,KP_End";
|
||||||
|
kb-row-select: "Control+space";
|
||||||
|
kb-screenshot: "Alt+S";
|
||||||
|
kb-ellipsize: "Alt+period";
|
||||||
|
kb-toggle-case-sensitivity: "grave,dead_grave";
|
||||||
|
kb-toggle-sort: "Alt+grave";
|
||||||
|
kb-cancel: "Escape,Control+g,Control+bracketleft";
|
||||||
|
kb-custom-1: "Alt+1";
|
||||||
|
kb-custom-2: "Alt+2";
|
||||||
|
kb-custom-3: "Alt+3";
|
||||||
|
kb-custom-4: "Alt+4";
|
||||||
|
kb-custom-5: "Alt+5";
|
||||||
|
kb-custom-6: "Alt+6";
|
||||||
|
kb-custom-7: "Alt+7";
|
||||||
|
kb-custom-8: "Alt+8";
|
||||||
|
kb-custom-9: "Alt+9";
|
||||||
|
kb-custom-10: "Alt+0";
|
||||||
|
kb-custom-11: "Alt+exclam";
|
||||||
|
kb-custom-12: "Alt+at";
|
||||||
|
kb-custom-13: "Alt+numbersign";
|
||||||
|
kb-custom-14: "Alt+dollar";
|
||||||
|
kb-custom-15: "Alt+percent";
|
||||||
|
kb-custom-16: "Alt+dead_circumflex";
|
||||||
|
kb-custom-17: "Alt+ampersand";
|
||||||
|
kb-custom-18: "Alt+asterisk";
|
||||||
|
kb-custom-19: "Alt+parenleft";
|
||||||
|
kb-select-1: "Super+1";
|
||||||
|
kb-select-2: "Super+2";
|
||||||
|
kb-select-3: "Super+3";
|
||||||
|
kb-select-4: "Super+4";
|
||||||
|
kb-select-5: "Super+5";
|
||||||
|
kb-select-6: "Super+6";
|
||||||
|
kb-select-7: "Super+7";
|
||||||
|
kb-select-8: "Super+8";
|
||||||
|
kb-select-9: "Super+9";
|
||||||
|
kb-select-10: "Super+0";
|
||||||
|
ml-row-left: "ScrollLeft";
|
||||||
|
ml-row-right: "ScrollRight";
|
||||||
|
ml-row-up: "ScrollUp";
|
||||||
|
ml-row-down: "ScrollDown";
|
||||||
|
me-select-entry: "MousePrimary";
|
||||||
|
me-accept-entry: "MouseDPrimary";
|
||||||
|
me-accept-custom: "Control+MouseDPrimary";
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//@theme "/usr/share/rofi/themes/android_notification.rasi"
|
||||||
|
|
||||||
|
//@theme "/usr/share/rofi/themes/Adapta-Nokto.rasi"
|
||||||
|
|
||||||
|
//@theme "/usr/share/rofi/themes/Arc-Dark.rasi"
|
||||||
|
|
||||||
|
@theme "~/.config/rofi/current.rasi"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user