diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 434e8ad31..a24470334 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -33,6 +33,7 @@ bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls +bind = Super, G, global, quickshell:crosshairToggle # Toggle crosshair bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback) @@ -49,6 +50,7 @@ bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidd bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindd = Ctrl+Super, T, Toggle wallpaper selector, global, quickshell:wallpaperSelectorToggle # Wallpaper selector +bindd = Ctrl+Super+Alt, T, Select random wallpaper, global, quickshell:wallpaperSelectorRandom # Random wallpaper bindd = Ctrl+Super, T, Change wallpaper, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # [hidden] Change wallpaper (fallback) bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets diff --git a/.config/hypr/hyprland/rules.conf b/.config/hypr/hyprland/rules.conf index f5a86a238..0589edb85 100644 --- a/.config/hypr/hyprland/rules.conf +++ b/.config/hypr/hyprland/rules.conf @@ -130,32 +130,22 @@ layerrule = blurpopups, quickshell:.* layerrule = blur, quickshell:.* layerrule = ignorealpha 0.79, quickshell:.* layerrule = animation slide, quickshell:bar -layerrule = animation slide, quickshell:verticalBar -layerrule = animation fade, quickshell:screenCorners -layerrule = animation slide right, quickshell:sidebarRight -layerrule = animation slide left, quickshell:sidebarLeft -layerrule = animation slide top, quickshell:wallpaperSelector -layerrule = animation slide bottom, quickshell:osk -layerrule = animation slide bottom, quickshell:dock layerrule = animation slide bottom, quickshell:cheatsheet +layerrule = noanim, quickshell:crosshair +layerrule = animation slide bottom, quickshell:dock +layerrule = animation popin 120%, quickshell:screenCorners +layerrule = noanim, quickshell:lockWindowPusher +layerrule = animation fade, quickshell:notificationPopup +layerrule = noanim, quickshell:overview +layerrule = animation slide bottom, quickshell:osk +layerrule = noanim, quickshell:screenshot layerrule = blur, quickshell:session layerrule = noanim, quickshell:session layerrule = ignorealpha 0, quickshell:session -layerrule = animation fade, quickshell:notificationPopup -layerrule = blur, quickshell:backgroundWidgets -layerrule = ignorealpha 0.05, quickshell:backgroundWidgets -layerrule = noanim, quickshell:screenshot -layerrule = animation popin 120%, quickshell:screenCorners -layerrule = noanim, quickshell:lockWindowPusher - +layerrule = animation slide right, quickshell:sidebarRight +layerrule = animation slide left, quickshell:sidebarLeft +layerrule = animation slide, quickshell:verticalBar +layerrule = animation slide top, quickshell:wallpaperSelector # Launchers need to be FAST -layerrule = noanim, quickshell:overview layerrule = noanim, gtk4-layer-shell -## outfoxxed's stuff -layerrule = blur, shell:bar -layerrule = ignorezero, shell:bar -layerrule = blur, shell:notifications -layerrule = ignorealpha 0.1, shell:notifications - - diff --git a/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh b/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh index 794414554..eac998afa 100755 --- a/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh +++ b/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh @@ -23,13 +23,15 @@ while [[ "$#" -gt 0 ]]; do done # Combine the system prompt with the clipboard content -content=$(wl-paste -p | tr '\n' ' ') -prompt="$SYSTEM_PROMPT $content" +content=$(wl-paste -p | tr '\n' ' ' | head -c 2000) # 2000 char limit to prevent overflow + +# Properly escape content for JSON using jq +prompt_json=$(jq -n --arg system_prompt "$SYSTEM_PROMPT" --arg content "$content" '$system_prompt + " " + $content') # Make the API call with the specified or default model -response=$(curl http://localhost:11434/api/generate -d \ - "{\"model\": \"$model\",\"prompt\": \"$prompt\",\"stream\": false}" \ - | jq -r '.response') +api_payload=$(jq -n --arg model "$model" --argjson prompt "$prompt_json" --argjson stream false \ + '{model: $model, prompt: $prompt, stream: $stream}') +response=$(curl -s http://localhost:11434/api/generate -d "$api_payload" | jq -r '.response' 2>/dev/null) # Check if content is a single line and no longer than 30 characters if [[ ${#content} -le 30 && "$content" != *$'\n'* ]]; then diff --git a/.config/hypr/shaders/chromatic_abberation.frag b/.config/hypr/shaders/chromatic_abberation.frag deleted file mode 100644 index 5389241cd..000000000 --- a/.config/hypr/shaders/chromatic_abberation.frag +++ /dev/null @@ -1,24 +0,0 @@ -// vim: set ft=glsl: - -precision highp float; -varying highp vec2 v_texcoord; -uniform highp sampler2D tex; - -#define STRENGTH 0.0027 - -void main() { - vec2 center = vec2(0.5, 0.5); - vec2 offset = (v_texcoord - center) * STRENGTH; - - float rSquared = dot(offset, offset); - float distortion = 1.0 + 1.0 * rSquared; - vec2 distortedOffset = offset * distortion; - - vec2 redOffset = vec2(distortedOffset.x, distortedOffset.y); - vec2 blueOffset = vec2(distortedOffset.x, distortedOffset.y); - - vec4 redColor = texture2D(tex, v_texcoord + redOffset); - vec4 blueColor = texture2D(tex, v_texcoord + blueOffset); - - gl_FragColor = vec4(redColor.r, texture2D(tex, v_texcoord).g, blueColor.b, 1.0); -} diff --git a/.config/hypr/shaders/crt.frag b/.config/hypr/shaders/crt.frag deleted file mode 100644 index a37ff9fb2..000000000 --- a/.config/hypr/shaders/crt.frag +++ /dev/null @@ -1,511 +0,0 @@ -#version 100 -precision highp float; -varying highp vec2 v_texcoord; -varying highp vec3 v_pos; -uniform highp sampler2D tex; -uniform lowp float time; - -#define BORDER_COLOR vec4(vec3(0.0, 0.0, 0.0), 1.0) // black border -#define BORDER_RADIUS 1.0 // larger vignette radius -#define BORDER_SIZE 0.01 // small border size -#define CHROMATIC_ABERRATION_STRENGTH 0.00 -#define DENOISE_INTENSITY 0.0001 // -#define DISTORTION_AMOUNT 0.00 // moderate distortion amount -#define HDR_BLOOM 0.75 // bloom intensity -#define HDR_BRIGHTNESS 0.011 // brightness -#define HDR_CONTRAST 0.011 // contrast -#define HDR_SATURATION 1.0// saturation -#define LENS_DISTORTION_AMOUNT 0.0 -#define NOISE_THRESHOLD 0.0001 -#define PHOSPHOR_BLUR_AMOUNT 0.77 // Amount of blur for phosphor glow -#define PHOSPHOR_GLOW_AMOUNT 0.77 // Amount of phosphor glow -#define SAMPLING_RADIUS 0.0001 -#define SCANLINE_FREQUENCY 540.0 -#define SCANLINE_THICKNESS 0.0507 -#define SCANLINE_TIME time * 471.24 -#define SHARPNESS 0.25 -#define SUPERSAMPLING_SAMPLES 16.0 -#define VIGNETTE_RADIUS 0.0 // larger vignette radius -#define PI 3.14159265359 -#define TWOPI 6.28318530718 - -vec2 applyBarrelDistortion(vec2 coord, float amt) { - vec2 p = coord.xy / vec2(1.0); - vec2 v = p * 2.0 - vec2(1.0); - float r = dot(v, v); - float k = 1.0 + pow(r, 2.0) * pow(amt, 2.0); - vec2 result = v * k; - return vec2(0.5, 0.5) + 0.5 * result.xy; -} - -vec4 applyColorCorrection(vec4 color) { - color.rgb *= vec3(1.0, 0.79, 0.89); - return vec4(color.rgb, 1.0); -} - -vec4 applyBorder(vec2 tc, vec4 color, float borderSize, vec4 borderColor) { - float dist_x = min(tc.x, 1.0 - tc.x); - float dist_y = min(tc.y, 1.0 - tc.y); - float dist = min(dist_x, dist_y) * -1.0; - float border = smoothstep(borderSize, 0.0, dist); - border += smoothstep(borderSize, 0.0, dist); - return mix(color, borderColor, border); -} - -vec4 applyFakeHDR(vec4 color, float brightness, float contrast, float saturation, float bloom) { - color.rgb = (color.rgb - vec3(0.5)) * exp2(brightness) + vec3(0.5); - vec3 crtfactor = vec3(1.05, 0.92, 1.0); - color.rgb = pow(color.rgb, crtfactor); - // // NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); - - // // BT.709 - // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); - - // BT.2020 - vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); - - // // Warm NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); - - float luminance = dot(color.rgb, lumCoeff); - luminance = pow(luminance, 2.2); - color.rgb = mix(vec3(luminance), color.rgb, saturation); - color.rgb = mix(color.rgb, vec3(1.0), pow(max(0.0, luminance - 1.0 + bloom), 4.0)); - return color; -} - -vec4 applyVignette(vec4 color) { - vec2 center = vec2(0.5, 0.5); // center of screen - float radius = VIGNETTE_RADIUS; // radius of vignette effect - float softness = 1.0; // softness of vignette effect - float intensity = 0.7; // intensity of vignette effect - vec2 offset = v_texcoord - center; // offset from center of screen - float distance = length(offset); // distance from center of screen - float alpha = smoothstep(radius, radius - radius * softness, distance) * intensity; // calculate alpha value for vignette effect - return mix(vec4(0.0, 0.0, 0.0, alpha), color, alpha); // mix black with color using calculated alpha value -} - -vec4 applyPhosphorGlow(vec2 tc, vec4 color, sampler2D tex) { - // Calculate average color value of the texture - vec4 texelColor = color; - float averageColor = (texelColor.r + texelColor.g + texelColor.b) / 3.0; - - // Determine brightness-dependent color factor - float factor = mix( - mix(0.09, - mix(0.005, 0.0075, (averageColor - 0.1) / 0.1), - step(0.01, averageColor)), 0.0005, - step(0.02, averageColor)); - // Apply phosphor glow effect - vec4 sum = vec4(0.0); - vec4 pixels[9]; - pixels[0] = texture2D(tex, tc - vec2(0.001, 0.001)); - pixels[1] = texture2D(tex, tc - vec2(0.001, 0.0)); - pixels[2] = texture2D(tex, tc - vec2(0.001, -0.001)); - pixels[3] = texture2D(tex, tc - vec2(0.0, 0.001)); - pixels[4] = texture2D(tex, tc); - pixels[5] = texture2D(tex, tc + vec2(0.001, 0.001)); - pixels[6] = texture2D(tex, tc + vec2(0.001, 0.0)); - pixels[7] = texture2D(tex, tc + vec2(0.001, -0.001)); - pixels[8] = texture2D(tex, tc + vec2(0.0, 0.001)); - -// Perform operations on input pixels in parallel - sum = pixels[0] - + pixels[1] - + pixels[2] - + pixels[3] - + pixels[4] - + pixels[5] - + pixels[6] - + pixels[7] - + pixels[8]; - sum /= 9.0; - sum += texture2D(tex, tc - vec2(0.01, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.0, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(-0.01, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.01, 0.0)) * 0.001; - sum += color * PHOSPHOR_BLUR_AMOUNT; - sum += texture2D(tex, tc - vec2(-0.01, 0.0)) * 0.001; - sum += texture2D(tex, tc - vec2(0.01, -0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.0, -0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(-0.01, -0.01)) * 0.001; - sum *= PHOSPHOR_GLOW_AMOUNT; - - // Initialize sum_sum_factor to zero - vec4 sum_sum_factor = vec4(0.0); - // Compute sum_j for i = -1 - vec4 sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - - // Compute sum_j for i = 0 - sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - - // Compute sum_j for i = 1 - sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - color += mix(sum_sum_factor * sum_sum_factor * vec4(factor), sum, 0.5); - return color; -} - -vec4 applyAdaptiveSharpen(vec2 tc, vec4 color, sampler2D tex) { - vec4 color_tl = texture2D(tex, tc + vec2(-1.0, -1.0) * 0.5 / 2160.0); - vec4 color_tr = texture2D(tex, tc + vec2(1.0, -1.0) * 0.5 / 2160.0); - vec4 color_bl = texture2D(tex, tc + vec2(-1.0, 1.0) * 0.5 / 2160.0); - vec4 color_br = texture2D(tex, tc + vec2(1.0, 1.0) * 0.5 / 2160.0); - float sharpness = SHARPNESS; - vec3 color_no_alpha = color.rgb; - vec3 color_tl_no_alpha = color_tl.rgb; - vec3 color_tr_no_alpha = color_tr.rgb; - vec3 color_bl_no_alpha = color_bl.rgb; - vec3 color_br_no_alpha = color_br.rgb; - float delta = (dot(color_no_alpha, vec3(0.333333)) + dot(color_tl_no_alpha, vec3(0.333333)) + dot(color_tr_no_alpha, vec3(0.333333)) + dot(color_bl_no_alpha, vec3(0.333333)) + dot(color_br_no_alpha, vec3(0.333333))) * 0.2 - dot(color_no_alpha, vec3(0.333333)); - vec3 sharp_color_no_alpha = color_no_alpha + min(vec3(0.0), vec3(delta * sharpness)); - vec4 sharp_color = vec4(sharp_color_no_alpha, color.a); - return sharp_color; -} - -vec4 applyScanlines(vec2 tc, vec4 color) { - float scanline = (cos(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME) * - sin(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME)) * SCANLINE_THICKNESS; - float alpha = clamp(1.0 - abs(scanline), 0.0, 1.0); - return vec4(color.rgb * alpha, color.a); -} - -vec4 applyChromaticAberration(vec2 uv, vec4 color) { - vec2 center = vec2(0.5, 0.5); // center of the screen - vec2 offset = (uv - center) * CHROMATIC_ABERRATION_STRENGTH; // calculate the offset from the center - - // apply lens distortion - float rSquared = dot(offset, offset); - float distortion = 1.0 + LENS_DISTORTION_AMOUNT * rSquared; - vec2 distortedOffset = offset * distortion; - - // apply chromatic aberration - vec2 redOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); - vec2 blueOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); - - vec4 redColor = texture2D(tex, uv + redOffset); - vec4 blueColor = texture2D(tex, uv + blueOffset); - - vec4 result = vec4(redColor.r, color.g, blueColor.b, color.a); - - return result; -} - -vec4 reduceGlare(vec4 color) { - // Calculate the intensity of the color by taking the average of the RGB components - float intensity = (color.r + color.g + color.b) / 3.0; - // Set the maximum intensity that can be considered for glare - float maxIntensity = 0.98; - // Use smoothstep to create a smooth transition from no glare to full glare - // based on the intensity of the color and the maximum intensity - float glareIntensity = smoothstep(maxIntensity - 0.02, maxIntensity, intensity); - // Set the amount of glare to apply to the color - float glareAmount = 0.02; - // Mix the original color with the reduced color that has glare applied to it - vec3 reducedColor = mix(color.rgb, vec3(glareIntensity), glareAmount); - // Return the reduced color with the original alpha value - return vec4(reducedColor, color.a); -} - -// Apply a fake HDR effect to the input color. -// Parameters: -// - inputColor: the color to apply the effect to. -// - brightness: the brightness of the image. Should be a value between 0 and 1. -// - contrast: the contrast of the image. Should be a value between 0 and 1. -// - saturation: the saturation of the image. Should be a value between 0 and 2. -// - bloom: the intensity of the bloom effect. Should be a value between 0 and 1. -vec4 applyFakeHDREffect(vec4 inputColor, float brightness, float contrast, float saturation, float bloom) { - const float minBrightness = 0.0; - const float maxBrightness = 1.0; - const float minContrast = 0.0; - const float maxContrast = 1.0; - const float minSaturation = 0.0; - const float maxSaturation = 2.0; - const float minBloom = 0.0; - const float maxBloom = 1.0; - - // Check input parameters for validity - if (brightness < minBrightness || brightness > maxBrightness) { - return vec4(0.0, 0.0, 0.0, 1.0); // Return black with alpha of 1.0 to indicate error - } - if (contrast < minContrast || contrast > maxContrast) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - if (saturation < minSaturation || saturation > maxSaturation) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - if (bloom < minBloom || bloom > maxBloom) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - - // Apply brightness and contrast - vec3 color = inputColor.rgb; - color = (color - vec3(0.5)) * exp2(brightness * 10.0) + vec3(0.5); - color = mix(vec3(0.5), color, pow(contrast * 4.0 + 1.0, 2.0)); - - // // NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); - - // // BT.709 - // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); - - // // BT.2020 - // vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); - - // Warm NTSC - vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); - - // Apply saturation - float luminance = dot(color, lumCoeff); - vec3 grey = vec3(luminance); - color = mix(grey, color, saturation); - - // Apply bloom effect - float threshold = 1.0 - bloom; - vec3 bloomColor = max(color - threshold, vec3(0.0)); - bloomColor = pow(bloomColor, vec3(2.0)); - bloomColor = mix(vec3(0.0), bloomColor, pow(min(luminance, threshold), 4.0)); - color += bloomColor; - - return vec4(color, inputColor.a); -} - -vec4 bilateralFilter(sampler2D tex, vec2 uv, vec4 color, float sampleRadius, float noiseThreshold, float intensity) { - vec4 filteredColor = vec4(0.0); - float totalWeight = 0.0; - - // Top-left pixel - vec4 sample = texture2D(tex, uv + vec2(-1.0, -1.0)); - float dist = length(vec2(-1.0, -1.0)); - float colorDist = length(sample - color); - float weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Top pixel - sample = texture2D(tex, uv + vec2(0.0, -1.0)); - dist = length(vec2(0.0, -1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Top-right pixel - sample = texture2D(tex, uv + vec2(1.0, -1.0)); - dist = length(vec2(1.0, -1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Left pixel - sample = texture2D(tex, uv + vec2(-1.0, 0.0)); - dist = length(vec2(-1.0, 0.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Center pixel - sample = texture2D(tex, uv); - dist = 0.0; - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Right pixel - sample = texture2D(tex, uv + vec2(1.0, 0.0)); - dist = length(vec2(1.0, 0.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Bottom-left pixel - sample = texture2D(tex, uv + vec2(-1.0, 1.0)); - dist = length(vec2(-1.0, 1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - -// Bottom pixel - sample = texture2D(tex, uv + vec2(0.0, sampleRadius)); - dist = length(vec2(0.0, sampleRadius)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - filteredColor /= totalWeight; - return mix(color, filteredColor, step(noiseThreshold, length(filteredColor - color))); -} - -vec4 supersample(sampler2D tex, vec2 uv, float sampleRadius, float noiseThreshold, float intensity) { - float radiusSq = sampleRadius * sampleRadius; - vec2 poissonDisk; - vec4 color = vec4(0.0); - - float r1_0 = sqrt(0.0 / 16.0); - float r2_0 = fract(1.0 / 3.0); - float theta_0 = TWOPI * r2_0; - poissonDisk = vec2(r1_0 * cos(theta_0), r1_0 * sin(theta_0)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_1 = sqrt(1.0 / 16.0); - float r2_1 = fract(2.0 / 3.0); - float theta_1 = TWOPI * r2_1; - poissonDisk = vec2(r1_1 * cos(theta_1), r1_1 * sin(theta_1)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_2 = sqrt(2.0 / 16.0); - float r2_2 = fract(3.0 / 3.0); - float theta_2 = TWOPI * r2_2; - poissonDisk = vec2(r1_2 * cos(theta_2), r1_2 * sin(theta_2)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_3 = sqrt(3.0 / 16.0); - float r2_3 = fract(4.0 / 3.0); - float theta_3 = TWOPI * r2_3; - poissonDisk = vec2(r1_3 * cos(theta_3), r1_3 * sin(theta_3)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_4 = sqrt(4.0 / 16.0); - float r2_4 = fract(5.0 / 3.0); - float theta_4 = TWOPI * r2_4; - poissonDisk = vec2(r1_4 * cos(theta_4), r1_4 * sin(theta_4)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_5 = sqrt(5.0 / 16.0); - float r2_5 = fract(6.0 / 3.0); - float theta_5 = TWOPI * r2_5; - poissonDisk = vec2(r1_5 * cos(theta_5), r1_5 * sin(theta_5)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_6 = sqrt(6.0 / 16.0); - float r2_6 = fract(7.0 / 3.0); - float theta_6 = TWOPI * r2_6; - poissonDisk = vec2(r1_6 * cos(theta_6), r1_6 * sin(theta_6)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_7 = sqrt(7.0 / 16.0); - float r2_7 = fract(8.0 / 3.0); - float theta_7 = TWOPI * r2_7; - poissonDisk = vec2(r1_7 * cos(theta_7), r1_7 * sin(theta_7)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_8 = sqrt(8.0 / 16.0); - float r2_8 = fract(9.0 / 3.0); - float theta_8 = TWOPI * r2_8; - poissonDisk = vec2(r1_8 * cos(theta_8), r1_8 * sin(theta_8)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_9 = sqrt(9.0 / 16.0); - float r2_9 = fract(10.0 / 3.0); - float theta_9 = TWOPI * r2_9; - poissonDisk = vec2(r1_9 * cos(theta_9), r1_9 * sin(theta_9)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_10 = sqrt(10.0 / 16.0); - float r2_10 = fract(11.0 / 3.0); - float theta_10 = TWOPI * r2_10; - poissonDisk = vec2(r1_10 * cos(theta_10), r1_10 * sin(theta_10)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_11 = sqrt(11.0 / 16.0); - float r2_11 = fract(12.0 / 3.0); - float theta_11 = TWOPI * r2_11; - poissonDisk = vec2(r1_11 * cos(theta_11), r1_11 * sin(theta_11)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_12 = sqrt(12.0 / 16.0); - float r2_12 = fract(13.0 / 3.0); - float theta_12 = TWOPI * r2_12; - poissonDisk = vec2(r1_12 * cos(theta_12), r1_12 * sin(theta_12)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_13 = sqrt(13.0 / 16.0); - float r2_13 = fract(14.0 / 3.0); - float theta_13 = TWOPI * r2_13; - poissonDisk = vec2(r1_13 * cos(theta_13), r1_13 * sin(theta_13)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_14 = sqrt(14.0 / 16.0); - float r2_14 = fract(15.0 / 3.0); - float theta_14 = TWOPI * r2_14; - poissonDisk = vec2(r1_14 * cos(theta_14), r1_14 * sin(theta_14)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_15 = sqrt(15.0 / 16.0); - float r2_15 = fract(16.0 / 3.0); - float theta_15 = TWOPI * r2_15; - poissonDisk = vec2(r1_15 * cos(theta_15), r1_15 * sin(theta_15)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - return bilateralFilter(tex, uv, color, sampleRadius, noiseThreshold, intensity); -} -void main() { - vec2 tc_no_dist = v_texcoord; - - vec2 tc = applyBarrelDistortion(tc_no_dist, DISTORTION_AMOUNT); - - // [-1, 1] - vec2 tc_no_dist_symmetric = tc_no_dist * 2.0 - 1.0; - - // [0,1] - vec2 tc_no_dist_normalized = (tc_no_dist_symmetric + 1.0) / 2.0; - - // vec4 color = texture2D(tex, tc); - vec4 color = supersample(tex, tc, SAMPLING_RADIUS, NOISE_THRESHOLD, DENOISE_INTENSITY); - - color = applyAdaptiveSharpen(tc, color, tex); - - color = applyPhosphorGlow(tc, color, tex); - - color = reduceGlare(color); - - color = mix(applyFakeHDREffect(color, HDR_BRIGHTNESS, HDR_CONTRAST, HDR_SATURATION, HDR_BLOOM), color, 0.5); - - color = applyColorCorrection(color); - - color /= SUPERSAMPLING_SAMPLES; - - color = mix(applyChromaticAberration(tc, color), color, 0.25); - - color = mix(color, applyVignette(color), 0.37); - - color = applyBorder(tc_no_dist_normalized, color, 1.0 - BORDER_SIZE * BORDER_RADIUS, BORDER_COLOR); - - color = mix(applyBorder(tc, color, BORDER_SIZE, BORDER_COLOR), color, 0.05); - - color = applyScanlines(tc, color); - - gl_FragColor = color; - gl_FragColor.a = 1.0; -} - diff --git a/.config/hypr/shaders/drugs.frag b/.config/hypr/shaders/drugs.frag deleted file mode 100644 index 1190ed03c..000000000 --- a/.config/hypr/shaders/drugs.frag +++ /dev/null @@ -1,42 +0,0 @@ - -precision highp float; -varying vec2 v_texcoord; -uniform sampler2D tex; -uniform float time; - -void warpco(inout vec2 tc) { - tc -= 0.5; - tc *= length(tc) * 2.0; - tc += 0.5; -} - -float rand1d(float seed) { - return sin(seed*1454.0); -} - -float rand2d(vec2 co) -{ - return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -vec3 rgb(in vec2 tc, float freq, float amp, inout vec4 centre) { - vec2 off = vec2(1.0/800.0, 0.0) * sin(tc.t * freq + time) * amp; - vec2 off2 = vec2(1.0/800.0, 0.0) * sin(tc.t * freq - time * 1.5) * amp; - centre = texture2D(tex, tc); - return vec3(texture2D(tex, tc-off).r, centre.g, texture2D(tex, tc+off2).b); -} - -void main() { - // vec2 px = 1.0 / textureSize(tex, 0).st; - vec2 tc = v_texcoord; - warpco(tc); - tc = mix(v_texcoord, tc, sin(time * 2.0)*0.07); - tc.x += rand2d(floor(tc * 20.0 + floor(time * 2.5))) * 0.01; - tc.x += rand1d(floor(tc.x * 40.0)) * 0.005 * rand1d(time * 0.001); - tc.y += sin(tc.x + time) * 0.02; - vec4 centre; - vec3 bent = rgb(tc, 100.0, 5.0, centre); - vec3 col = mix(centre.rgb, bent, sin(time)); - gl_FragColor = vec4(col, centre.a); - // gl_FragColor = vec4(texture2D(tex, v_texcoord)); -} \ No newline at end of file diff --git a/.config/hypr/shaders/extradark.frag b/.config/hypr/shaders/extradark.frag deleted file mode 100644 index 089ee8149..000000000 --- a/.config/hypr/shaders/extradark.frag +++ /dev/null @@ -1,21 +0,0 @@ -// vim: set ft=glsl: -// blue light filter shader -// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux - -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -void main() { - - vec4 pixColor = texture2D(tex, v_texcoord); - - // red - pixColor[0] *= 0.7; - // green - pixColor[1] *= 0.6; - // blue - pixColor[2] *= 0.5; - - gl_FragColor = pixColor; -} diff --git a/.config/hypr/shaders/invert.frag b/.config/hypr/shaders/invert.frag deleted file mode 100644 index 864f7188c..000000000 --- a/.config/hypr/shaders/invert.frag +++ /dev/null @@ -1,13 +0,0 @@ -// vim: set ft=glsl: -// blue light filter shader -// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux - -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -void main() { - vec4 pixColor = texture2D(tex, v_texcoord); - pixColor.rgb = 1.0 - pixColor.rgb; - gl_FragColor = pixColor; -} \ No newline at end of file diff --git a/.config/hypr/shaders/solarized.frag b/.config/hypr/shaders/solarized.frag deleted file mode 100644 index 1c37f2cc0..000000000 --- a/.config/hypr/shaders/solarized.frag +++ /dev/null @@ -1,41 +0,0 @@ -// -*- mode:c -*- -precision lowp float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -float distanceSquared(vec3 pixColor, vec3 solarizedColor) { - vec3 distanceVector = pixColor - solarizedColor; - return dot(distanceVector, distanceVector); -} - -void main() { - vec3 solarized[16]; - solarized[0] = vec3(0.,0.169,0.212); - solarized[1] = vec3(0.027,0.212,0.259); - solarized[2] = vec3(0.345,0.431,0.459); - solarized[3] = vec3(0.396,0.482,0.514); - solarized[4] = vec3(0.514,0.58,0.588); - solarized[5] = vec3(0.576,0.631,0.631); - solarized[6] = vec3(0.933,0.91,0.835); - solarized[7] = vec3(0.992,0.965,0.89); - solarized[8] = vec3(0.71,0.537,0.); - solarized[9] = vec3(0.796,0.294,0.086); - solarized[10] = vec3(0.863,0.196,0.184); - solarized[11] = vec3(0.827,0.212,0.51); - solarized[12] = vec3(0.424,0.443,0.769); - solarized[13] = vec3(0.149,0.545,0.824); - solarized[14] = vec3(0.165,0.631,0.596); - solarized[15] = vec3(0.522,0.6,0.); - - vec3 pixColor = vec3(texture2D(tex, v_texcoord)); - int closest = 0; - float closestDistanceSquared = distanceSquared(pixColor, solarized[0]); - for (int i = 1; i < 15; i++) { - float newDistanceSquared = distanceSquared(pixColor, solarized[i]); - if (newDistanceSquared < closestDistanceSquared) { - closest = i; - closestDistanceSquared = newDistanceSquared; - } - } - gl_FragColor = vec4(solarized[closest], 1.); -} diff --git a/.config/quickshell/ii/GlobalStates.qml b/.config/quickshell/ii/GlobalStates.qml index f2836c99b..207b23fe3 100644 --- a/.config/quickshell/ii/GlobalStates.qml +++ b/.config/quickshell/ii/GlobalStates.qml @@ -10,6 +10,7 @@ pragma ComponentBehavior: Bound Singleton { id: root property bool barOpen: true + property bool crosshairOpen: false property bool sidebarLeftOpen: false property bool sidebarRightOpen: false property bool mediaControlsOpen: false diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 8b054fc91..4ed1055d7 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound import qs import qs.services import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.modules.common.functions as CF import QtQuick @@ -13,14 +14,14 @@ import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland - Variants { id: root - readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition - readonly property real fixedClockX: Config.options.background.clockX - readonly property real fixedClockY: Config.options.background.clockY + readonly property bool fixedClockPosition: Config.options.background.clock.fixedPosition + readonly property real fixedClockX: Config.options.background.clock.x + readonly property real fixedClockY: Config.options.background.clock.y readonly property real clockSizePadding: 20 readonly property real screenSizePadding: 50 + readonly property string clockStyle: Config.options.background.clock.style model: Quickshell.screens PanelWindow { @@ -29,8 +30,8 @@ Variants { required property var modelData // Hide when fullscreen - property list workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name) - property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland?.fullscreen)[0] != undefined) && workspace.active))[0] + property list workspacesForMonitor: Hyprland.workspaces.values.filter(workspace => workspace.monitor && workspace.monitor.name == monitor.name) + property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace => ((workspace.toplevels.values.filter(window => window.wayland?.fullscreen)[0] != undefined) && workspace.active))[0] visible: GlobalStates.screenLocked || (!(activeWorkspaceWithFullscreen != undefined)) || !Config?.options.background.hideWhenFullscreen // Workspaces @@ -39,12 +40,14 @@ Variants { property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1 property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10 // Wallpaper - property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") - || Config.options.background.wallpaperPath.endsWith(".webm") - || Config.options.background.wallpaperPath.endsWith(".mkv") - || Config.options.background.wallpaperPath.endsWith(".avi") - || Config.options.background.wallpaperPath.endsWith(".mov") + property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath + property bool wallpaperSafetyTriggered: { + const enabled = Config.options.workSafety.enable.wallpaper + const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords)) + const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveWallpaper && sensitiveNetwork; + } property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated @@ -57,7 +60,7 @@ Variants { property real clockX: (modelData.width / 2) property real clockY: (modelData.height / 2) property var textHorizontalAlignment: { - if (Config.options.background.blur.enable && Config.options.background.blur.centerClock && GlobalStates.screenLocked) + if ((Config.options.lock.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered) return Text.AlignHCenter; if (clockX < screen.width / 3) return Text.AlignLeft; @@ -66,10 +69,17 @@ Variants { return Text.AlignHCenter; } // Colors - property color dominantColor: Appearance.colors.colPrimary + property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable) + property color dominantColor: Appearance.colors.colPrimary // Default, to be changed property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 - property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) - property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.blur.enable) + property color colText: { + if (wallpaperSafetyTriggered) + return CF.ColorUtils.mix(Appearance.colors.colOnLayer0, Appearance.colors.colPrimary, 0.75); + return (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)); + } + Behavior on colText { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } // Layer props screen: modelData @@ -83,42 +93,43 @@ Variants { left: true right: true } - color: "transparent" + color: CF.ColorUtils.transparentize(CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75), (bgRoot.wallpaperIsVideo ? 1 : 0)) + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } onWallpaperPathChanged: { - bgRoot.updateZoomScale() + bgRoot.updateZoomScale(); // Clock position gets updated after zoom scale is updated } // Wallpaper zoom scale function updateZoomScale() { - getWallpaperSizeProc.path = bgRoot.wallpaperPath + getWallpaperSizeProc.path = bgRoot.wallpaperPath; getWallpaperSizeProc.running = true; } Process { id: getWallpaperSizeProc property string path: bgRoot.wallpaperPath - command: [ "magick", "identify", "-format", "%w %h", path ] + command: ["magick", "identify", "-format", "%w %h", path] stdout: StdioCollector { id: wallpaperSizeOutputCollector onStreamFinished: { - const output = wallpaperSizeOutputCollector.text + const output = wallpaperSizeOutputCollector.text; const [width, height] = output.split(" ").map(Number); const [screenWidth, screenHeight] = [bgRoot.screen.width, bgRoot.screen.height]; - bgRoot.wallpaperWidth = width - bgRoot.wallpaperHeight = height + bgRoot.wallpaperWidth = width; + bgRoot.wallpaperHeight = height; - if (width <= screenWidth || height <= screenHeight) { // Undersized/perfectly sized wallpapers + if (width <= screenWidth || height <= screenHeight) { + // Undersized/perfectly sized wallpapers bgRoot.effectiveWallpaperScale = Math.max(screenWidth / width, screenHeight / height); - } else { // Oversized = can be zoomed for parallax, yay - bgRoot.effectiveWallpaperScale = Math.min( - bgRoot.preferredWallpaperScale, - width / screenWidth, height / screenHeight - ); + } else { + // Oversized = can be zoomed for parallax, yay + bgRoot.effectiveWallpaperScale = Math.min(bgRoot.preferredWallpaperScale, width / screenWidth, height / screenHeight); } - - bgRoot.updateClockPosition() + bgRoot.updateClockPosition(); } } } @@ -126,11 +137,11 @@ Variants { // Clock positioning function updateClockPosition() { // Somehow all this manual setting is needed to make the proc correctly use the new values - leastBusyRegionProc.path = bgRoot.wallpaperPath - leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2 - leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2 - leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2 - leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2 + leastBusyRegionProc.path = bgRoot.wallpaperPath; + leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2; + leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2; + leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2; + leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2; leastBusyRegionProc.running = false; leastBusyRegionProc.running = true; } @@ -141,246 +152,317 @@ Variants { property int contentHeight: 300 property int horizontalPadding: bgRoot.movableXSpace property int verticalPadding: bgRoot.movableYSpace - command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), - "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), - "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), - "--width", contentWidth, - "--height", contentHeight, - "--horizontal-padding", horizontalPadding, - "--vertical-padding", verticalPadding, - path, + command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), "--width", contentWidth, "--height", contentHeight, "--horizontal-padding", horizontalPadding, "--vertical-padding", verticalPadding, path // "--visual-output", - ] + ,] stdout: StdioCollector { id: leastBusyRegionOutputCollector onStreamFinished: { - const output = leastBusyRegionOutputCollector.text + const output = leastBusyRegionOutputCollector.text; // console.log("[Background] Least busy region output:", output) - if (output.length === 0) return; - const parsedContent = JSON.parse(output) - bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale - bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale - bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary + if (output.length === 0) + return; + const parsedContent = JSON.parse(output); + bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale; + bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale; + bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary; } } } // Wallpaper - Image { - id: wallpaper - visible: opacity > 0 && !blurLoader.active - opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) - } - cache: false - asynchronous: true - retainWhileLoading: true - smooth: false - // Range = groups that workspaces span on - property int chunkSize: Config?.options.bar.workspaces.shown ?? 10; - property int lower: Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize; - property int upper: Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize; - property int range: upper - lower; - property real valueX: { - let result = 0.5; - if (Config.options.background.parallax.enableWorkspace && !bgRoot.verticalParallax) { - result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); - } - if (Config.options.background.parallax.enableSidebar) { - result += (0.15 * GlobalStates.sidebarRightOpen - 0.15 * GlobalStates.sidebarLeftOpen); - } - return result; - } - property real valueY: { - let result = 0.5; - if (Config.options.background.parallax.enableWorkspace && bgRoot.verticalParallax) { - result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); - } - return result; - } - property real effectiveValueX: Math.max(0, Math.min(1, valueX)) - property real effectiveValueY: Math.max(0, Math.min(1, valueY)) - x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace - y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace - source: bgRoot.wallpaperPath - fillMode: Image.PreserveAspectCrop - Behavior on x { - NumberAnimation { - duration: 600 - easing.type: Easing.OutCubic - } - } - Behavior on y { - NumberAnimation { - duration: 600 - easing.type: Easing.OutCubic - } - } - sourceSize { - width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale - height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale - } - width: bgRoot.wallpaperWidth / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale - height: bgRoot.wallpaperHeight / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale - } + Item { + anchors.fill: parent + clip: true - Loader { - id: blurLoader - active: Config.options.background.blur.enable && (GlobalStates.screenLocked || scaleAnim.running) - anchors.fill: wallpaper - scale: GlobalStates.screenLocked ? Config.options.background.blur.extraZoom : 1 - Behavior on scale { - NumberAnimation { - id: scaleAnim - duration: 400 - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + Image { + id: wallpaper + visible: opacity > 0 && !blurLoader.active + opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + cache: false + asynchronous: true + retainWhileLoading: true + smooth: false + // Range = groups that workspaces span on + property int chunkSize: Config?.options.bar.workspaces.shown ?? 10 + property int lower: Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize + property int upper: Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize + property int range: upper - lower + property real valueX: { + let result = 0.5; + if (Config.options.background.parallax.enableWorkspace && !bgRoot.verticalParallax) { + result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); + } + if (Config.options.background.parallax.enableSidebar) { + result += (0.15 * GlobalStates.sidebarRightOpen - 0.15 * GlobalStates.sidebarLeftOpen); + } + return result; + } + property real valueY: { + let result = 0.5; + if (Config.options.background.parallax.enableWorkspace && bgRoot.verticalParallax) { + result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); + } + return result; + } + property real effectiveValueX: Math.max(0, Math.min(1, valueX)) + property real effectiveValueY: Math.max(0, Math.min(1, valueY)) + x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace + y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace + source: bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath + fillMode: Image.PreserveAspectCrop + Behavior on x { + NumberAnimation { + duration: 600 + easing.type: Easing.OutCubic + } + } + Behavior on y { + NumberAnimation { + duration: 600 + easing.type: Easing.OutCubic + } + } + sourceSize { + width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale + height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale + } + width: bgRoot.wallpaperWidth / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale + height: bgRoot.wallpaperHeight / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale } - sourceComponent: GaussianBlur { - source: wallpaper - radius: GlobalStates.screenLocked ? Config.options.background.blur.radius : 0 - samples: radius * 2 + 1 - Rectangle { - opacity: GlobalStates.screenLocked ? 1 : 0 - anchors.fill: parent - color: CF.ColorUtils.transparentize(Appearance.colors.colLayer0, 0.7) + Loader { + id: blurLoader + active: Config.options.lock.blur.enable && (GlobalStates.screenLocked || scaleAnim.running) + anchors.fill: wallpaper + scale: GlobalStates.screenLocked ? Config.options.lock.blur.extraZoom : 1 + Behavior on scale { + NumberAnimation { + id: scaleAnim + duration: 400 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + } } - } - } + sourceComponent: GaussianBlur { + source: wallpaper + radius: GlobalStates.screenLocked ? Config.options.lock.blur.radius : 0 + samples: radius * 2 + 1 - // The clock - Loader { - id: clockLoader - active: Config.options.background.showClock - anchors { - left: wallpaper.left - top: wallpaper.top - horizontalCenter: undefined - leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - topMargin: { - if (bgRoot.shouldBlur) - return bgRoot.modelData.height / 3 - return bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) - } - Behavior on leftMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - Behavior on topMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + Rectangle { + opacity: GlobalStates.screenLocked ? 1 : 0 + anchors.fill: parent + color: CF.ColorUtils.transparentize(Appearance.colors.colLayer0, 0.7) + } } } - states: State { - name: "centered" - when: bgRoot.shouldBlur - AnchorChanges { - target: clockLoader + + // The clock + Loader { + id: clockLoader + scale: Config.options.background.clock.scale + active: Config.options.background.clock.show + anchors { + left: wallpaper.left + top: wallpaper.top + horizontalCenter: undefined + verticalCenter: undefined + leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) + topMargin: bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) + Behavior on leftMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on topMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + } + states: State { + name: "centered" + when: (GlobalStates.screenLocked && Config.options.lock.centerClock) || bgRoot.wallpaperSafetyTriggered + AnchorChanges { + target: clockLoader + anchors { + left: undefined + right: undefined + top: undefined + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + } + } + transitions: Transition { + AnchorAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } + } + sourceComponent: ColumnLayout { + spacing: 8 + + Loader { + id: digitalClockLoader + visible: root.clockStyle === "digital" + active: visible + sourceComponent: ColumnLayout { + id: clockColumn + spacing: 6 + + ClockText { + font.pixelSize: 90 + text: DateTime.time + } + ClockText { + Layout.topMargin: -5 + text: DateTime.date + } + StyledText { + // Somehow gets fucked up if made a ClockText??? + visible: Config.options.background.quote.length > 0 + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.normal + weight: 350 + italic: true + } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: Config.options.background.quote + } + } + } + + Loader { + id: cookieClockLoader + Layout.alignment: Qt.AlignHCenter + visible: root.clockStyle === "cookie" + active: visible + sourceComponent: CookieClock {} + } + } + + Item { anchors { - left: undefined - horizontalCenter: wallpaper.horizontalCenter - right: undefined + top: clockLoader.bottom + topMargin: 8 + horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined + left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined + right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined + leftMargin: -26 + rightMargin: -26 } - } - } - transitions: Transition { - AnchorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - } - sourceComponent: Item { - id: clock - implicitWidth: clockColumn.implicitWidth - implicitHeight: clockColumn.implicitHeight + implicitWidth: statusTextBg.implicitWidth + implicitHeight: statusTextBg.implicitHeight - ColumnLayout { - id: clockColumn - anchors.centerIn: parent - spacing: 6 + StyledRectangularShadow { + target: statusTextBg + visible: statusTextBg.visible && root.clockStyle === "cookie" + opacity: statusTextBg.opacity + } - StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 90 - weight: Font.Bold + Rectangle { + id: statusTextBg + anchors.centerIn: parent + clip: true + opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 + visible: opacity > 0 + implicitHeight: statusTextRow.implicitHeight + 5 * 2 + implicitWidth: statusTextRow.implicitWidth + 5 * 2 + radius: Appearance.rounding.small + color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1) + + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: DateTime.time - } - StyledText { - Layout.fillWidth: true - Layout.topMargin: -5 - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: DateTime.date - animateChange: true - } - StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } - color: bgRoot.colText - style: Text.Raised - visible: Config.options.background.quote !== "" - styleColor: Appearance.colors.colShadow - text: Config.options.background.quote - } - } - RowLayout { - anchors { - top: clockColumn.bottom - left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined - right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined - horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined - topMargin: 5 - leftMargin: -5 - rightMargin: -5 + RowLayout { + id: statusTextRow + anchors.centerIn: parent + spacing: 14 + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft + implicitWidth: 1 + } + ClockStatusText { + id: safetyStatusText + shown: bgRoot.wallpaperSafetyTriggered + statusIcon: "hide_image" + statusText: qsTr("Wallpaper safety enforced") + } + ClockStatusText { + id: lockStatusText + shown: GlobalStates.screenLocked && Config.options.lock.showLockedText + statusIcon: "lock" + statusText: qsTr("Locked") + } + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight + implicitWidth: 1 + } + } } - opacity: GlobalStates.screenLocked && (!Config.options.background.blur.enable || Config.options.background.blur.showLockedText) ? 1 : 0 - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } - MaterialSymbol { - text: "lock" - Layout.fillWidth: false - iconSize: Appearance.font.pixelSize.huge - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - StyledText { - Layout.fillWidth: false - text: "Locked" - color: bgRoot.colText - font.pixelSize: Appearance.font.pixelSize.larger - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } - } } } } + + // Components + component ClockText: StyledText { + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: Font.DemiBold + } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: true + } + component ClockStatusText: RowLayout { + id: statusTextRow + property alias statusIcon: statusIconWidget.text + property alias statusText: statusTextWidget.text + property bool shown: true + property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : bgRoot.colText + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Layout.fillWidth: false + MaterialSymbol { + id: statusIconWidget + Layout.fillWidth: false + iconSize: Appearance.font.pixelSize.huge + color: statusTextRow.textColor + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + ClockText { + id: statusTextWidget + Layout.fillWidth: false + color: statusTextRow.textColor + font { + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.large + weight: Font.Normal + } + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + } } diff --git a/.config/quickshell/ii/modules/background/CookieClock.qml b/.config/quickshell/ii/modules/background/CookieClock.qml new file mode 100644 index 000000000..0e1474543 --- /dev/null +++ b/.config/quickshell/ii/modules/background/CookieClock.qml @@ -0,0 +1,142 @@ +pragma ComponentBehavior: Bound + +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Layouts +import Quickshell +import Qt5Compat.GraphicalEffects + +Item { + id: root + + property real implicitSize: 230 + property real hourHandLength: 72 + property real hourHandWidth: 16 + property real minuteHandLength: 95 + property real minuteHandWidth: 8 + property real centerDotSize: 10 + property real hourDotSize: minuteHandWidth + + property color colShadow: Appearance.colors.colShadow + property color colBackground: Appearance.colors.colSecondaryContainer + property color colOnBackground: ColorUtils.mix(Appearance.colors.colPrimary, Appearance.colors.colSecondaryContainer, 0.5) + property color colHourHand: Appearance.colors.colPrimary + property color colMinuteHand: Appearance.colors.colSecondary + property color colOnHourHand: Appearance.colors.colOnPrimary + + readonly property list clockNumbers: DateTime.time.split(/[: ]/) + readonly property int clockHour: parseInt(clockNumbers[0]) % 12 + readonly property int clockMinute: parseInt(clockNumbers[1]) + implicitWidth: implicitSize + implicitHeight: implicitSize + + DropShadow { + source: cookie + anchors.fill: source + horizontalOffset: 0 + verticalOffset: 2 + radius: 12 + samples: radius * 2 + 1 + color: root.colShadow + transparentBorder: true + } + + MaterialCookie { + id: cookie + z: 0 + implicitSize: root.implicitSize + amplitude: implicitSize / 70 + sides: 12 + color: root.colBackground + + // 12 dots around the cookie + Repeater { + model: 12 + Item { + required property int index + rotation: 360 / 12 * index + anchors.fill: parent + Rectangle { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 10 + } + implicitWidth: root.hourDotSize + implicitHeight: implicitWidth + radius: implicitWidth / 2 + color: root.colOnBackground + opacity: 0.5 + } + } + } + } + + Column { + id: timeIndicators + z: 1 + anchors.centerIn: cookie + spacing: -16 + + // Numbers + Repeater { + model: root.clockNumbers + delegate: StyledText { + required property string modelData + + anchors.horizontalCenter: parent?.horizontalCenter + font { + pixelSize: modelData.match(/am|pm/i) ? 26 : 68 + family: Appearance.font.family.expressive + weight: Font.Bold + } + color: root.colOnBackground + text: modelData.padStart(2, "0") + } + } + } + + // Hour hand + Item { + anchors.fill: parent + z: 2 + rotation: -90 + (360 / 12) * (root.clockHour + root.clockMinute / 60) + Rectangle { + anchors.verticalCenter: parent.verticalCenter + x: parent.width / 2 - hourHandWidth / 2 + width: hourHandLength + height: hourHandWidth + radius: hourHandWidth / 2 + color: root.colHourHand + } + } + + // Minute hand + Item { + anchors.fill: parent + z: 3 + rotation: -90 + (360 / 60) * root.clockMinute + Rectangle { + anchors.verticalCenter: parent.verticalCenter + x: parent.width / 2 - minuteHandWidth / 2 + width: minuteHandLength + height: minuteHandWidth + radius: minuteHandWidth / 2 + color: root.colMinuteHand + } + } + + // Center dot + Rectangle { + z: 4 + color: root.colOnHourHand + anchors.centerIn: parent + implicitWidth: centerDotSize + implicitHeight: implicitWidth + radius: implicitWidth / 2 + } +} diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index f23c7eb9d..93587c398 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -1,11 +1,9 @@ import "./weather" import QtQuick -import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland -import Quickshell.Services.UPower import qs import qs.services import qs.modules.common @@ -93,8 +91,8 @@ Scope { id: hoverMaskRegion anchors { fill: barContent - topMargin: -1 - bottomMargin: -1 + topMargin: -Config.options.bar.autoHide.hoverRegionWidth + bottomMargin: -Config.options.bar.autoHide.hoverRegionWidth } } diff --git a/.config/quickshell/ii/modules/bar/BarContent.qml b/.config/quickshell/ii/modules/bar/BarContent.qml index 7e1e885c0..c0c5e31b8 100644 --- a/.config/quickshell/ii/modules/bar/BarContent.qml +++ b/.config/quickshell/ii/modules/bar/BarContent.qml @@ -289,16 +289,9 @@ Item { // Bar content region color: rightSidebarButton.colText } } - Loader { - active: HyprlandXkb.layoutCodes.length > 1 - visible: active + HyprlandXkbIndicator { + Layout.alignment: Qt.AlignVCenter Layout.rightMargin: indicatorsRowLayout.realSpacing - sourceComponent: StyledText { - text: HyprlandXkb.currentLayoutCode - font.pixelSize: Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true - } } MaterialSymbol { Layout.rightMargin: indicatorsRowLayout.realSpacing diff --git a/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml new file mode 100644 index 000000000..3e85b829b --- /dev/null +++ b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml @@ -0,0 +1,27 @@ +import QtQuick +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +Loader { + id: root + property bool vertical: false + + active: HyprlandXkb.layoutCodes.length > 1 + visible: active + + sourceComponent: Item { + implicitWidth: root.vertical ? null : layoutCodeText.implicitWidth + implicitHeight: root.vertical ? layoutCodeText.implicitHeight : null + + StyledText { + id: layoutCodeText + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: HyprlandXkb.currentLayoutCode.split(":").join("\n") + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small + color: rightSidebarButton.colText + animateChange: true + } + } +} diff --git a/.config/quickshell/ii/modules/bar/Media.qml b/.config/quickshell/ii/modules/bar/Media.qml index 87151c6ec..63295d8d6 100644 --- a/.config/quickshell/ii/modules/bar/Media.qml +++ b/.config/quickshell/ii/modules/bar/Media.qml @@ -21,7 +21,7 @@ Item { Timer { running: activePlayer?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: activePlayer.positionChanged() } diff --git a/.config/quickshell/ii/modules/bar/ScrollHint.qml b/.config/quickshell/ii/modules/bar/ScrollHint.qml index a8e1c8dfe..9a338df48 100644 --- a/.config/quickshell/ii/modules/bar/ScrollHint.qml +++ b/.config/quickshell/ii/modules/bar/ScrollHint.qml @@ -24,7 +24,7 @@ Revealer { // Scroll hint // StyledToolTip { // extraVisibleCondition: tooltipText.length > 0 - // content: tooltipText + // text: tooltipText // } ColumnLayout { diff --git a/.config/quickshell/ii/modules/bar/StyledPopup.qml b/.config/quickshell/ii/modules/bar/StyledPopup.qml index 9570da884..68ba609e2 100644 --- a/.config/quickshell/ii/modules/bar/StyledPopup.qml +++ b/.config/quickshell/ii/modules/bar/StyledPopup.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.widgets import qs.modules.common.functions import QtQuick import QtQuick.Effects @@ -26,6 +27,10 @@ LazyLoader { implicitWidth: popupBackground.implicitWidth + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin implicitHeight: popupBackground.implicitHeight + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin + mask: Region { + item: popupBackground + } + exclusionMode: ExclusionMode.Ignore exclusiveZone: 0 margins { @@ -49,15 +54,8 @@ LazyLoader { WlrLayershell.namespace: "quickshell:popup" WlrLayershell.layer: WlrLayer.Overlay - RectangularShadow { - property var target: popupBackground - anchors.fill: target - radius: target.radius - blur: 0.9 * Appearance.sizes.hyprlandGapsOut - offset: Qt.vector2d(0.0, 1.0) - spread: 0.7 - color: Appearance.colors.colShadow - cached: true + StyledRectangularShadow { + target: popupBackground } Rectangle { diff --git a/.config/quickshell/ii/modules/bar/SysTray.qml b/.config/quickshell/ii/modules/bar/SysTray.qml index 8eba7b6db..8da341192 100644 --- a/.config/quickshell/ii/modules/bar/SysTray.qml +++ b/.config/quickshell/ii/modules/bar/SysTray.qml @@ -3,6 +3,7 @@ import qs.modules.common.widgets import QtQuick import QtQuick.Layouts import Quickshell +import Quickshell.Hyprland import Quickshell.Services.SystemTray Item { @@ -14,20 +15,58 @@ Item { property bool trayOverflowOpen: false property bool showSeparator: true property bool showOverflowMenu: true + property var activeMenu: null property list itemsInUserList: SystemTray.items.values.filter(i => (Config.options.bar.tray.pinnedItems.includes(i.id) && i.status !== Status.Passive)) property list itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.bar.tray.pinnedItems.includes(i.id) && i.status !== Status.Passive)) property bool invertPins: Config.options.bar.tray.invertPinnedItems property list pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList property list unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList - onUnpinnedItemsChanged: if (unpinnedItems.length == 0) - root.trayOverflowOpen = false + onUnpinnedItemsChanged: { + if (unpinnedItems.length == 0) root.closeOverflowMenu(); + } + + function grabFocus() { + focusGrab.active = true; + } + + function setExtraWindowAndGrabFocus(window) { + root.activeMenu = window; + root.grabFocus(); + } + + function releaseFocus() { + focusGrab.active = false; + } + + function closeOverflowMenu() { + focusGrab.active = false; + } + + onTrayOverflowOpenChanged: { + if (root.trayOverflowOpen) { + root.grabFocus(); + } + } + + HyprlandFocusGrab { + id: focusGrab + active: false + windows: [trayOverflowLayout.QsWindow?.window, root.activeMenu] + onCleared: { + root.trayOverflowOpen = false; + if (root.activeMenu) { + root.activeMenu.close(); + root.activeMenu = null; + } + } + } GridLayout { id: gridLayout columns: root.vertical ? 1 : -1 anchors.fill: parent - rowSpacing: 6 + rowSpacing: 8 columnSpacing: 15 RippleButton { @@ -60,6 +99,7 @@ Item { } StyledPopup { + id: overflowPopup hoverTarget: trayOverflowButton active: root.trayOverflowOpen popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily) @@ -79,6 +119,8 @@ Item { item: modelData Layout.fillHeight: !root.vertical Layout.fillWidth: root.vertical + onMenuClosed: root.releaseFocus(); + onMenuOpened: (qsWindow) => root.setExtraWindowAndGrabFocus(qsWindow); } } } @@ -95,6 +137,10 @@ Item { item: modelData Layout.fillHeight: !root.vertical Layout.fillWidth: root.vertical + onMenuClosed: root.releaseFocus(); + onMenuOpened: (qsWindow) => { + root.setExtraWindowAndGrabFocus(qsWindow); + } } } diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index f2bbc39d8..1fe5df5f3 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -9,16 +9,17 @@ import Qt5Compat.GraphicalEffects MouseArea { id: root - - property var bar: root.QsWindow.window required property SystemTrayItem item property bool targetMenuOpen: false - hoverEnabled: true + signal menuOpened(qsWindow: var) + signal menuClosed() + + hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton implicitWidth: 20 implicitHeight: 20 - onClicked: (event) => { + onPressed: (event) => { switch (event.button) { case Qt.LeftButton: item.activate(); @@ -30,22 +31,36 @@ MouseArea { event.accepted = true; } onEntered: { - tooltip.content = item.tooltipTitle.length > 0 ? item.tooltipTitle + tooltip.text = item.tooltipTitle.length > 0 ? item.tooltipTitle : (item.title.length > 0 ? item.title : item.id); - if (item.tooltipDescription.length > 0) tooltip.content += " • " + item.tooltipDescription; - if (Config.options.bar.tray.showItemId) tooltip.content += "\n[" + item.id + "]"; + if (item.tooltipDescription.length > 0) tooltip.text += " • " + item.tooltipDescription; + if (Config.options.bar.tray.showItemId) tooltip.text += "\n[" + item.id + "]"; } - QsMenuAnchor { + Loader { id: menu - - menu: root.item.menu - anchor.window: bar - anchor.rect.x: root.x + (Config.options.bar.vertical ? 0 : bar?.width) - anchor.rect.y: root.y + (Config.options.bar.vertical ? bar?.height : 0) - anchor.rect.height: root.height - anchor.rect.width: root.width - anchor.edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + function open() { + menu.active = true; + } + active: false + sourceComponent: SysTrayMenu { + Component.onCompleted: this.open(); + trayItemMenuHandle: root.item.menu + anchor { + window: root.QsWindow.window + rect.x: root.x + (Config.options.bar.vertical ? 0 : QsWindow.window?.width) + rect.y: root.y + (Config.options.bar.vertical ? QsWindow.window?.height : 0) + rect.height: root.height + rect.width: root.width + edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + gravity: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + } + onMenuOpened: (window) => root.menuOpened(window); + onMenuClosed: { + root.menuClosed(); + menu.active = false; + } + } } IconImage { @@ -76,10 +91,11 @@ MouseArea { } } - StyledToolTip { + PopupToolTip { id: tooltip extraVisibleCondition: root.containsMouse alternativeVisibleCondition: extraVisibleCondition + anchorEdges: (!Config.options.bar.bottom && !Config.options.bar.vertical) ? Edges.Bottom : Edges.Top } } diff --git a/.config/quickshell/ii/modules/bar/SysTrayMenu.qml b/.config/quickshell/ii/modules/bar/SysTrayMenu.qml new file mode 100644 index 000000000..6caffb41c --- /dev/null +++ b/.config/quickshell/ii/modules/bar/SysTrayMenu.qml @@ -0,0 +1,218 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland + +PopupWindow { + id: root + required property QsMenuHandle trayItemMenuHandle + property real popupBackgroundMargin: 0 + + signal menuClosed + signal menuOpened(qsWindow: var) // Correct type is QsWindow, but QML does not like that + + color: "transparent" + property real padding: Appearance.sizes.elevationMargin + + implicitHeight: { + let result = 0; + for (let child of stackView.children) { + result = Math.max(child.implicitHeight, result); + } + return result + popupBackground.padding * 2 + root.padding * 2; + } + implicitWidth: { + let result = 0; + for (let child of stackView.children) { + result = Math.max(child.implicitWidth, result); + } + return result + popupBackground.padding * 2 + root.padding * 2; + } + + function open() { + root.visible = true; + root.menuOpened(root); + } + + function close() { + root.visible = false; + while (stackView.depth > 1) + stackView.pop(); + root.menuClosed(); + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.BackButton | Qt.RightButton + onClicked: event => { + if ((event.button === Qt.BackButton || event.button === Qt.RightButton) && stackView.depth > 1) + stackView.pop(); + } + + StyledRectangularShadow { + target: popupBackground + opacity: popupBackground.opacity + } + + Rectangle { + id: popupBackground + readonly property real padding: 4 + anchors { + left: parent.left + right: parent.right + verticalCenter: Config.options.bar.vertical ? parent.verticalCenter : undefined + top: Config.options.bar.vertical ? undefined : Config.options.bar.bottom ? undefined : parent.top + bottom: Config.options.bar.vertical ? undefined : Config.options.bar.bottom ? parent.bottom : undefined + margins: root.padding + } + + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.windowRounding + border.width: 1 + border.color: Appearance.colors.colLayer0Border + clip: true + + opacity: 0 + Component.onCompleted: opacity = 1 + implicitWidth: stackView.implicitWidth + popupBackground.padding * 2 + implicitHeight: stackView.implicitHeight + popupBackground.padding * 2 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + + StackView { + id: stackView + anchors { + fill: parent + margins: popupBackground.padding + } + pushEnter: NoAnim {} + pushExit: NoAnim {} + popEnter: NoAnim {} + popExit: NoAnim {} + + implicitWidth: currentItem.implicitWidth + implicitHeight: currentItem.implicitHeight + + initialItem: SubMenu { + handle: root.trayItemMenuHandle + } + } + } + } + + component NoAnim: Transition { + NumberAnimation { + duration: 0 + } + } + + component SubMenu: ColumnLayout { + id: submenu + required property QsMenuHandle handle + property bool isSubMenu: false + property bool shown: false + opacity: shown ? 1 : 0 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + Component.onCompleted: shown = true + StackView.onActivating: shown = true + StackView.onDeactivating: shown = false + StackView.onRemoved: destroy() + + QsMenuOpener { + id: menuOpener + menu: submenu.handle + } + + spacing: 0 + + Loader { + Layout.fillWidth: true + visible: submenu.isSubMenu + active: visible + sourceComponent: RippleButton { + id: backButton + buttonRadius: popupBackground.radius - popupBackground.padding + horizontalPadding: 12 + implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + implicitHeight: 36 + + onClicked: stackView.pop() + + contentItem: RowLayout { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + leftMargin: backButton.horizontalPadding + rightMargin: backButton.horizontalPadding + } + spacing: 8 + MaterialSymbol { + iconSize: 20 + text: "chevron_left" + } + StyledText { + Layout.fillWidth: true + text: Translation.tr("Back") + } + } + } + } + + Repeater { + id: menuEntriesRepeater + property bool iconColumnNeeded: { + for (let i = 0; i < menuOpener.children.values.length; i++) { + if (menuOpener.children.values[i].icon.length > 0) + return true; + } + return false; + } + property bool specialInteractionColumnNeeded: { + for (let i = 0; i < menuOpener.children.values.length; i++) { + if (menuOpener.children.values[i].buttonType !== QsMenuButtonType.None) + return true; + } + return false; + } + model: menuOpener.children + delegate: SysTrayMenuEntry { + required property QsMenuEntry modelData + forceIconColumn: menuEntriesRepeater.iconColumnNeeded + forceSpecialInteractionColumn: menuEntriesRepeater.specialInteractionColumnNeeded + menuEntry: modelData + + buttonRadius: popupBackground.radius - popupBackground.padding + + onDismiss: root.close() + onOpenSubmenu: handle => { + stackView.push(subMenuComponent.createObject(null, { + handle: handle, + isSubMenu: true + })); + } + } + } + } + + Component { + id: subMenuComponent + SubMenu {} + } +} diff --git a/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml new file mode 100644 index 000000000..59f3325c4 --- /dev/null +++ b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml @@ -0,0 +1,126 @@ +pragma ComponentBehavior: Bound + +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets + +RippleButton { + id: root + required property QsMenuEntry menuEntry + property bool forceIconColumn: false + property bool forceSpecialInteractionColumn: false + readonly property bool hasIcon: menuEntry.icon.length > 0 + readonly property bool hasSpecialInteraction: menuEntry.buttonType !== QsMenuButtonType.None + + signal dismiss() + signal openSubmenu(handle: QsMenuHandle) + + colBackground: menuEntry.isSeparator ? Appearance.m3colors.m3outlineVariant : ColorUtils.transparentize(Appearance.colors.colLayer0) + enabled: !menuEntry.isSeparator + opacity: 1 + + horizontalPadding: 12 + implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + implicitHeight: menuEntry.isSeparator ? 1 : 36 + Layout.topMargin: menuEntry.isSeparator ? 4 : 0 + Layout.bottomMargin: menuEntry.isSeparator ? 4 : 0 + Layout.fillWidth: true + + Component.onCompleted: { + if (menuEntry.isSeparator) { + root.buttonColor = root.colBackground; + } + } + + releaseAction: () => { + if (menuEntry.hasChildren) { + root.openSubmenu(root.menuEntry); + return; + } + menuEntry.triggered(); + root.dismiss(); + } + altAction: (event) => { // Not hog right-click + event.accepted = false; + } + + contentItem: RowLayout { + id: contentItem + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + leftMargin: root.horizontalPadding + rightMargin: root.horizontalPadding + } + spacing: 8 + visible: !root.menuEntry.isSeparator + + // Interaction: checkbox or radio button + Item { + visible: root.hasSpecialInteraction || root.forceSpecialInteractionColumn + implicitWidth: 20 + implicitHeight: 20 + + Loader { + anchors.fill: parent + active: root.menuEntry.buttonType === QsMenuButtonType.RadioButton + + sourceComponent: StyledRadioButton { + enabled: false + padding: 0 + checked: root.menuEntry.checkState === Qt.Checked + } + } + + Loader { + anchors.fill: parent + active: root.menuEntry.buttonType === QsMenuButtonType.CheckBox && root.menuEntry.checkState !== Qt.Unchecked + + sourceComponent: MaterialSymbol { + text: root.menuEntry.checkState === Qt.PartiallyChecked ? "check_indeterminate_small" : "check" + iconSize: 20 + } + } + } + + // Button icon + Item { + visible: root.hasIcon || root.forceIconColumn + implicitWidth: 20 + implicitHeight: 20 + + Loader { + anchors.centerIn: parent + active: root.menuEntry.icon.length > 0 + sourceComponent: IconImage { + asynchronous: true + source: root.menuEntry.icon + implicitSize: 20 + mipmap: true + } + } + } + + StyledText { + id: label + text: root.menuEntry.text + font.pixelSize: Appearance.font.pixelSize.smallie + Layout.fillWidth: true + } + + Loader { + active: root.menuEntry.hasChildren + + sourceComponent: MaterialSymbol { + text: "chevron_right" + iconSize: 20 + } + } + } +} diff --git a/.config/quickshell/ii/modules/bar/Workspaces.qml b/.config/quickshell/ii/modules/bar/Workspaces.qml index 775df18d7..5d0c9f62b 100644 --- a/.config/quickshell/ii/modules/bar/Workspaces.qml +++ b/.config/quickshell/ii/modules/bar/Workspaces.qml @@ -204,7 +204,7 @@ Item { rowSpacing: 0 anchors.fill: parent - implicitHeight: vertical ? Appearance.sizes.barWidth : Appearance.sizes.barHeight + implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth Repeater { diff --git a/.config/quickshell/ii/modules/common/Appearance.qml b/.config/quickshell/ii/modules/common/Appearance.qml index 3bfdf530d..d63d04800 100644 --- a/.config/quickshell/ii/modules/common/Appearance.qml +++ b/.config/quickshell/ii/modules/common/Appearance.qml @@ -212,6 +212,7 @@ Singleton { property QtObject pixelSize: QtObject { property int smallest: 10 property int smaller: 12 + property int smallie: 13 property int small: 15 property int normal: 16 property int large: 17 @@ -254,14 +255,8 @@ Singleton { easing.bezierCurve: root.animation.elementMove.bezierCurve } } - property Component colorAnimation: Component { - ColorAnimation { - duration: root.animation.elementMove.duration - easing.type: root.animation.elementMove.type - easing.bezierCurve: root.animation.elementMove.bezierCurve - } - } } + property QtObject elementMoveEnter: QtObject { property int duration: 400 property int type: Easing.BezierSpline @@ -275,6 +270,7 @@ Singleton { } } } + property QtObject elementMoveExit: QtObject { property int duration: 200 property int type: Easing.BezierSpline @@ -288,6 +284,7 @@ Singleton { } } } + property QtObject elementMoveFast: QtObject { property int duration: animationCurves.expressiveEffectsDuration property int type: Easing.BezierSpline @@ -304,6 +301,21 @@ Singleton { easing.bezierCurve: root.animation.elementMoveFast.bezierCurve }} } + + property QtObject elementResize: QtObject { + property int duration: 400 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.emphasized + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementResize.duration + easing.type: root.animation.elementResize.type + easing.bezierCurve: root.animation.elementResize.bezierCurve + } + } + } + property QtObject clickBounce: QtObject { property int duration: 200 property int type: Easing.BezierSpline @@ -315,11 +327,13 @@ Singleton { easing.bezierCurve: root.animation.clickBounce.bezierCurve }} } + property QtObject scroll: QtObject { property int duration: 200 property int type: Easing.BezierSpline property list bezierCurve: animationCurves.standardDecel } + property QtObject menuDecel: QtObject { property int duration: 350 property int type: Easing.OutExpo diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index c95b148c7..e7b595d44 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -122,12 +122,18 @@ Singleton { } property JsonObject background: JsonObject { - property bool fixedClockPosition: false - property real clockX: -500 - property real clockY: -500 - property bool showClock: true + property JsonObject clock: JsonObject { + property bool fixedPosition: false + property real x: -500 + property real y: -500 + property bool show: true + property string style: "cookie" // Options: "cookie", "digital" + property real scale: 1 + } property string wallpaperPath: "" property string thumbnailPath: "" + property string quote: "" + property bool hideWhenFullscreen: true property JsonObject parallax: JsonObject { property bool vertical: false property bool autoVertical: false @@ -135,20 +141,12 @@ Singleton { property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property bool enableSidebar: true } - property JsonObject blur: JsonObject { - property bool enable: true - property int radius: 100 - property bool centerClock: true - property bool showLockedText: true - property real extraZoom: 1.1 - } - property string quote: "" - property bool hideWhenFullscreen: true } property JsonObject bar: JsonObject { property JsonObject autoHide: JsonObject { property bool enable: false + property int hoverRegionWidth: 2 property bool pushWindows: false property JsonObject showWhenPressingSuper: JsonObject { property bool enable: true @@ -214,6 +212,11 @@ Singleton { property bool autoKillTrays: false } + property JsonObject crosshair: JsonObject { + // Valorant crosshair format. Use https://www.vcrdb.net/builder + property string code: "0;P;d;1;0l;10;0o;2;1b;0" + } + property JsonObject dock: JsonObject { property bool enable: false property bool monochromeIcons: true @@ -256,6 +259,16 @@ Singleton { } } + property JsonObject lock: JsonObject { + property JsonObject blur: JsonObject { + property bool enable: false + property real radius: 100 + property real extraZoom: 1.1 + } + property bool centerClock: true + property bool showLockedText: true + } + property JsonObject media: JsonObject { // Attempt to remove dupes (the aggregator playerctl one and browsers' native ones when there's plasma browser integration) property bool filterDuplicatePlayers: true @@ -265,6 +278,10 @@ Singleton { property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" } + property JsonObject notifications: JsonObject { + property int timeout: 7000 + } + property JsonObject osd: JsonObject { property int timeout: 1000 } @@ -355,6 +372,18 @@ Singleton { property JsonObject screenshotTool: JsonObject { property bool showContentRegions: true } + + property JsonObject workSafety: JsonObject { + property JsonObject enable: JsonObject { + property bool wallpaper: true + property bool clipboard: true + } + property JsonObject triggerCondition: JsonObject { + property list networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"] + property list fileKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] + property list linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"] + } + } } } } diff --git a/.config/quickshell/ii/modules/common/functions/FileUtils.qml b/.config/quickshell/ii/modules/common/functions/FileUtils.qml index 4ed8d8cb1..e7f5e6100 100644 --- a/.config/quickshell/ii/modules/common/functions/FileUtils.qml +++ b/.config/quickshell/ii/modules/common/functions/FileUtils.qml @@ -10,7 +10,9 @@ Singleton { * @returns {string} */ function trimFileProtocol(str) { - return str.startsWith("file://") ? str.slice(7) : str; + let s = str; + if (typeof s !== "string") s = str.toString(); // Convert to string if it's an url or whatever + return s.startsWith("file://") ? s.slice(7) : s; } /** diff --git a/.config/quickshell/ii/modules/common/functions/StringUtils.qml b/.config/quickshell/ii/modules/common/functions/StringUtils.qml index ea5e42df9..d3158a46c 100644 --- a/.config/quickshell/ii/modules/common/functions/StringUtils.qml +++ b/.config/quickshell/ii/modules/common/functions/StringUtils.qml @@ -8,7 +8,7 @@ Singleton { * Formats a string according to the args that are passed inc * @param { string } str * @param {...any} args - * @returns + * @returns { string } */ function format(str, ...args) { return str.replace(/{(\d+)}/g, (match, index) => typeof args[index] !== 'undefined' ? args[index] : match); @@ -35,10 +35,10 @@ Singleton { } /** - * Escapes single quotes in shell commands - * @param { string } str - * @returns { string } - */ + * Escapes single quotes in shell commands + * @param { string } str + * @returns { string } + */ function shellSingleQuoteEscape(str) { return String(str) // .replace(/\\/g, '\\\\') @@ -48,6 +48,7 @@ Singleton { /** * Splits markdown blocks into three different types: text, think, and code. * @param { string } markdown + * @returns {Array<{type: "text" | "think" | "code", content: string, lang?: string, completed?: boolean}>} */ function splitMarkdownBlocks(markdown) { const regex = /```(\w+)?\n([\s\S]*?)```|([\s\S]*?)<\/think>/g; @@ -182,6 +183,11 @@ Singleton { return lines.join("\n"); } + /** + * Cleans up a music title by removing bracketed and special characters. + * @param { string } title + * @returns { string } + */ function cleanMusicTitle(title) { if (!title) return ""; @@ -198,6 +204,11 @@ Singleton { return title.trim(); } + /** + * Converts seconds to a friendly time string (e.g. 1:23 or 1:02:03). + * @param { number } seconds + * @returns { string } + */ function friendlyTimeForSeconds(seconds) { if (isNaN(seconds) || seconds < 0) return "0:00"; @@ -212,13 +223,38 @@ Singleton { } } + /** + * Escapes HTML special characters in a string. + * @param { string } str + * @returns { string } + */ function escapeHtml(str) { if (typeof str !== 'string') return str; return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); } + /** + * Cleans a cliphist entry by removing leading digits and tab. + * @param { string } str + * @returns { string } + */ function cleanCliphistEntry(str: string): string { return str.replace(/^\d+\t/, ""); } + + /** + * Checks if any substring in the list is contained in the string. + * @param { string } str + * @param { string[] } substrings + * @returns { boolean } + */ + function stringListContainsSubstring(str, substrings) { + for (let i = 0; i < substrings.length; ++i) { + if (str.includes(substrings[i])) { + return true; + } + } + return false; + } } diff --git a/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml b/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml new file mode 100644 index 000000000..6cc374c93 --- /dev/null +++ b/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml @@ -0,0 +1,27 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +/** + * Material color scheme adapted to a given color. It's incomplete but enough for what we need... + */ +QtObject { + id: root + required property color color + readonly property bool colorIsDark: color.hslLightness < 0.5 + + property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, root.color, (colorIsDark && Appearance.m3colors.darkmode) ? 0.6 : 0.5) + property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, root.color, 0.5) + property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, root.color, 0.5) + property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, root.color, 0.5) + property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, root.color, 0.5) + property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimary, root.color), root.color, 0.5) + property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, root.color), root.color, 0.3) + property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, root.color), root.color, 0.3) + property color colSecondary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colSecondary, root.color), root.color, 0.5) + property color colSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, root.color, 0.15) + property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, root.color, 0.3) + property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, root.color, 0.5) + property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, root.color), root.color, 0.5) + property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, root.color, 0.5) +} diff --git a/.config/quickshell/ii/modules/common/widgets/AddressBar.qml b/.config/quickshell/ii/modules/common/widgets/AddressBar.qml index c965a536d..bf1cf2305 100644 --- a/.config/quickshell/ii/modules/common/widgets/AddressBar.qml +++ b/.config/quickshell/ii/modules/common/widgets/AddressBar.qml @@ -113,7 +113,7 @@ Rectangle { } StyledToolTip { - content: Translation.tr("Edit directory") + text: Translation.tr("Edit directory") } } } diff --git a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml index e72954622..339cd4e6c 100644 --- a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml +++ b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml @@ -12,6 +12,8 @@ Rectangle { property string entry property real maxWidth property real maxHeight + property bool blur: false + property string blurText: "Image hidden" property string imageDecodePath: Directories.cliphistDecode property string imageDecodeFileName: `${entryNumber}` @@ -19,26 +21,25 @@ Rectangle { property string source property int entryNumber: { - if (!root.entry) return 0 - const match = root.entry.match(/^(\d+)\t/) - return match ? parseInt(match[1]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/^(\d+)\t/); + return match ? parseInt(match[1]) : 0; } property int imageWidth: { - if (!root.entry) return 0 - const match = root.entry.match(/(\d+)x(\d+)/) - return match ? parseInt(match[1]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/(\d+)x(\d+)/); + return match ? parseInt(match[1]) : 0; } property int imageHeight: { - if (!root.entry) return 0 - const match = root.entry.match(/(\d+)x(\d+)/) - return match ? parseInt(match[2]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/(\d+)x(\d+)/); + return match ? parseInt(match[2]) : 0; } property real scale: { - return Math.min( - root.maxWidth / imageWidth, - root.maxHeight / imageHeight, - 1 - ) + return Math.min(root.maxWidth / imageWidth, root.maxHeight / imageHeight, 1); } color: Appearance.colors.colLayer1 @@ -47,26 +48,33 @@ Rectangle { implicitWidth: imageWidth * scale Component.onCompleted: { - decodeImageProcess.running = true + decodeImageProcess.running = true; } Process { id: decodeImageProcess - command: ["bash", "-c", - `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'` - ] + command: ["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`] onExited: (exitCode, exitStatus) => { if (exitCode === 0) { - root.source = imageDecodeFilePath + root.source = imageDecodeFilePath; } else { - console.error("[CliphistImage] Failed to decode image for entry:", root.entry) - root.source = "" + console.error("[CliphistImage] Failed to decode image for entry:", root.entry); + root.source = ""; } } } Component.onDestruction: { - Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`]) + Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`]); + } + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: image.width + height: image.height + radius: root.radius + } } Image { @@ -82,15 +90,42 @@ Rectangle { height: root.imageHeight * root.scale sourceSize.width: width sourceSize.height: height + } - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: image.width - height: image.height - radius: root.radius + Loader { + id: blurLoader + active: root.blur + anchors.fill: image + sourceComponent: GaussianBlur { + source: image + radius: 35 + samples: radius * 2 + 1 + + Rectangle { + anchors.fill: parent + color: ColorUtils.transparentize(Appearance.colors.colLayer0, 0.5) + + Column { + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + } + MaterialSymbol { + visible: width <= image.width + anchors.horizontalCenter: parent.horizontalCenter + text: "visibility_off" + font.pixelSize: 28 + } + StyledText { + visible: width <= image.width + anchors.horizontalCenter: parent.horizontalCenter + text: root.blurText + color: Appearance.colors.colOnSurface + font.pixelSize: Appearance.font.pixelSize.smallie + } + } } } } } - diff --git a/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml b/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml index 2c9d7eabb..33fb84057 100644 --- a/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml +++ b/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml @@ -24,7 +24,6 @@ RowLayout { id: labelWidget Layout.fillWidth: true text: root.text - font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnSecondaryContainer } } diff --git a/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml b/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml index 9c0da8f98..349ee5737 100644 --- a/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml +++ b/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml @@ -7,8 +7,11 @@ import QtQuick.Controls RippleButton { id: root property string buttonIcon + Layout.fillWidth: true implicitHeight: contentItem.implicitHeight + 8 * 2 + font.pixelSize: Appearance.font.pixelSize.small + onClicked: checked = !checked contentItem: RowLayout { @@ -21,7 +24,7 @@ RippleButton { id: labelWidget Layout.fillWidth: true text: root.text - font.pixelSize: Appearance.font.pixelSize.small + font: root.font color: Appearance.colors.colOnSecondaryContainer } StyledSwitch { diff --git a/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml b/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml index b78f3aa81..6c3824e08 100644 --- a/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml +++ b/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml @@ -32,7 +32,7 @@ ColumnLayout { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: infoMouseArea.containsMouse - content: root.tooltip + text: root.tooltip } } } diff --git a/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml b/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml new file mode 100644 index 000000000..3f85cb294 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQuick.Shapes +import Quickshell + +Item { + id: root + + property int sides: 12 + property int implicitSize: 100 + property real amplitude: implicitSize / 50 + property int renderPoints: 360 + property color color: "#605790" + property alias strokeWidth: shapePath.strokeWidth + + implicitWidth: implicitSize + implicitHeight: implicitSize + + Shape { + id: shape + anchors.fill: parent + preferredRendererType: Shape.CurveRenderer + + ShapePath { + id: shapePath + strokeWidth: 0 + fillColor: root.color + pathHints: ShapePath.PathSolid & ShapePath.PathNonIntersecting + + PathPolyline { + property var pointsList: { + var points = [] + var cx = shape.width / 2 // center x + var cy = shape.height / 2 // center y + var steps = root.renderPoints + var radius = root.implicitSize / 2 - root.amplitude + for (var i = 0; i <= steps; i++) { + var angle = (i / steps) * 2 * Math.PI + var wave = Math.sin(angle * root.sides + Math.PI/2) * root.amplitude + var x = Math.cos(angle) * (radius + wave) + cx + var y = Math.sin(angle) * (radius + wave) + cy + points.push(Qt.point(x, y)) + } + return points + } + + path: pointsList + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml index 126845313..75ff77d9b 100644 --- a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml +++ b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml @@ -6,6 +6,7 @@ StyledText { property real iconSize: Appearance?.font.pixelSize.small ?? 16 property real fill: 0 property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping + renderType: fill !== 0 ? Text.CurveRendering : Text.NativeRendering font { hintingPreference: Font.PreferFullHinting family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded" diff --git a/.config/quickshell/ii/modules/common/NoticeBox.qml b/.config/quickshell/ii/modules/common/widgets/NoticeBox.qml similarity index 100% rename from .config/quickshell/ii/modules/common/NoticeBox.qml rename to .config/quickshell/ii/modules/common/widgets/NoticeBox.qml diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index d4aa00234..e0736bb10 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -215,7 +215,7 @@ MouseArea { // Notification group area altAction: () => { root.toggleExpanded() } StyledToolTip { - content: Translation.tr("Tip: right-clicking a group\nalso expands it") + text: Translation.tr("Tip: right-clicking a group\nalso expands it") } } } diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml index 82367db17..3aafdc400 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml @@ -4,6 +4,7 @@ import qs.services import qs.modules.common.functions import QtQuick import QtQuick.Layouts +import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Hyprland import Quickshell.Services.Notifications @@ -15,6 +16,7 @@ Item { // Notification item area property bool onlyNotification: false property real fontSize: Appearance.font.pixelSize.small property real padding: onlyNotification ? 0 : 8 + property real summaryElideRatio: 0.85 property real dragConfirmThreshold: 70 // Drag further to discard notification property real dismissOvershoot: notificationIcon.implicitWidth + 20 // Account for gaps and bouncy animations @@ -57,6 +59,12 @@ Item { // Notification item area destroyAnimation.running = true; } + TextMetrics { + id: summaryTextMetrics + font.pixelSize: root.fontSize + text: root.notificationObject.summary || "" + } + SequentialAnimation { // Drag finish animation id: destroyAnimation running: false @@ -163,9 +171,9 @@ Item { // Notification item area visible: !root.onlyNotification || !root.expanded Layout.fillWidth: true implicitHeight: summaryText.implicitHeight - // Layout.fillWidth: true StyledText { id: summaryText + Layout.fillWidth: summaryTextMetrics.width >= summaryRow.width * root.summaryElideRatio visible: !root.onlyNotification font.pixelSize: root.fontSize color: Appearance.colors.colOnLayer3 @@ -209,7 +217,7 @@ Item { // Notification item area textFormat: Text.RichText text: { return `` + - `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` + `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` } onLinkActivated: (link) => { @@ -220,91 +228,110 @@ Item { // Notification item area PointingHandLinkHover {} } - StyledFlickable { // Notification actions - id: actionsFlickable + Item { Layout.fillWidth: true - implicitHeight: actionRowLayout.implicitHeight - contentWidth: actionRowLayout.implicitWidth - clip: !onlyNotification + implicitWidth: actionsFlickable.implicitWidth + implicitHeight: actionsFlickable.implicitHeight - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on height { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on implicitHeight { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: actionsFlickable.width + height: actionsFlickable.height + radius: Appearance.rounding.small + } } - RowLayout { - id: actionRowLayout - Layout.alignment: Qt.AlignBottom + ScrollEdgeFade { + target: actionsFlickable + vertical: false + } - NotificationActionButton { - Layout.fillWidth: true - buttonText: Translation.tr("Close") - urgency: notificationObject.urgency - implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : - (contentItem.implicitWidth + leftPadding + rightPadding) + StyledFlickable { // Notification actions + id: actionsFlickable + anchors.fill: parent + implicitHeight: actionRowLayout.implicitHeight + contentWidth: actionRowLayout.implicitWidth - onClicked: { - root.destroyWithAnimation() - } - - contentItem: MaterialSymbol { - iconSize: Appearance.font.pixelSize.large - horizontalAlignment: Text.AlignHCenter - color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface - text: "close" - } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on height { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } - Repeater { - id: actionRepeater - model: notificationObject.actions + RowLayout { + id: actionRowLayout + Layout.alignment: Qt.AlignBottom + NotificationActionButton { Layout.fillWidth: true - buttonText: modelData.text + buttonText: Translation.tr("Close") urgency: notificationObject.urgency + implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : + (contentItem.implicitWidth + leftPadding + rightPadding) + onClicked: { - Notifications.attemptInvokeAction(notificationObject.notificationId, modelData.identifier); + root.destroyWithAnimation() } - } - } - NotificationActionButton { - Layout.fillWidth: true - urgency: notificationObject.urgency - implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : - (contentItem.implicitWidth + leftPadding + rightPadding) - - onClicked: { - Quickshell.clipboardText = notificationObject.body - copyIcon.text = "inventory" - copyIconTimer.restart() - } - - Timer { - id: copyIconTimer - interval: 1500 - repeat: false - onTriggered: { - copyIcon.text = "content_copy" + contentItem: MaterialSymbol { + iconSize: Appearance.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + color: (notificationObject.urgency == NotificationUrgency.Critical) ? + Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface + text: "close" } } - contentItem: MaterialSymbol { - id: copyIcon - iconSize: Appearance.font.pixelSize.large - horizontalAlignment: Text.AlignHCenter - color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface - text: "content_copy" + Repeater { + id: actionRepeater + model: notificationObject.actions + NotificationActionButton { + Layout.fillWidth: true + buttonText: modelData.text + urgency: notificationObject.urgency + onClicked: { + Notifications.attemptInvokeAction(notificationObject.notificationId, modelData.identifier); + } + } } + + NotificationActionButton { + Layout.fillWidth: true + urgency: notificationObject.urgency + implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : + (contentItem.implicitWidth + leftPadding + rightPadding) + + onClicked: { + Quickshell.clipboardText = notificationObject.body + copyIcon.text = "inventory" + copyIconTimer.restart() + } + + Timer { + id: copyIconTimer + interval: 1500 + repeat: false + onTriggered: { + copyIcon.text = "content_copy" + } + } + + contentItem: MaterialSymbol { + id: copyIcon + iconSize: Appearance.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + color: (notificationObject.urgency == NotificationUrgency.Critical) ? + Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface + text: "content_copy" + } + } + } - } } } diff --git a/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml b/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml new file mode 100644 index 000000000..741b4556c --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml @@ -0,0 +1,52 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell + +Item { + id: root + property string text: "" + property bool extraVisibleCondition: true + property bool alternativeVisibleCondition: false + property real horizontalPadding: 10 + property real verticalPadding: 5 + + property var anchorEdges: Edges.Top + property var anchorGravity: anchorEdges + + readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition + + Loader { + id: tooltipLoader + anchors.fill: parent + active: internalVisibleCondition + sourceComponent: PopupWindow { + visible: true + anchor { + window: root.QsWindow.window + item: root.parent + edges: root.anchorEdges + gravity: root.anchorGravity + } + mask: Region { + item: null + } + + color: "transparent" + implicitWidth: contentItem.implicitWidth + root.horizontalPadding * 2 + implicitHeight: contentItem.implicitHeight + root.verticalPadding * 2 + + StyledToolTipContent { + id: contentItem + anchors.centerIn: parent + text: root.text + shown: false + Component.onCompleted: shown = true + horizontalPadding: root.horizontalPadding + verticalPadding: root.verticalPadding + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 07e6c5318..498bfc1cb 100644 --- a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -63,7 +63,7 @@ Button { acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton onPressed: (event) => { if(event.button === Qt.RightButton) { - if (root.altAction) root.altAction(); + if (root.altAction) root.altAction(event); return; } if(event.button === Qt.MiddleButton) { diff --git a/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml b/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml new file mode 100644 index 000000000..5c19f818a --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml @@ -0,0 +1,59 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Item { + id: root + z: 99 + required property Item target + property real fadeSize: Appearance.m3colors.darkmode ? 40 : 20 + property color color: ColorUtils.transparentize(Appearance.colors.colShadow, Appearance.m3colors.darkmode ? 0 : 0.7) + property bool vertical: true + + anchors.fill: target + + EndGradient { + anchors { + top: parent.top + left: parent.left + right: vertical ? parent.right : undefined + bottom: vertical ? undefined : parent.bottom + } + shown: !(root.vertical ? root.target.atYBeginning : root.target.atXBeginning) + } + + EndGradient { + anchors { + bottom: parent.bottom + right: parent.right + left: vertical ? parent.left : undefined + top: vertical ? undefined : parent.top + } + shown: !(root.vertical ? root.target.atYEnd : root.target.atXEnd) + rotation: 180 + } + + component EndGradient: Rectangle { + required property bool shown + height: vertical ? root.fadeSize : parent.height + width: vertical ? parent.width : root.fadeSize + + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + + gradient: Gradient { + orientation: root.vertical ? Gradient.Vertical : Gradient.Horizontal + GradientStop { + position: 0.0 + color: root.color + } + GradientStop { + position: 1.0 + color: ColorUtils.transparentize(root.color) + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/StyledBlurEffect.qml b/.config/quickshell/ii/modules/common/widgets/StyledBlurEffect.qml similarity index 100% rename from .config/quickshell/ii/modules/common/StyledBlurEffect.qml rename to .config/quickshell/ii/modules/common/widgets/StyledBlurEffect.qml diff --git a/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml b/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml index 1f32326d9..10994ee00 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml @@ -50,4 +50,5 @@ Flickable { root.scrollTargetY = root.contentY; } } + } diff --git a/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml b/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml index a6a63b7b8..ac511ce57 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml @@ -10,7 +10,8 @@ import Quickshell.Services.Pipewire RadioButton { id: root - implicitHeight: contentItem.implicitHeight + 4 * 2 + padding: 4 + implicitHeight: contentItem.implicitHeight + padding * 2 property string description property color activeColor: Appearance?.colors.colPrimary ?? "#685496" property color inactiveColor: Appearance?.m3colors.m3onSurfaceVariant ?? "#45464F" diff --git a/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml b/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml index a3c842c4b..a0c9f7ba3 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml @@ -5,7 +5,7 @@ import qs.modules.common RectangularShadow { required property var target anchors.fill: target - radius: target.radius + radius: 20 blur: 0.9 * Appearance.sizes.elevationMargin offset: Qt.vector2d(0.0, 1.0) spread: 1 diff --git a/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml b/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml index e940f1a41..6f2fc2314 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml @@ -149,7 +149,7 @@ Slider { StyledToolTip { extraVisibleCondition: root.pressed - content: root.tooltipContent + text: root.tooltipContent } } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml b/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml index 813c9ed14..f0e40db40 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml @@ -6,55 +6,20 @@ import QtQuick.Layouts ToolTip { id: root - property string content property bool extraVisibleCondition: true property bool alternativeVisibleCondition: false - property bool internalVisibleCondition: { - const ans = (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition - return ans - } + readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition verticalPadding: 5 - horizontalPadding: 10 - opacity: internalVisibleCondition ? 1 : 0 - visible: opacity > 0 - - Behavior on opacity { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - + horizontalPadding: 10 background: null + + visible: internalVisibleCondition - contentItem: Item { - id: contentItemBackground - implicitWidth: tooltipTextObject.width + 2 * root.horizontalPadding - implicitHeight: tooltipTextObject.height + 2 * root.verticalPadding - - Rectangle { - id: backgroundRectangle - anchors.bottom: contentItemBackground.bottom - anchors.horizontalCenter: contentItemBackground.horizontalCenter - color: Appearance?.colors.colTooltip ?? "#3C4043" - radius: Appearance?.rounding.verysmall ?? 7 - width: internalVisibleCondition ? (tooltipTextObject.width + 2 * padding) : 0 - height: internalVisibleCondition ? (tooltipTextObject.height + 2 * padding) : 0 - clip: true - - Behavior on width { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on height { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - - StyledText { - id: tooltipTextObject - anchors.centerIn: parent - text: content - font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14 - font.hintingPreference: Font.PreferNoHinting // Prevent shaky text - color: Appearance?.colors.colOnTooltip ?? "#FFFFFF" - wrapMode: Text.Wrap - } - } + contentItem: StyledToolTipContent { + id: contentItem + text: root.text + shown: root.internalVisibleCondition + horizontalPadding: root.horizontalPadding + verticalPadding: root.verticalPadding } -} \ No newline at end of file +} diff --git a/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml new file mode 100644 index 000000000..9b08f9902 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml @@ -0,0 +1,52 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +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 // Prevent shaky text + color: Appearance?.colors.colOnTooltip ?? "#FFFFFF" + wrapMode: Text.Wrap + } + } +} + diff --git a/.config/quickshell/ii/modules/common/ThumbnailImage.qml b/.config/quickshell/ii/modules/common/widgets/ThumbnailImage.qml similarity index 100% rename from .config/quickshell/ii/modules/common/ThumbnailImage.qml rename to .config/quickshell/ii/modules/common/widgets/ThumbnailImage.qml diff --git a/.config/quickshell/ii/modules/common/widgets/notification_utils.js b/.config/quickshell/ii/modules/common/widgets/notification_utils.js index 9b151055c..7ab21c3bb 100644 --- a/.config/quickshell/ii/modules/common/widgets/notification_utils.js +++ b/.config/quickshell/ii/modules/common/widgets/notification_utils.js @@ -9,7 +9,7 @@ function findSuitableMaterialSymbol(summary = "") { const keywordsToTypes = { 'reboot': 'restart_alt', - 'recording': 'screen_record', + 'record': 'screen_record', 'battery': 'power', 'power': 'power', 'screenshot': 'screenshot_monitor', @@ -21,7 +21,7 @@ function findSuitableMaterialSymbol(summary = "") { 'update': 'update', 'ai response': 'neurology', 'control': 'settings', - 'upscale': 'compare', + 'upsca': 'compare', 'install': 'deployed_code_update', 'startswith:file': 'folder_copy', // Declarative startsWith check }; diff --git a/.config/quickshell/ii/modules/crosshair/Crosshair.qml b/.config/quickshell/ii/modules/crosshair/Crosshair.qml new file mode 100644 index 000000000..31c25f0dc --- /dev/null +++ b/.config/quickshell/ii/modules/crosshair/Crosshair.qml @@ -0,0 +1,57 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + + Loader { + id: crosshairLoader + active: GlobalStates.crosshairOpen + sourceComponent: PanelWindow { + id: crosshairWindow + exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "quickshell:crosshair" + WlrLayershell.layer: WlrLayer.Overlay + visible: true + color: "transparent" + + mask: Region { // Crosshair should not block mouse input + item: null + } + + implicitWidth: crosshairContent.implicitWidth + implicitHeight: crosshairContent.implicitHeight + + CrosshairContent { + id: crosshairContent + anchors.centerIn: parent + } + } + } + + IpcHandler { + target: "sidebarRight" + + function toggle(): void { + GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; + } + } + + GlobalShortcut { + name: "crosshairToggle" + description: "Toggles crosshair on press" + + onPressed: { + GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; + } + } +} diff --git a/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml b/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml new file mode 100644 index 000000000..668a90867 --- /dev/null +++ b/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml @@ -0,0 +1,197 @@ +pragma ComponentBehavior: Bound +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Item { + id: root + + // Keys to props + // f, 0f, 1f, m are irrelevant as they're firing error stuff + // 0 is irrelevant because it's some profile stuff + property var propertyMap: ({ + "c": "color", + "u": "colorCode", + "h": "outline", + "o": "outlineOpacity", + "t": "outlineThickness", + "d": "centerDot", + "a": "centerDotOpacity", + "z": "centerDotSize", + "0a": "innerLineOpacity", + "0l": "innerLineLength", + "0v": "innerLineVerticalLength", + "0g": "innerLineUnbindAxesLengths", + "0t": "innerLineThickness", + "0o": "innerLineOffset", + "1b": "outerLines", + "1a": "outerLineOpacity", + "1l": "outerLineLength", + "1v": "outerLineVerticalLength", + "1g": "outerLineUnbindAxesLengths", + "1t": "outerLineThickness", + "1o": "outerLineOffset", + }) + property var colorMap: ({ + 0: "#FFFFFF", + 1: "#00FF00", + 2: "#7FFF00", + 3: "#DFFF00", + 4: "#FFFF00", + 5: "#00FFFF", + 6: "#FF00FF", + 7: "#FF0000" + }) + + // Raw props + property int color: 0 + property string colorCode: "#FFFFFF" + property bool outline: true + property real outlineOpacity: 0.5 + property int outlineThickness: 1 + property bool centerDot: false + property real centerDotOpacity: 1 + property int centerDotSize: 2 + property bool innerLines: true + property real innerLineOpacity: 0.8 + property int innerLineLength: 6 + property int innerLineVerticalLength: innerLineLength + property bool innerLineUnbindAxesLengths: false + property int innerLineThickness: 2 + property int innerLineOffset: 3 + property bool outerLines: true + property real outerLineOpacity: 0.35 + property int outerLineLength: 2 + property int outerLineVerticalLength: outerLineLength + property bool outerLineUnbindAxesLengths: false + property int outerLineThickness: 2 + property int outerLineOffset: 10 + property string defaultCode: "c;0;u;FFFFFF;h;1;o;0.5;t;1;d;0;a;1;z;2;0a;0.8;0l;6;0v;6;0g;0;0t;2;0o;3;1b;1;1a;0.35;1l;2;1v;2;1g;0;1t;2;1o;10" + + function loadFromCode(code: string): void { + let args = code.split(";"); + for (let i = 0; i < args.length; i+= 2) { + let key = args[i]; + let value = args[i+1]; + let targetKey = root.propertyMap[key]; + let targetType = typeof root[targetKey]; + + if (targetKey === undefined) continue; + + if (targetType === "number") { + value = parseFloat(value); + } else if (targetType === "boolean") { + value = (value === "1"); + } + if (targetKey === "colorCode") { + value = "#" + value.slice(0, 6); + } + root[targetKey] = value; + } + + if (!root.innerLineUnbindAxesLengths) { + root.innerLineVerticalLength = root.innerLineLength; + } + if (!root.outerLineUnbindAxesLengths) { + root.outerLineVerticalLength = root.outerLineLength; + } + + } + + // Update values from code + property var code: Config.options.crosshair.code + Component.onCompleted: reloadFromCode(); + onCodeChanged: reloadFromCode(); + function reloadFromCode() { + root.loadFromCode(root.defaultCode); + root.loadFromCode(root.code); + } + + // Aggregated props + property color crosshairColor: { + if (colorMap[color] !== undefined) return root.colorMap[color]; + if (color === 8) return colorCode; + return "#FFFFFF"; + } + property int borderWidth: outline ? outlineThickness : 0 + property color borderColor: ColorUtils.transparentize("black", 1 - root.outlineOpacity) + property color innerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.innerLineOpacity) + property color outerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.outerLineOpacity) + property int innerLineTotalOffset: root.centerDotSize / 2 + 1 + root.innerLineOffset + property int outerLineTotalOffset: root.centerDotSize / 2 + 1 + root.outerLineOffset + property real centerDotTotalSize: root.centerDotSize + root.borderWidth * 2 + property real innerLineTotalSize: (innerLineTotalOffset + root.innerLineLength + root.borderWidth) * 2 + property real outerLineTotalSize: (outerLineTotalOffset + root.outerLineLength + root.borderWidth) * 2 + implicitWidth: Math.max(centerDotTotalSize, innerLineTotalSize, outerLineTotalSize) + 2 // 2 for pixel correction + implicitHeight: implicitWidth + // width: implicitWidth + // height: implicitHeight + + Rectangle { + id: centerDot + visible: root.centerDot + anchors.centerIn: parent + + color: root.crosshairColor + opacity: root.centerDotOpacity + width: centerDotTotalSize + height: width + + border.width: root.borderWidth + border.color: root.borderColor + } + + Repeater { + id: innerLines + model: 4 + Item { + id: innerHair + z: index % 2 // Vertical lines above horizontal lines + required property int index + property int pixelCorrection: (root.innerLineThickness % 2 === 1 && index > 1) ? 1 : 0 + property int hairLength: (innerHair.index % 2 === 0 ? root.innerLineLength : root.innerLineVerticalLength) + visible: root.innerLines && hairLength > 0 + anchors.fill: parent + rotation: index * 90 + Rectangle { + x: parent.width / 2 + root.innerLineTotalOffset - root.borderWidth + innerHair.pixelCorrection + y: parent.height / 2 - height / 2 + + color: root.innerLineColor + width: innerHair.hairLength + root.borderWidth * 2 + height: root.innerLineThickness + root.borderWidth * 2 + + border.width: root.borderWidth + border.color: root.borderColor + } + } + } + + Repeater { + id: outerLines + model: 4 + Item { + id: outerHair + z: index % 2 + 2 // Vertical lines above horizontal lines, above inner lines + required property int index + property int pixelCorrection: (root.outerLineThickness % 2 === 1 && index > 1) ? 1 : 0 + property int hairLength: (outerHair.index % 2 === 0 ? root.outerLineLength : root.outerLineVerticalLength) + visible: root.outerLines && hairLength > 0 + anchors.fill: parent + rotation: index * 90 + Rectangle { + x: parent.width / 2 + root.outerLineTotalOffset - root.borderWidth + outerHair.pixelCorrection + y: parent.height / 2 - height / 2 + + color: root.outerLineColor + width: hairLength + root.borderWidth * 2 + height: root.outerLineThickness + root.borderWidth * 2 + + border.width: root.borderWidth + border.color: root.borderColor + } + } + } + + +} diff --git a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml index 33114fb55..5be82f09e 100644 --- a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.services import qs.modules.common.functions @@ -46,9 +47,9 @@ Item { // Player instance } } - Timer { // Force update for prevision + Timer { // Force update for revision running: playerController.player?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: { playerController.player.positionChanged() @@ -82,22 +83,8 @@ Item { // Player instance rescaleSize: 1 // Rescale to 1x1 pixel for faster processing } - property bool backgroundIsDark: artDominantColor.hslLightness < 0.5 - property QtObject blendedColors: QtObject { - property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, (backgroundIsDark && Appearance.m3colors.darkmode) ? 0.6 : 0.5) - property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) - property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.5) - property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) - property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) - property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimary, artDominantColor), artDominantColor, 0.5) - property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) - property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) - property color colSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.15) - property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) - property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.5) - property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5) - property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.5) - + property QtObject blendedColors: AdaptedMaterialScheme { + color: artDominantColor } StyledRectangularShadow { diff --git a/.config/quickshell/ii/modules/overview/OverviewWidget.qml b/.config/quickshell/ii/modules/overview/OverviewWidget.qml index 2510a5642..05bf29e82 100644 --- a/.config/quickshell/ii/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/ii/modules/overview/OverviewWidget.qml @@ -32,7 +32,7 @@ Item { ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) property real workspaceNumberMargin: 80 - property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale + property real workspaceNumberSize: 250 * monitor.scale property int workspaceZ: 0 property int windowZ: 1 property int windowDraggingZ: 99999 @@ -97,8 +97,11 @@ Item { StyledText { anchors.centerIn: parent text: workspaceValue - font.pixelSize: root.workspaceNumberSize * root.scale - font.weight: Font.DemiBold + font { + pixelSize: root.workspaceNumberSize * root.scale + weight: Font.DemiBold + family: Appearance.font.family.expressive + } color: ColorUtils.transparentize(Appearance.colors.colOnLayer1, 0.8) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -146,7 +149,7 @@ Item { values: { // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2)) return ToplevelManager.toplevels.values.filter((toplevel) => { - const address = `0x${toplevel.HyprlandToplevel.address}` + const address = `0x${toplevel.HyprlandToplevel?.address}` var win = windowByAddress[address] const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) return inWorkspaceGroup; @@ -235,7 +238,7 @@ Item { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active - content: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` + text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}` } } } diff --git a/.config/quickshell/ii/modules/overview/SearchItem.qml b/.config/quickshell/ii/modules/overview/SearchItem.qml index 2e0afbfe8..46b470de6 100644 --- a/.config/quickshell/ii/modules/overview/SearchItem.qml +++ b/.config/quickshell/ii/modules/overview/SearchItem.qml @@ -16,14 +16,16 @@ RippleButton { property string query property bool entryShown: entry?.shown ?? true property string itemType: entry?.type ?? Translation.tr("App") - property string itemName: entry?.name + property string itemName: entry?.name ?? "" property string itemIcon: entry?.icon ?? "" property var itemExecute: entry?.execute property string fontType: entry?.fontType ?? "main" - property string itemClickActionName: entry?.clickActionName + property string itemClickActionName: entry?.clickActionName ?? "Open" property string bigText: entry?.bigText ?? "" property string materialSymbol: entry?.materialSymbol ?? "" property string cliphistRawString: entry?.cliphistRawString ?? "" + property bool blurImage: entry?.blurImage ?? false + property string blurImageText: entry?.blurImageText ?? "Image hidden" visible: root.entryShown property int horizontalMargin: 10 @@ -208,6 +210,8 @@ RippleButton { entry: root.cliphistRawString maxWidth: contentColumn.width maxHeight: 140 + blur: root.blurImage + blurText: root.blurImageText } } } @@ -233,8 +237,8 @@ RippleButton { delegate: RippleButton { id: actionButton required property var modelData - property string iconName: modelData.icon - property string materialIconName: modelData.materialIcon + property string iconName: modelData.icon ?? "" + property string materialIconName: modelData.materialIcon ?? "" implicitHeight: 34 implicitWidth: 34 @@ -246,7 +250,7 @@ RippleButton { anchors.centerIn: parent Loader { anchors.centerIn: parent - active: !(actionButton.iconName && actionButton.iconName !== "") || actionButton.materialIconName + active: !(actionButton.iconName !== "") || actionButton.materialIconName sourceComponent: MaterialSymbol { text: actionButton.materialIconName || "video_settings" font.pixelSize: Appearance.font.pixelSize.hugeass @@ -255,7 +259,7 @@ RippleButton { } Loader { anchors.centerIn: parent - active: !actionButton.materialIconName && actionButton.iconName && actionButton.iconName !== "" + active: actionButton.materialIconName.length == 0 && actionButton.iconName && actionButton.iconName !== "" sourceComponent: IconImage { source: Quickshell.iconPath(actionButton.iconName) implicitSize: 20 @@ -266,7 +270,7 @@ RippleButton { onClicked: modelData.execute() StyledToolTip { - content: modelData.name + text: modelData.name } } } diff --git a/.config/quickshell/ii/modules/overview/SearchWidget.qml b/.config/quickshell/ii/modules/overview/SearchWidget.qml index 85b7d58cb..c857c0676 100644 --- a/.config/quickshell/ii/modules/overview/SearchWidget.qml +++ b/.config/quickshell/ii/modules/overview/SearchWidget.qml @@ -20,20 +20,10 @@ Item { // Wrapper implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2 property string mathResult: "" - - function disableExpandAnimation() { - searchWidthBehavior.enabled = false; - } - - function cancelSearch() { - searchInput.selectAll(); - root.searchingText = ""; - searchWidthBehavior.enabled = true; - } - - function setSearchingText(text) { - searchInput.text = text; - root.searchingText = text; + property bool clipboardWorkSafetyActive: { + const enabled = Config.options.workSafety.enable.clipboard; + const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveNetwork; } property var searchActions: [ @@ -97,6 +87,27 @@ Item { // Wrapper appResults.currentIndex = 0; } + function disableExpandAnimation() { + searchWidthBehavior.enabled = false; + } + + function cancelSearch() { + searchInput.selectAll(); + root.searchingText = ""; + searchWidthBehavior.enabled = true; + } + + function setSearchingText(text) { + searchInput.text = text; + root.searchingText = text; + } + + function containsUnsafeLink(entry) { + if (entry == undefined) return false; + const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; + return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); + } + Timer { id: nonAppResultsTimer interval: Config.options.search.nonAppResultDelay @@ -311,7 +322,12 @@ Item { // Wrapper if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) { // Clipboard const searchString = root.searchingText.slice(Config.options.search.prefix.clipboard.length); - return Cliphist.fuzzyQuery(searchString).map(entry => { + return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { + const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; + let shouldBlurImage = mightBlurImage; + if (mightBlurImage) { + shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1])); + } return { cliphistRawString: entry, name: StringUtils.cleanCliphistEntry(entry), @@ -335,7 +351,9 @@ Item { // Wrapper Cliphist.deleteEntry(entry); } } - ] + ], + blurImage: shouldBlurImage, + blurImageText: Translation.tr("Work safety") }; }).filter(Boolean); } diff --git a/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml b/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml index 199f2abed..000c4e201 100644 --- a/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml +++ b/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml @@ -52,7 +52,7 @@ RippleButton { } StyledToolTip { - content: buttonText + text: buttonText } } diff --git a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml index 9a8aa2a3b..1a400cddf 100644 --- a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml +++ b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml @@ -27,7 +27,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.enableQtApps = checked; } StyledToolTip { - content: Translation.tr("Shell & utilities theming must also be enabled") + text: Translation.tr("Shell & utilities theming must also be enabled") } } ConfigSwitch { @@ -38,7 +38,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.enableTerminal = checked; } StyledToolTip { - content: Translation.tr("Shell & utilities theming must also be enabled") + text: Translation.tr("Shell & utilities theming must also be enabled") } } ConfigRow { @@ -51,7 +51,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.terminalGenerationProps.forceDarkMode= checked; } StyledToolTip { - content: Translation.tr("Ignored if terminal theming is not enabled") + text: Translation.tr("Ignored if terminal theming is not enabled") } } } diff --git a/.config/quickshell/ii/modules/settings/GeneralConfig.qml b/.config/quickshell/ii/modules/settings/GeneralConfig.qml index e520dab0b..5ba068f10 100644 --- a/.config/quickshell/ii/modules/settings/GeneralConfig.qml +++ b/.config/quickshell/ii/modules/settings/GeneralConfig.qml @@ -21,7 +21,7 @@ ContentPage { Config.options.audio.protection.enable = checked; } StyledToolTip { - content: Translation.tr("Prevents abrupt increments and restricts volume limit") + text: Translation.tr("Prevents abrupt increments and restricts volume limit") } } ConfigRow { @@ -85,7 +85,7 @@ ContentPage { Config.options.battery.automaticSuspend = checked; } StyledToolTip { - content: Translation.tr("Automatically suspends the system when battery is low") + text: Translation.tr("Automatically suspends the system when battery is low") } } ConfigSpinBox { diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 57074a34f..e79a40779 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -14,9 +14,42 @@ ContentPage { ConfigSwitch { text: Translation.tr("Show clock") - checked: Config.options.background.showClock + checked: Config.options.background.clock.show onCheckedChanged: { - Config.options.background.showClock = checked; + Config.options.background.clock.show = checked; + } + } + + ConfigSpinBox { + text: Translation.tr("Scale (%)") + value: Config.options.background.clock.scale * 100 + from: 1 + to: 200 + stepSize: 2 + onValueChanged: { + Config.options.background.clock.scale = value / 100; + } + } + + ContentSubsection { + title: Translation.tr("Clock style") + ConfigSelectionArray { + currentValue: Config.options.background.clock.style + onSelected: newValue => { + Config.options.background.clock.style = newValue; + } + options: [ + { + displayName: Translation.tr("Simple digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Material cookie"), + icon: "cookie", + value: "cookie" + }, + ] } } @@ -61,6 +94,37 @@ ContentPage { } } + ContentSection { + icon: "point_scan" + title: Translation.tr("Crosshair") + + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Crosshair code (in Valorant's format)") + text: Config.options.crosshair.code + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.crosshair.code = text; + } + } + + RowLayout { + Item { Layout.fillWidth: true } + RippleButtonWithIcon { + id: editorButton + buttonRadius: Appearance.rounding.full + materialIcon: "open_in_new" + mainText: Translation.tr("Open editor") + onClicked: { + Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`); + } + StyledToolTip { + text: "www.vcrdb.net" + } + } + } + } + ContentSection { icon: "call_to_action" title: Translation.tr("Dock") @@ -99,6 +163,68 @@ ContentPage { } } + ContentSection { + icon: "lock" + title: Translation.tr("Lock screen") + + ContentSubsection { + title: Translation.tr("Blurred style") + + ConfigSwitch { + text: Translation.tr('Enable blur') + checked: Config.options.lock.blur.enable + onCheckedChanged: { + Config.options.lock.blur.enable = checked; + } + } + + ConfigSpinBox { + text: Translation.tr("Blur: Extra zoom (%)") + value: Config.options.lock.blur.extraZoom * 100 + from: 1 + to: 150 + stepSize: 2 + onValueChanged: { + Config.options.lock.blur.extraZoom = value / 100; + } + } + + ConfigSwitch { + text: Translation.tr('Center clock') + checked: Config.options.lock.centerClock + onCheckedChanged: { + Config.options.lock.centerClock = checked; + } + } + + ConfigSwitch { + text: Translation.tr('Show "Locked" text') + checked: Config.options.lock.showLockedText + onCheckedChanged: { + Config.options.lock.showLockedText = checked; + } + } + + + } + } + + ContentSection { + icon: "notifications" + title: Translation.tr("Notifications") + + ConfigSpinBox { + text: Translation.tr("Timeout duration (if not defined by notification) (ms)") + value: Config.options.notifications.timeout + from: 1000 + to: 60000 + stepSize: 1000 + onValueChanged: { + Config.options.notifications.timeout = value; + } + } + } + ContentSection { icon: "side_navigation" title: Translation.tr("Sidebars") @@ -110,7 +236,7 @@ ContentPage { Config.options.sidebar.keepRightSidebarLoaded = checked; } StyledToolTip { - content: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help") + text: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help") } } @@ -134,7 +260,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("When this is off you'll have to click") + text: Translation.tr("When this is off you'll have to click") } } } @@ -148,7 +274,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("Place the corners to trigger at the bottom") + text: Translation.tr("Place the corners to trigger at the bottom") } } ConfigSwitch { @@ -159,7 +285,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("Brightness and volume") + text: Translation.tr("Brightness and volume") } } } @@ -268,7 +394,7 @@ ContentPage { Config.options.screenshotTool.showContentRegions = checked; } StyledToolTip { - content: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.") + text: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.") } } } diff --git a/.config/quickshell/ii/modules/settings/QuickConfig.qml b/.config/quickshell/ii/modules/settings/QuickConfig.qml index d9a0a09cb..ca8a0f73d 100644 --- a/.config/quickshell/ii/modules/settings/QuickConfig.qml +++ b/.config/quickshell/ii/modules/settings/QuickConfig.qml @@ -76,6 +76,7 @@ ContentPage { sourceSize.height: parent.implicitHeight fillMode: Image.PreserveAspectCrop source: Config.options.background.wallpaperPath + cache: false layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { @@ -93,20 +94,20 @@ ContentPage { visible: Config.options.policies.weeb === 1 Layout.fillWidth: true buttonRadius: Appearance.rounding.small - materialIcon: "wallpaper" + materialIcon: "ifl" mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { konachanWallProc.running = true; } StyledToolTip { - content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") + text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { Layout.fillWidth: true materialIcon: "wallpaper" StyledToolTip { - content: Translation.tr("Pick wallpaper image on your system") + text: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`); @@ -161,7 +162,7 @@ ContentPage { Config.options.appearance.transparency.enable = checked; } StyledToolTip { - content: Translation.tr("Might look ass. Unsupported.") + text: Translation.tr("Might look ass. Unsupported.") } } } diff --git a/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/.config/quickshell/ii/modules/settings/ServicesConfig.qml index f28eab31a..dc7817cab 100644 --- a/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -69,7 +69,7 @@ ContentPage { Config.options.search.sloppy = checked; } StyledToolTip { - content: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)") + text: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)") } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 22a64412c..b746e9482 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -209,6 +209,9 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) else { Ai.sendUserMessage(inputText); } + + // Always scroll to bottom when user sends a message + messageListView.positionViewAtEnd() } Process { @@ -257,7 +260,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } StyledToolTip { - content: statusItem.description + text: statusItem.description extraVisibleCondition: false alternativeVisibleCondition: statusItem.containsMouse } @@ -305,6 +308,20 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) Item { // Messages Layout.fillWidth: true Layout.fillHeight: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } + } + + ScrollEdgeFade { + target: messageListView + vertical: true + } + StyledListView { // Message list id: messageListView anchors.fill: parent @@ -315,15 +332,14 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4 property int lastResponseLength: 0 + property bool shouldAutoScroll: true - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small - } + onContentYChanged: shouldAutoScroll = atYEnd + onContentHeightChanged: { + if (shouldAutoScroll) positionViewAtEnd(); + } + onCountChanged: { // Auto-scroll when new messages are added + if (shouldAutoScroll) positionViewAtEnd(); } add: null // Prevent function calls from being janky diff --git a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml index 9e7bfb480..19b930958 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml @@ -141,6 +141,21 @@ Item { Item { Layout.fillWidth: true Layout.fillHeight: true + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } + } + + ScrollEdgeFade { + target: booruResponseListView + vertical: true + } + StyledListView { // Booru responses id: booruResponseListView anchors.fill: parent @@ -151,16 +166,6 @@ Item { property int lastResponseLength: 0 - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small - } - } - model: ScriptModel { values: { if(root.responses.length > booruResponseListView.lastResponseLength) { diff --git a/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml b/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml index 878fba2b6..5cd969a24 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml @@ -41,7 +41,7 @@ Item { // Model indicator id: toolTip extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered - content: root.tooltipText + text: root.tooltipText } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/Translator.qml b/.config/quickshell/ii/modules/sidebarLeft/Translator.qml index e9e3d6fe9..b56681fb2 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/Translator.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/Translator.qml @@ -101,7 +101,7 @@ Item { ColumnLayout { anchors.fill: parent - Flickable { + StyledFlickable { Layout.fillWidth: true Layout.fillHeight: true contentHeight: contentColumn.implicitHeight diff --git a/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml b/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml index 87bfba710..2072a67ae 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml @@ -164,7 +164,7 @@ Rectangle { text: "visibility_off" } StyledToolTip { - content: Translation.tr("Not visible to model") + text: Translation.tr("Not visible to model") } } @@ -191,7 +191,7 @@ Rectangle { } StyledToolTip { - content: Translation.tr("Copy") + text: Translation.tr("Copy") } } AiMessageControlButton { @@ -206,7 +206,7 @@ Rectangle { } } StyledToolTip { - content: root.editing ? Translation.tr("Save") : Translation.tr("Edit") + text: root.editing ? Translation.tr("Save") : Translation.tr("Edit") } } AiMessageControlButton { @@ -217,7 +217,7 @@ Rectangle { root.renderMarkdown = !root.renderMarkdown } StyledToolTip { - content: Translation.tr("View Markdown source") + text: Translation.tr("View Markdown source") } } AiMessageControlButton { @@ -227,7 +227,7 @@ Rectangle { Ai.removeMessage(root.messageIndex) } StyledToolTip { - content: Translation.tr("Delete") + text: Translation.tr("Delete") } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml index f8b0bac3e..dbe31d45d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml @@ -84,7 +84,7 @@ ColumnLayout { } } StyledToolTip { - content: Translation.tr("Copy code") + text: Translation.tr("Copy code") } } AiMessageControlButton { @@ -114,7 +114,7 @@ ColumnLayout { } } StyledToolTip { - content: Translation.tr("Save to Downloads") + text: Translation.tr("Save to Downloads") } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml index b2eef2939..8df196d9a 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml @@ -41,7 +41,7 @@ Button { } StyledToolTip { - content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}` + text: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}` } padding: 0 diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml index 9e021e584..cfb2a7f9d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml @@ -105,7 +105,6 @@ Rectangle { return true } implicitHeight: tagRowLayout.implicitHeight - // height: tagRowLayout.implicitHeight contentWidth: tagRowLayout.implicitWidth clip: true @@ -118,9 +117,6 @@ Rectangle { } } - Behavior on height { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } Behavior on implicitHeight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml index 4147c3abe..cdc861ce6 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml @@ -1,12 +1,7 @@ import qs import qs.services import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell import Quickshell.Wayland diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml index 636d57729..ed2e4376f 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml @@ -88,7 +88,7 @@ Item { Quickshell.reload(true); } StyledToolTip { - content: Translation.tr("Reload Hyprland & Quickshell") + text: Translation.tr("Reload Hyprland & Quickshell") } } QuickToggleButton { @@ -99,7 +99,7 @@ Item { Quickshell.execDetached(["qs", "-p", root.settingsQmlPath]); } StyledToolTip { - content: Translation.tr("Settings") + text: Translation.tr("Settings") } } QuickToggleButton { @@ -109,7 +109,7 @@ Item { GlobalStates.sessionOpen = true; } StyledToolTip { - content: Translation.tr("Session") + text: Translation.tr("Session") } } } diff --git a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml index 6b5e5aa13..37a45db08 100644 --- a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml +++ b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml @@ -30,7 +30,7 @@ RippleButton { } StyledToolTip { - content: tooltipText + text: tooltipText extraVisibleCondition: tooltipText.length > 0 } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml index 1a4c053ed..af8a5cfc8 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -21,7 +21,7 @@ QuickToggleButton { GlobalStates.sidebarRightOpen = false } StyledToolTip { - content: Translation.tr("%1 | Right-click to configure").arg( + text: Translation.tr("%1 | Right-click to configure").arg( (BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("Bluetooth")) + (BluetoothStatus.activeDeviceCount > 1 ? ` +${BluetoothStatus.activeDeviceCount - 1}` : "") ) diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml index 39416ab22..1aebffa9a 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml @@ -87,6 +87,6 @@ QuickToggleButton { } } StyledToolTip { - content: Translation.tr("Cloudflare WARP (1.1.1.1)") + text: Translation.tr("Cloudflare WARP (1.1.1.1)") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml index 10b866007..643764ff3 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml @@ -21,11 +21,11 @@ QuickToggleButton { } altAction: () => { - Quickshell.execDetached(["easyeffects"]) + Quickshell.execDetached(["bash", "-c", "flatpak run com.github.wwmm.easyeffects || easyeffects"]) GlobalStates.sidebarRightOpen = false } StyledToolTip { - content: Translation.tr("EasyEffects | Right-click to configure") + text: Translation.tr("EasyEffects | Right-click to configure") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml index 1907080e8..e2b03ebef 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml @@ -26,6 +26,6 @@ QuickToggleButton { } } StyledToolTip { - content: Translation.tr("Game mode") + text: Translation.tr("Game mode") } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml index 2b8f8b170..b6bce02f5 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml @@ -10,7 +10,7 @@ QuickToggleButton { Idle.toggleInhibit() } StyledToolTip { - content: Translation.tr("Keep system awake") + text: Translation.tr("Keep system awake") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml index cb9e0d8e7..bb7d0969c 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -18,6 +18,6 @@ QuickToggleButton { GlobalStates.sidebarRightOpen = false } StyledToolTip { - content: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) + text: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml index f0265126c..5fda831e1 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml @@ -23,6 +23,6 @@ QuickToggleButton { } StyledToolTip { - content: Translation.tr("Night Light | Right-click to toggle Auto mode") + text: Translation.tr("Night Light | Right-click to toggle Auto mode") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml b/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml index b0a6e7b9a..d2fe0abad 100644 --- a/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml +++ b/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml @@ -26,7 +26,7 @@ RippleButton { } StyledToolTip { - content: tooltipText + text: tooltipText extraVisibleCondition: tooltipText.length > 0 } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml index d23eb43be..3851f06d3 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml @@ -82,8 +82,8 @@ Scope { id: hoverMaskRegion anchors { fill: barContent - leftMargin: -1 - rightMargin: -1 + leftMargin: -Config.options.bar.autoHide.hoverRegionWidth + rightMargin: -Config.options.bar.autoHide.hoverRegionWidth } } diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml index bfcd0c6fb..612510720 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml @@ -269,16 +269,10 @@ Item { // Bar content region color: rightSidebarButton.colText } } - Loader { - active: HyprlandXkb.layoutCodes.length > 1 - visible: active + Bar.HyprlandXkbIndicator { + vertical: true + Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: indicatorsColumnLayout.realSpacing - sourceComponent: StyledText { - text: HyprlandXkb.currentLayoutCode - font.pixelSize: Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true - } } MaterialSymbol { Layout.bottomMargin: indicatorsColumnLayout.realSpacing diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml b/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml index f64792f2e..1be93d4a7 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml @@ -22,7 +22,7 @@ MouseArea { Timer { running: activePlayer?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: activePlayer.positionChanged() } diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml index 43ebc31c2..197acbf2e 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml @@ -71,11 +71,25 @@ Scope { function toggle(): void { root.toggleWallpaperSelector(); } + + function random(): void { + Wallpapers.randomFromCurrentFolder(); + } } GlobalShortcut { name: "wallpaperSelectorToggle" description: "Toggle wallpaper selector" - onPressed: root.toggleWallpaperSelector(); + onPressed: { + root.toggleWallpaperSelector(); + } + } + + GlobalShortcut { + name: "wallpaperSelectorRandom" + description: "Select random wallpaper in current folder" + onPressed: { + Wallpapers.randomFromCurrentFolder(); + } } } diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index bc25ce669..9d89dfd7d 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -40,6 +40,13 @@ MouseArea { } } + function selectWallpaperPath(filePath) { + if (filePath && filePath.length > 0) { + Wallpapers.select(filePath, root.useDarkMode); + filterField.text = ""; + } + } + acceptedButtons: Qt.BackButton | Qt.ForwardButton onPressed: event => { if (event.button === Qt.BackButton) { @@ -164,7 +171,7 @@ MouseArea { { icon: "movie", name: "Videos", path: Directories.videos }, { icon: "", name: "---", path: "INTENTIONALLY_INVALID_DIR" }, { icon: "wallpaper", name: "Wallpapers", path: `${Directories.pictures}/Wallpapers` }, - { icon: "favorite", name: "Homework", path: `${Directories.pictures}/homework` }, + ...(Config.options.policies.weeb === 1 ? [{ icon: "favorite", name: "Homework", path: `${Directories.pictures}/homework` }] : []), ] delegate: RippleButton { id: quickDirButton @@ -185,6 +192,7 @@ MouseArea { color: quickDirButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1 iconSize: Appearance.font.pixelSize.larger text: quickDirButton.modelData.icon + fill: quickDirButton.toggled ? 1 : 0 } StyledText { Layout.fillWidth: true @@ -267,8 +275,7 @@ MouseArea { function activateCurrent() { const filePath = grid.model.get(currentIndex, "filePath") - Wallpapers.select(filePath, root.useDarkMode); - filterField.text = ""; + root.selectWallpaperPath(filePath); } model: Wallpapers.folderModel @@ -287,8 +294,7 @@ MouseArea { } onActivated: { - Wallpapers.select(fileModelData.filePath, root.useDarkMode); - filterField.text = ""; + root.selectWallpaperPath(fileModelData.filePath); } } @@ -322,11 +328,29 @@ MouseArea { Config.options.wallpaperSelector.useSystemFileDialog = true } contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter text: "open_in_new" iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Use the system file picker instead\nRight-click to make this the default behavior") + text: Translation.tr("Use the system file picker instead\nRight-click to make this the default behavior") + } + } + + ToolbarButton { + implicitWidth: height + onClicked: { + Wallpapers.randomFromCurrentFolder(); + } + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: "ifl" + iconSize: Appearance.font.pixelSize.larger + } + StyledToolTip { + text: Translation.tr("Pick random from this folder") } } @@ -334,11 +358,13 @@ MouseArea { implicitWidth: height onClicked: root.useDarkMode = !root.useDarkMode contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter text: root.useDarkMode ? "dark_mode" : "light_mode" iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Click to toggle light/dark mode (applied when wallpaper is chosen)") + text: Translation.tr("Click to toggle light/dark mode\n(applied when wallpaper is chosen)") } } @@ -378,11 +404,18 @@ MouseArea { } ToolbarButton { + implicitWidth: height onClicked: { GlobalStates.wallpaperSelectorOpen = false; } - contentItem: StyledText { - text: "Cancel" + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: "cancel_presentation" + iconSize: Appearance.font.pixelSize.larger + } + StyledToolTip { + text: Translation.tr("Cancel wallpaper selection") } } } diff --git a/.config/quickshell/ii/services/Brightness.qml b/.config/quickshell/ii/services/Brightness.qml index 154bdffd4..42becc766 100644 --- a/.config/quickshell/ii/services/Brightness.qml +++ b/.config/quickshell/ii/services/Brightness.qml @@ -103,16 +103,27 @@ Singleton { } } - function setBrightness(value: real): void { - value = Math.max(0.01, Math.min(1, value)); - const rounded = Math.round(value * monitor.rawMaxBrightness); - if (Math.round(brightness * monitor.rawMaxBrightness) === rounded) - return; - brightness = value; + // We need a delay for DDC monitors because they can be quite slow and might act weird with rapid changes + property var setTimer: Timer { + id: setTimer + interval: monitor.isDdc ? 300 : 0 + onTriggered: { + syncBrightness(); + } + } + + function syncBrightness() { + const rounded = Math.round(monitor.brightness * monitor.rawMaxBrightness); setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", rounded, "--quiet"]; setProc.startDetached(); } + function setBrightness(value: real): void { + value = Math.max(0.01, Math.min(1, value)); + monitor.brightness = value; + setTimer.restart(); + } + Component.onCompleted: { initialize(); } diff --git a/.config/quickshell/ii/services/EasyEffects.qml b/.config/quickshell/ii/services/EasyEffects.qml index 8767a9d4e..4117e50c4 100644 --- a/.config/quickshell/ii/services/EasyEffects.qml +++ b/.config/quickshell/ii/services/EasyEffects.qml @@ -25,12 +25,12 @@ Singleton { function disable() { root.active = false - Quickshell.execDetached(["pkill", "easyeffects"]) + Quickshell.execDetached(["bash", "-c", "pkill easyeffects || flatpak pkill com.github.wwmm.easyeffects"]) } function enable() { root.active = true - Quickshell.execDetached(["easyeffects", "--gapplication-service"]) + Quickshell.execDetached(["bash", "-c", "easyeffects --gapplication-service || flatpak run com.github.wwmm.easyeffects --gapplication-service"]) } function toggle() { @@ -44,7 +44,7 @@ Singleton { Process { id: fetchAvailabilityProc running: true - command: ["bash", "-c", "command -v easyeffects"] + command: ["bash", "-c", "command -v easyeffects || flatpak info com.github.wwmm.easyeffects > /dev/null 2>&1"] onExited: (exitCode, exitStatus) => { root.available = exitCode === 0 } @@ -53,7 +53,7 @@ Singleton { Process { id: fetchActiveStateProc running: true - command: ["pidof", "easyeffects"] + command: ["bash", "-c", "pidof easyeffects || flatpak ps | grep com.github.wwmm.easyeffects > /dev/null 2>&1"] onExited: (exitCode, exitStatus) => { root.active = exitCode === 0 } diff --git a/.config/quickshell/ii/services/HyprlandXkb.qml b/.config/quickshell/ii/services/HyprlandXkb.qml index cace0d29e..1aa52ec54 100644 --- a/.config/quickshell/ii/services/HyprlandXkb.qml +++ b/.config/quickshell/ii/services/HyprlandXkb.qml @@ -46,13 +46,24 @@ Singleton { if (!line.trim() || line.trim().startsWith('!')) return false; - // Match: key + whitespace + description - const match = line.match(/^\s*(\S+)\s+(.+)$/); - if (match && match[2] === targetDescription) { - root.cachedLayoutCodes[match[2]] = match[1]; - root.currentLayoutCode = match[1]; + // Match layout: (whitespace + ) key + whitespace + description + const matchLayout = line.match(/^\s*(\S+)\s+(.+)$/); + if (matchLayout && matchLayout[2] === targetDescription) { + root.cachedLayoutCodes[matchLayout[2]] = matchLayout[1]; + root.currentLayoutCode = matchLayout[1]; return true; } + + // Match variant: (whitespace + ) variant + whitespace + key + whitespace + description + const matchVariant = line.match(/^\s*(\S+)\s+(\S+)\s+(.+)$/); + if (matchVariant && matchVariant[3] === targetDescription) { + const complexLayout = matchVariant[2] + matchVariant[1]; + root.cachedLayoutCodes[matchVariant[3]] = complexLayout; + root.currentLayoutCode = complexLayout; + return true; + } + + return false; }); // console.log("[HyprlandXkb] Found line:", foundLine); // console.log("[HyprlandXkb] Layout:", root.currentLayoutName, "| Code:", root.currentLayoutCode); diff --git a/.config/quickshell/ii/services/Notifications.qml b/.config/quickshell/ii/services/Notifications.qml index f1f279672..00a87c3a7 100644 --- a/.config/quickshell/ii/services/Notifications.qml +++ b/.config/quickshell/ii/services/Notifications.qml @@ -60,7 +60,7 @@ Singleton { component NotifTimer: Timer { required property int notificationId - interval: 5000 + interval: 7000 running: true onTriggered: () => { root.timeoutNotification(notificationId); @@ -168,7 +168,7 @@ Singleton { if (notification.expireTimeout != 0) { newNotifObject.timer = notifTimerComponent.createObject(root, { "notificationId": newNotifObject.notificationId, - "interval": notification.expireTimeout < 0 ? 5000 : notification.expireTimeout, + "interval": notification.expireTimeout < 0 ? (Config?.options.notifications.timeout ?? 7000) : notification.expireTimeout, }); } } diff --git a/.config/quickshell/ii/services/Wallpapers.qml b/.config/quickshell/ii/services/Wallpapers.qml index 4afffa1ff..8ee9b7dae 100644 --- a/.config/quickshell/ii/services/Wallpapers.qml +++ b/.config/quickshell/ii/services/Wallpapers.qml @@ -16,7 +16,7 @@ Singleton { id: root property string thumbgenScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/thumbgen.py` - property string generateThumbnailsMagicScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/generate-thumbnails-magick.sh` + property string generateThumbnailsMagickScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/generate-thumbnails-magick.sh` property alias directory: folderModel.folder readonly property string effectiveDirectory: FileUtils.trimFileProtocol(folderModel.folder.toString()) property url defaultFolder: Qt.resolvedUrl(`${Directories.pictures}/Wallpapers`) @@ -33,6 +33,8 @@ Singleton { signal thumbnailGenerated(directory: string) signal thumbnailGeneratedFile(filePath: string) + function load () {} // For forcing initialization + // Executions Process { id: applyProc @@ -77,6 +79,14 @@ Singleton { selectProc.select(filePath, darkMode); } + function randomFromCurrentFolder(darkMode = Appearance.m3colors.darkmode) { + if (folderModel.count === 0) return; + const randomIndex = Math.floor(Math.random() * folderModel.count); + const filePath = folderModel.get(randomIndex, "filePath"); + print("Randomly selected wallpaper:", filePath); + root.select(filePath, darkMode); + } + Process { id: validateDirProc property string nicePath: "" @@ -136,12 +146,13 @@ Singleton { // Thumbnail generation function generateThumbnail(size: string) { + // console.log("[Wallpapers] Updating thumbnails") if (!["normal", "large", "x-large", "xx-large"].includes(size)) throw new Error("Invalid thumbnail size"); thumbgenProc.directory = root.directory thumbgenProc.running = false thumbgenProc.command = [ "bash", "-c", - `${thumbgenScriptPath} --size ${size} --machine_progress -d ${root.directory} || ${generateThumbnailsMagicScriptPath} --size ${size} -d ${root.directory}`, + `${thumbgenScriptPath} --size ${size} --machine_progress -d ${FileUtils.trimFileProtocol(root.directory)} || ${generateThumbnailsMagickScriptPath} --size ${size} -d ${root.directory}`, ] root.thumbnailGenerationProgress = 0 thumbgenProc.running = true diff --git a/.config/quickshell/ii/settings.qml b/.config/quickshell/ii/settings.qml index 0f52b2382..3f3df0703 100644 --- a/.config/quickshell/ii/settings.qml +++ b/.config/quickshell/ii/settings.qml @@ -71,8 +71,8 @@ ApplicationWindow { MaterialThemeLoader.reapplyTheme() } - minimumWidth: 600 - minimumHeight: 400 + minimumWidth: 750 + minimumHeight: 500 width: 1100 height: 750 color: Appearance.m3colors.m3background @@ -177,7 +177,7 @@ ApplicationWindow { } StyledToolTip { - content: Translation.tr("Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json") + text: Translation.tr("Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json") } } diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index f2062cc23..1088669ed 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -11,6 +11,7 @@ import "./modules/common/" import "./modules/background/" import "./modules/bar/" import "./modules/cheatsheet/" +import "./modules/crosshair/" import "./modules/dock/" import "./modules/lock/" import "./modules/mediaControls/" @@ -36,6 +37,7 @@ ShellRoot { property bool enableBar: true property bool enableBackground: true property bool enableCheatsheet: true + property bool enableCrosshair: true property bool enableDock: true property bool enableLock: true property bool enableMediaControls: true @@ -59,11 +61,13 @@ ShellRoot { FirstRunExperience.load() ConflictKiller.load() Cliphist.refresh() + Wallpapers.load() } LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} } LazyLoader { active: enableBackground; component: Background {} } LazyLoader { active: enableCheatsheet; component: Cheatsheet {} } + LazyLoader { active: enableCrosshair; component: Crosshair {} } LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} } LazyLoader { active: enableLock; component: Lock {} } LazyLoader { active: enableMediaControls; component: MediaControls {} } diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index fefdc7976..1f7e0dc22 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -109,6 +109,10 @@ ApplicationWindow { text: "close" iconSize: 20 } + + StyledToolTip { + text: Translation.tr("Tip: Close a window with Super+Q") + } } } } @@ -239,20 +243,20 @@ ApplicationWindow { visible: Config.options.policies.weeb === 1 Layout.alignment: Qt.AlignHCenter buttonRadius: Appearance.rounding.small - materialIcon: "wallpaper" + materialIcon: "ifl" mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { console.log(konachanWallProc.command.join(" ")); konachanWallProc.running = true; } StyledToolTip { - content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") + text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { materialIcon: "wallpaper" StyledToolTip { - content: Translation.tr("Pick wallpaper image on your system") + text: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached([`${Directories.wallpaperSwitchScriptPath}`]); diff --git a/Extras/emacs/material-theme.el b/Extras/emacs/material-theme.el index 41c6bdcbc..a836f2ebf 100644 --- a/Extras/emacs/material-theme.el +++ b/Extras/emacs/material-theme.el @@ -23,7 +23,7 @@ ;; Define function to read SCSS variables (defun material-get-color-from-scss (var-name) "Extract color value for VAR-NAME from material_colors.scss file." - (let* ((scss-file (expand-file-name "~/.cache/ags/user/generated/material_colors.scss")) + (let* ((scss-file (expand-file-name "~/.local/state/quickshell/user/generated/material_colors.scss")) (scss-content (with-temp-buffer (insert-file-contents scss-file) (buffer-string))) diff --git a/README.md b/README.md index 56f3115eb..982f10273 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ | [Hyprland](https://github.com/hyprwm/hyprland) | The compositor (manages and renders windows) | | [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, used for the status bar, sidebars, etc. | - - THERE IS NO WAYBAR - For the full list of dependencies, see the [arch-packages folder](https://github.com/end-4/dots-hyprland/tree/main/arch-packages) + - THERE IS NO WAYBAR STOP FUCKING CALLING EVERY BAR WAYBAR
@@ -62,20 +62,14 @@

- - - - - -
- illogical-impulse logo - - latest and only style that I actively use. Other past styles are still there for your viewing pleasure and not actual use, but code is still available, see below. -
- +
+ illogical-impulse logo +
### illogical-impulseQuickshell +This is the latest and only supported style. Other stuff are still there mostly for viewing pleasure and not actual use, but code is still available, see below. + Widget system: Quickshell | Support: Yes [Showcase video](https://www.youtube.com/watch?v=RPwovTInagE) @@ -86,52 +80,7 @@ Widget system: Quickshell | Support: Yes | Window management | Weeb power | | image | image | -### illogical-impulseAGS (Deprecated) - -Widget system: AGS | Support: No - -| AI | Common widgets | -|:---|:---------------| -| ![image](https://github.com/user-attachments/assets/9d7af13f-89ef-470d-ba78-d2288b79cf60) | ![image](https://github.com/end-4/dots-hyprland/assets/97237370/406b72b6-fa38-4f0d-a6c4-4d7d5d5ddcb7) | -| Window management | Weeb power | -| ![image](https://github.com/user-attachments/assets/02983b9b-79ba-4c25-8717-90bef2357ae5) | ![image](https://github.com/user-attachments/assets/bbb332ec-962a-4e88-a95b-486d0bd8ce76) | - -### Very old stuff - -- Not likely to work, but the source is still available in the [`archive`](https://github.com/end-4/dots-hyprland/tree/archive) branch. Extremely spaghetti. -- Click image for a presentation video - -#### m3ww - - Widget system: EWW | Support: No, dead - - - Material Eww! - - -#### NovelKnock - - Widget system: EWW | Support: No, dead - - - Desktop Preview - - -#### Hybrid - - Widget system: EWW | Support: No, dead - - - click the circles! - - -#### Windoes - - Widget system: EWW | Support: No, dead - - - Desktop Preview - +### Other styles: Available at the end of the readme.

• thank you •

@@ -162,4 +111,60 @@ Widget system: AGS | Support: No
- Inspiration: osu!lazer, Windows 11, Material Design 3, AvdanOS (concept) - - Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (unless you do weird stuff and violate the license). (this note is here because some people actually asked) + - Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (maybe unless you loudly do weird stuff and violate the license) (some people actually had to ask smh) + +--- + +--- + +
+

• old, UNSUPPORTED stuff •

+

+
+ +- Source for illogical-impulse AGS available in the `ii-ags` branch, others in the `archive` branch. +- The list goes from newest to the oldest, and the code quality is worse the older a style is +- No bug fix or official support will be provided. + +### illogical-impulseAGS + +Widget system: AGS | Support: No + +| AI | Common widgets | +|:---|:---------------| +| ![image](https://github.com/user-attachments/assets/9d7af13f-89ef-470d-ba78-d2288b79cf60) | ![image](https://github.com/end-4/dots-hyprland/assets/97237370/406b72b6-fa38-4f0d-a6c4-4d7d5d5ddcb7) | +| Window management | Weeb power | +| ![image](https://github.com/user-attachments/assets/02983b9b-79ba-4c25-8717-90bef2357ae5) | ![image](https://github.com/user-attachments/assets/bbb332ec-962a-4e88-a95b-486d0bd8ce76) | + +#### m3ww + + Widget system: EWW | Support: No, dead + + + Material Eww! + + +#### NovelKnock + + Widget system: EWW | Support: No + + + Desktop Preview + + +#### Hybrid + + Widget system: EWW | Support: No + + + click the circles! + + +#### Windoes + + Widget system: EWW | Support: No + + + Desktop Preview + +