summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components
diff options
context:
space:
mode:
authorNippy <nippy@Nippy-Mac-mini.local>2026-10-09 17:23:53 +0200
committerNippy <nippy@Nippy-Mac-mini.local>2026-10-09 17:23:53 +0200
commit9d5be1660d8f74038ebed3d74fdbc809e43787ae (patch)
tree4cb46c3765be87ca4c5219f2f714425d7dfddb56 /raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components
parentdeb4b8a9c407832c35100a643bd7a592589c9679 (diff)
downloadRaveOS-PKGBUILD-9d5be1660d8f74038ebed3d74fdbc809e43787ae.tar.gz
RaveOS-PKGBUILD-9d5be1660d8f74038ebed3d74fdbc809e43787ae.zip
gnome fix
Diffstat (limited to 'raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components')
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js87
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js182
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js378
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js35
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js400
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup.js1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/blur_surface.js484
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/index.js556
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/message_stacks.js571
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_actor.js301
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_corner.js70
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_fade.js132
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_geometry.js291
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_placement.js328
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_signals.js117
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_style.js59
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_transitions.js139
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/targets.js255
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js8
19 files changed, 4071 insertions, 323 deletions
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js
index 8824777..b04e39d 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js
@@ -2,11 +2,8 @@ import Shell from 'gi://Shell';
import Clutter from 'gi://Clutter';
import Cogl from 'gi://Cogl';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
-import { adjustAnimationTime } from 'resource:///org/gnome/shell/misc/animationUtils.js';
import { PaintSignals } from '../conveniences/paint_signals.js';
-// TODO drop Tweener in favour of Clutter's `ease` (will need to extend the blur effect for it)
-const Tweener = imports.tweener.tweener;
// TODO: Drop GNOME 46 backwards compatibility
const transparent = Clutter.Color ?
@@ -30,6 +27,26 @@ let original_zoomAndFadeOut = null;
let sigma;
let brightness;
+const BLUR_RADIUS_PROPERTY = '@effects.appfolder-blur.radius';
+const BLUR_BRIGHTNESS_PROPERTY = '@effects.appfolder-blur.brightness';
+
+function stop_blur_animation(dialog) {
+ dialog.remove_transition(BLUR_RADIUS_PROPERTY);
+ dialog.remove_transition(BLUR_BRIGHTNESS_PROPERTY);
+}
+
+function animate_blur(dialog, target, mode) {
+ stop_blur_animation(dialog);
+ dialog.ease_property(BLUR_RADIUS_PROPERTY, target.radius, {
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode,
+ });
+ dialog.ease_property(BLUR_BRIGHTNESS_PROPERTY, target.brightness, {
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode,
+ });
+}
+
let _zoomAndFadeIn = function () {
let [sourceX, sourceY] =
this._source.get_transformed_position();
@@ -50,13 +67,10 @@ let _zoomAndFadeIn = function () {
blur_effect.radius = 0;
blur_effect.brightness = 1.0;
- Tweener.addTween(blur_effect,
- {
- radius: sigma * 2,
- brightness: brightness,
- time: adjustAnimationTime(FOLDER_DIALOG_ANIMATION_TIME / 1000),
- transition: 'easeOutQuad'
- }
+ animate_blur(
+ this,
+ { radius: sigma * 2, brightness },
+ Clutter.AnimationMode.EASE_OUT_QUAD
);
this.child.ease({
@@ -93,14 +107,10 @@ let _zoomAndFadeOut = function () {
this.set_background_color(transparent);
- let blur_effect = this.get_effect("appfolder-blur");
- Tweener.addTween(blur_effect,
- {
- radius: 0,
- brightness: 1.0,
- time: adjustAnimationTime(FOLDER_DIALOG_ANIMATION_TIME / 1000),
- transition: 'easeInQuad'
- }
+ animate_blur(
+ this,
+ { radius: 0, brightness: 1.0 },
+ Clutter.AnimationMode.EASE_IN_QUAD
);
this.child.ease({
@@ -172,15 +182,19 @@ export const AppFoldersBlur = class AppFoldersBlur {
original_zoomAndFadeOut = icon._dialog._zoomAndFadeOut;
}
- let blur_effect = new Shell.BlurEffect({
- name: "appfolder-blur",
- radius: sigma * 2,
- brightness: brightness,
- mode: Shell.BlurMode.BACKGROUND
- });
-
- icon._dialog.remove_effect_by_name("appfolder-blur");
- icon._dialog.add_effect(blur_effect);
+ let blur_effect = icon._dialog.get_effect('appfolder-blur');
+ if (!blur_effect) {
+ blur_effect = new Shell.BlurEffect({
+ name: 'appfolder-blur',
+ radius: sigma * 2,
+ brightness,
+ mode: Shell.BlurMode.BACKGROUND,
+ });
+ icon._dialog.add_effect(blur_effect);
+ } else {
+ stop_blur_animation(icon._dialog);
+ blur_effect.set({ radius: sigma * 2, brightness });
+ }
DIALOGS_STYLES.forEach(
style => icon._dialog._viewBox.remove_style_class_name(style)
@@ -218,19 +232,31 @@ export const AppFoldersBlur = class AppFoldersBlur {
set_sigma(s) {
sigma = s;
if (this.settings.appfolder.BLUR)
- this.blur_appfolders();
+ this.update_blur_effects({ radius: sigma * 2 });
}
set_brightness(b) {
brightness = b;
if (this.settings.appfolder.BLUR)
- this.blur_appfolders();
+ this.update_blur_effects({ brightness });
+ }
+
+ update_blur_effects(params) {
+ const appDisplay = Main.overview._overview.controls._appDisplay;
+ appDisplay._folderIcons.forEach(icon => {
+ if (!icon._dialog)
+ return;
+
+ stop_blur_animation(icon._dialog);
+ icon._dialog.get_effect('appfolder-blur')?.set(params);
+ });
}
disable() {
this._log("removing blur from appfolders");
let appDisplay = Main.overview._overview.controls._appDisplay;
+ this.paint_signals.disconnect_all();
if (original_zoomAndFadeIn != null) {
appDisplay._folderIcons.forEach(icon => {
@@ -248,6 +274,7 @@ export const AppFoldersBlur = class AppFoldersBlur {
appDisplay._folderIcons.forEach(icon => {
if (icon._dialog) {
+ stop_blur_animation(icon._dialog);
icon._dialog.remove_effect_by_name("appfolder-blur");
DIALOGS_STYLES.forEach(
s => icon._dialog._viewBox.remove_style_class_name(s)
@@ -262,4 +289,4 @@ export const AppFoldersBlur = class AppFoldersBlur {
if (this.settings.DEBUG)
console.log(`[Blur my Shell > appfolders] ${str}`);
}
-}; \ No newline at end of file
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
index 5280b55..4711a0e 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
@@ -145,34 +145,23 @@ export const ApplicationsBlur = class ApplicationsBlur {
/// shown on every window of the workspaces viewer.
connect_to_overview() {
this.connections.disconnect_all_for(Main.overview);
+ this.overview_visible = Main.overview.visible;
- if (this.settings.applications.BLUR_ON_OVERVIEW) {
- // when the overview is opened, show every window actors (which
- // allows the blur to be shown too)
- this.connections.connect(
- Main.overview, 'showing',
- _ => this.meta_window_map.forEach((meta_window, _pid) => {
- let window_actor = meta_window.get_compositor_private();
- window_actor?.show();
- })
- );
-
- // when the overview is closed, hide every actor that is not on the
- // current workspace (to mimic the original behaviour)
- this.connections.connect(
- Main.overview, 'hidden',
- _ => {
- this.meta_window_map.forEach((meta_window, _pid) => {
- let window_actor = meta_window.get_compositor_private();
+ const reconcile_windows = () => this.meta_window_map.forEach(
+ (meta_window, _pid) => this.reconcile_window_visibility(meta_window)
+ );
+ this.connections.connect(Main.overview, 'showing', _ => {
+ this.overview_visible = true;
+ reconcile_windows();
+ });
+ this.connections.connect(Main.overview, 'hidden', _ => {
+ this.overview_visible = false;
+ reconcile_windows();
+ });
- if (
- (!meta_window.get_workspace().active) || meta_window.minimized
- )
- window_actor.hide();
- });
- }
- );
- }
+ this.meta_window_map.forEach((meta_window, _pid) =>
+ this.reconcile_window_visibility(meta_window)
+ );
}
/// Iterate through all existing windows and add blur as needed.
@@ -239,14 +228,14 @@ export const ApplicationsBlur = class ApplicationsBlur {
if (this.settings.applications.STATIC_BLUR && meta_window.get_client_type() === Meta.WindowClientType.X11) {
const window_actor = meta_window.get_compositor_private();
- window_actor.connect('child-added', _ => {
- if (!meta_window.blur_actor) {
- this._warn("can't move blur actor to back, it doesn't exist");
- return;
- }
+ if (window_actor) {
+ this.connections.connect(window_actor, 'child-added', _ => {
+ if (!meta_window.blur_actor)
+ return;
- window_actor.set_child_below_sibling(meta_window.blur_actor, null);
- });
+ window_actor.set_child_below_sibling(meta_window.blur_actor, null);
+ });
+ }
}
}
@@ -375,19 +364,17 @@ export const ApplicationsBlur = class ApplicationsBlur {
meta_window.blur_actor = blur_actor;
- // make sure window is blurred in overview
- if (this.settings.applications.BLUR_ON_OVERVIEW)
- this.enforce_window_visibility_on_overview_for(window_actor);
+ if (!this.settings.applications.BLUR_ON_OVERVIEW && Main.overview.visible)
+ blur_actor.hide();
// update the size
this.update_size(pid);
- // set the window actor's opacity
- this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
-
// update corner radius based on window state
this.update_corner_radius(meta_window);
+ this.reconcile_window_visibility(meta_window);
+
// now set up the signals, for the window actor only: they are disconnected
// in `remove_blur`, whereas the signals for the meta window are disconnected
// only when the whole component is disabled
@@ -403,15 +390,17 @@ export const ApplicationsBlur = class ApplicationsBlur {
);
this.connections.connect(
meta_window, 'notify::fullscreen',
- _ => this.update_corner_radius(meta_window)
+ _ => {
+ this.update_corner_radius(meta_window);
+ this.reconcile_window_visibility(meta_window);
+ }
);
// update the window opacity when it changes, else we don't control it fully
this.connections.connect(
window_actor, 'notify::opacity',
_ => {
- if (this.focused_window_pid != pid)
- this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
+ this.reconcile_window_visibility(meta_window);
}
);
@@ -422,12 +411,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
this.connections.connect(
window_actor,
'notify::visible',
- window_actor => {
- if (window_actor.visible)
- meta_window.blur_actor.show();
- else
- meta_window.blur_actor.hide();
- }
+ _ => this.reconcile_window_visibility(meta_window)
);
}
@@ -435,57 +419,18 @@ export const ApplicationsBlur = class ApplicationsBlur {
/// we are not focused anymore). It automatically removes the ancient focus.
/// With `focus=false`, just remove the focus from said window (which can still be null).
set_focus_for_window(meta_window, focus = true) {
- let blur_actor = null;
- let window_actor = null;
let new_pid = null;
if (meta_window) {
- blur_actor = meta_window.blur_actor;
- window_actor = meta_window.get_compositor_private();
new_pid = meta_window.bms_pid;
}
- if (focus) {
- // remove old focused window if any
- if (this.focused_window_pid) {
- const old_focused_window = this.meta_window_map.get(this.focused_window_pid);
- if (old_focused_window)
- this.set_focus_for_window(old_focused_window, false);
- }
- // set new focused window pid
+ if (focus)
this.focused_window_pid = new_pid;
- // if we have blur, hide it and make the window opaque
- if (this.settings.applications.DYNAMIC_OPACITY && blur_actor) {
- blur_actor.hide();
- this.set_window_opacity(window_actor, 255);
- }
- }
- // if we remove the focus and have blur, show it and make the window transparent
- else if (blur_actor) {
- blur_actor.show();
- this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
- }
- }
+ else
+ this.focused_window_pid = null;
- /// Makes sure that, when the overview is visible, the window actor will
- /// stay visible no matter what.
- /// We can instead hide the last child of the window actor, which will
- /// improve performances without hiding the blur effect.
- enforce_window_visibility_on_overview_for(window_actor) {
- this.connections.connect(window_actor, 'notify::visible',
- _ => {
- if (this.settings.applications.BLUR_ON_OVERVIEW) {
- if (
- !window_actor.visible
- && Main.overview.visible
- ) {
- window_actor.show();
- window_actor.get_last_child().hide();
- } else if (
- window_actor.visible
- )
- window_actor.get_last_child().show();
- }
- }
+ this.meta_window_map.forEach((tracked_window, _pid) =>
+ this.reconcile_window_visibility(tracked_window)
);
}
@@ -504,11 +449,38 @@ export const ApplicationsBlur = class ApplicationsBlur {
})
} else {
if (meta_window.bg_manager?._bms_pipeline?.effect)
- meta_window.bg_manager._bms_pipeline.effect.corner_radius = use_0_radius ?
+ meta_window.bg_manager._bms_pipeline.effect.unscaled_corner_radius = use_0_radius ?
0 : this.settings.applications.CORNER_RADIUS;
}
}
+ reconcile_window_visibility(meta_window, overview_visible = this.overview_visible) {
+ const window_actor = meta_window.get_compositor_private();
+ const blur_actor = meta_window.blur_actor;
+ if (!window_actor || !blur_actor)
+ return;
+
+ const is_focused = this.settings.applications.DYNAMIC_OPACITY
+ && meta_window.bms_pid === this.focused_window_pid;
+ const is_fullscreen = this.settings.applications.UNBLUR_WHEN_FULLSCREEN
+ && meta_window.fullscreen;
+ const visible_for_blur = window_actor.visible
+ || (this.settings.applications.BLUR_ON_OVERVIEW && overview_visible);
+ const show_blur = visible_for_blur
+ && !is_focused
+ && !is_fullscreen
+ && (this.settings.applications.BLUR_ON_OVERVIEW || !overview_visible);
+
+ if (show_blur)
+ blur_actor.show();
+ else
+ blur_actor.hide();
+
+ this.set_window_opacity(window_actor, show_blur
+ ? this.settings.applications.OPACITY
+ : 255);
+ }
+
/// Update all corners, to use when the setting has been changed.
update_all_corner_radii() {
this.meta_window_map.forEach(
@@ -516,24 +488,30 @@ export const ApplicationsBlur = class ApplicationsBlur {
)
}
+ update_fullscreen_status() {
+ this.meta_window_map.forEach(
+ (meta_window, _pid) => this.reconcile_window_visibility(meta_window)
+ );
+ }
+
/// Set the opacity of the window actor that sits on top of the blur effect.
set_window_opacity(window_actor, opacity) {
+ // Define known blur actor names. This makes it easy to update if names change again.
+ const BLUR_ACTOR_NAMES = new Set(["blur-actor", "bms-application-blurred-widget"]);
+
window_actor?.get_children().forEach(child => {
- if (child.name !== "blur-actor" && child.opacity != opacity)
+ // Check against the Set and the opacity
+ if (!BLUR_ACTOR_NAMES.has(child.name) && child.opacity != opacity) {
child.opacity = opacity;
+ }
});
}
/// Update the opacity of all window actors.
set_opacity() {
- let opacity = this.settings.applications.OPACITY;
-
- this.meta_window_map.forEach(((meta_window, pid) => {
- if (pid != this.focused_window_pid && meta_window.blur_actor) {
- let window_actor = meta_window.get_compositor_private();
- this.set_window_opacity(window_actor, opacity);
- }
- }));
+ this.meta_window_map.forEach((meta_window, _pid) =>
+ this.reconcile_window_visibility(meta_window)
+ );
}
/// Find the system's window scaling.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js
index 4ff212c..d386722 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js
@@ -3,6 +3,7 @@ import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
import { PaintSignals } from '../conveniences/paint_signals.js';
+import * as utils from '../conveniences/utils.js';
import { Pipeline } from '../conveniences/pipeline.js';
import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
@@ -21,7 +22,7 @@ const DASH_STYLES = [
class DashInfos {
constructor(
dash_blur, dash, dash_container, dash_background,
- background, background_group, bg_manager
+ background, background_group, bg_manager, paint_signals
) {
// the parent DashBlur object, to communicate
this.dash_blur = dash_blur;
@@ -31,28 +32,55 @@ class DashInfos {
this.background = background;
this.background_group = background_group;
this.bg_manager = bg_manager;
+ this.paint_signals = paint_signals;
this.settings = dash_blur.settings;
- this.old_style = this.dash._background.style;
+ this.old_style = this.dash._background?.style;
+
+ this.updateId = 0;
+ this._first_boot = true;
+
+ this.bg_allocation_id = this.background_group?.connect('notify::allocation', () => {
+ this.schedule_update();
+ });
+
+ let monitor = Main.layoutManager.findMonitorForActor(this.dash_container);
+ this.current_monitor_index = monitor ? monitor.index : null;
- this.dash_destroy_id = dash.connect('destroy', () => this.remove_dash_blur(false));
+ this.dash_destroy_id = dash.connect('destroy', () => this.remove_dash_blur());
this.dash_blur_connections_ids = [];
this.dash_blur_connections_ids.push(
this.dash_blur.connect('remove-dashes', () => this.remove_dash_blur()),
this.dash_blur.connect('override-style', () => this.override_style()),
this.dash_blur.connect('remove-style', () => this.remove_style()),
- this.dash_blur.connect('show', () => this.background_group.show()),
- this.dash_blur.connect('hide', () => this.background_group.hide()),
- this.dash_blur.connect('update-size', () => this.update_size()),
+ this.dash_blur.connect('show', () => this.background_group?.show()),
+ this.dash_blur.connect('hide', () => this.background_group?.hide()),
+ this.dash_blur.connect('update-size', () => this.schedule_update()),
this.dash_blur.connect('change-blur-type', () => this.change_blur_type()),
this.dash_blur.connect('update-pipeline', () => this.update_pipeline())
);
}
+ schedule_update() {
+ this.clear_pending_idles();
+ this.updateId = global.compositor.get_laters().add(Meta.LaterType.IDLE, () => {
+ this.updateId = 0;
+ this.update_size();
+ return false;
+ });
+ }
+
+ clear_pending_idles() {
+ if (this.updateId) {
+ global.compositor.get_laters().remove(this.updateId);
+ this.updateId = 0;
+ }
+ }
+
// IMPORTANT: do never call this in a mutable `this.dash_blur.forEach`
- remove_dash_blur(dash_not_already_destroyed = true) {
+ remove_dash_blur() {
// remove the style and destroy the effects
this.remove_style();
- this.destroy_dash(dash_not_already_destroyed);
+ this.destroy_dash();
// remove the dash infos from their list
const dash_infos_index = this.dash_blur.dashes.indexOf(this);
@@ -60,6 +88,10 @@ class DashInfos {
this.dash_blur.dashes.splice(dash_infos_index, 1);
// disconnect everything
+ if (this.bg_allocation_id && this.background_group) {
+ this.background_group.disconnect(this.bg_allocation_id);
+ this.bg_allocation_id = null;
+ }
this.dash_blur_connections_ids.forEach(id => { if (id) this.dash_blur.disconnect(id); });
this.dash_blur_connections_ids = [];
if (this.dash_destroy_id)
@@ -70,36 +102,74 @@ class DashInfos {
override_style() {
this.remove_style();
- this.dash.set_style_class_name(
- DASH_STYLES[this.settings.dash_to_dock.STYLE_DASH_TO_DOCK]
- );
+ const style = DASH_STYLES[
+ this.settings.dash_to_dock.STYLE_DASH_TO_DOCK
+ ];
+
+ // Dash to Dock owns its style class list. Replacing it removes the
+ // extension's layout and border-radius rules. Add Blur My Shell's
+ // visual modifier alongside the native classes instead.
+ if (style && !this.dash.has_style_class_name(style))
+ this.dash.add_style_class_name(style);
}
remove_style() {
- this.dash._background.style = this.old_style;
+ if (this.dash?._background)
+ this.dash._background.style = this.old_style;
DASH_STYLES.forEach(
- style => this.dash.remove_style_class_name(style)
+ style => {
+ this.dash?.remove_style_class_name(style);
+ this.dash_background?.remove_style_class_name(style);
+ }
);
}
- destroy_dash(dash_not_already_destroyed = true) {
- if (!dash_not_already_destroyed)
+ destroy_dash() {
+ this.clear_pending_idles();
+
+ if (this.paint_signals)
+ this.paint_signals.disconnect_all();
+
+ if (this.background_group && this.dash) {
+ const parent = this.dash.get_parent();
+ if (parent)
+ parent.remove_child(this.background_group);
+ }
+
+ if (this.bg_manager) {
+ if (this.bg_manager._bms_pipeline) {
+ this.bg_manager._bms_pipeline.destroy();
+ this.bg_manager._bms_pipeline = null;
+ }
this.bg_manager.backgroundActor = null;
+ this.bg_manager.destroy();
+ this.bg_manager = null;
+ }
+
+ if (this.bg_allocation_id && this.background_group) {
+ this.background_group.disconnect(this.bg_allocation_id);
+ this.bg_allocation_id = null;
+ }
+
+ this._first_boot = null;
- this.paint_signals?.disconnect_all();
- this.dash.get_parent().remove_child(this.background_group);
- this.bg_manager._bms_pipeline.destroy();
- this.bg_manager.destroy();
- this.background_group.destroy();
+ if (this.background_group) {
+ this.background_group.destroy();
+ this.background_group = null;
+ }
}
change_blur_type() {
this.destroy_dash();
+ let blur_result = this.dash_blur.add_blur(this.dash, this.dash_container);
+ if (!blur_result)
+ return;
+
let [
background, background_group, bg_manager, paint_signals
- ] = this.dash_blur.add_blur(this.dash);
+ ] = blur_result;
this.background = background;
this.background_group = background_group;
@@ -108,50 +178,52 @@ class DashInfos {
this.dash.get_parent().insert_child_at_index(this.background_group, 0);
- this.update_size();
+ // Apply the layout instantly before drawing the first frame
+ this.bg_allocation_id = this.background_group.connect('notify::allocation', () => {
+ this.schedule_update();
+ });
+
+ this.schedule_update();
}
update_pipeline() {
- this.bg_manager._bms_pipeline.change_pipeline_to(
- this.settings.dash_to_dock.PIPELINE
- );
+ if (this.bg_manager?._bms_pipeline) {
+ this.bg_manager._bms_pipeline.change_pipeline_to(
+ this.settings.dash_to_dock.PIPELINE
+ );
+ }
}
update_size() {
+ if (!this.dash_blur._has_valid_allocation(this.dash_container) ||
+ !this.dash_blur._has_valid_allocation(this.dash) ||
+ !this.dash_blur._has_valid_allocation(this.dash_background))
+ return;
+
if (this.dash_blur.is_static) {
- let [x, y] = this.get_dash_position(this.dash_container, this.dash_background);
+ let monitor = Main.layoutManager.findMonitorForActor(this.dash_container);
+ if (!monitor) return;
+
+ if (this.current_monitor_index !== monitor.index) {
+ this.current_monitor_index = monitor.index;
+ this.change_blur_type();
+ return;
+ }
- this.background.x = -x;
- this.background.y = -y;
+ let dash_box = this.get_dash_position(this.dash_container);
+ if (!dash_box)
+ return;
- if (this.dash_container.get_style_class_name().includes("top"))
- this.background.set_clip(
- x,
- y + this.dash.y + this.dash_background.y,
- this.dash_background.width,
- this.dash_background.height
- );
- else if (this.dash_container.get_style_class_name().includes("bottom"))
- this.background.set_clip(
- x,
- y + this.dash.y + this.dash_background.y,
- this.dash_background.width,
- this.dash_background.height
- );
- else if (this.dash_container.get_style_class_name().includes("left"))
- this.background.set_clip(
- x + this.dash.x + this.dash_background.x,
- y + this.dash.y + this.dash_background.y,
- this.dash_background.width,
- this.dash_background.height
- );
- else if (this.dash_container.get_style_class_name().includes("right"))
- this.background.set_clip(
- x + this.dash.x + this.dash_background.x,
- y + this.dash.y + this.dash_background.y,
- this.dash_background.width,
- this.dash_background.height
- );
+ const inset = utils.static_blur_clip_inset();
+ const clip_x = Math.floor(dash_box.clip_x) - inset;
+ const clip_y = Math.floor(dash_box.clip_y) - inset;
+ const clip_w = Math.ceil(this.dash_background.width) + inset * 2;
+ const clip_h = Math.ceil(this.dash_background.height) + inset * 2;
+
+ this.background.x = dash_box.background_x + inset;
+ this.background.y = dash_box.background_y + inset;
+
+ this.background.set_clip(clip_x, clip_y, clip_w, clip_h);
} else {
this.background.width = this.dash_background.width;
this.background.height = this.dash_background.height;
@@ -159,29 +231,38 @@ class DashInfos {
this.background.x = this.dash_background.x;
this.background.y = this.dash_background.y + this.dash.y;
}
+
+ if (this._first_boot) {
+ if (this.settings.dash_to_dock.UNBLUR_IN_OVERVIEW && Main.overview.visible) {
+ this.background_group?.hide();
+ }
+ this._first_boot = false;
+ }
}
- get_dash_position(dash_container, dash_background) {
- var x, y;
+ get_dash_position(dash_container) {
+ let monitor = Main.layoutManager.findMonitorForActor(this.dash_container);
+ if (!monitor)
+ return;
- let monitor = Main.layoutManager.findMonitorForActor(dash_container);
let dash_box = dash_container._slider.get_child();
+ if (!dash_box)
+ return null;
- if (dash_container.get_style_class_name().includes("top")) {
- x = (monitor.width - dash_background.width) / 2;
- y = dash_box.y;
- } else if (dash_container.get_style_class_name().includes("bottom")) {
- x = (monitor.width - dash_background.width) / 2;
- y = monitor.height - dash_container.height;
- } else if (dash_container.get_style_class_name().includes("left")) {
- x = dash_box.x;
- y = dash_container.y + (dash_container.height - dash_background.height) / 2 - dash_background.y;
- } else if (dash_container.get_style_class_name().includes("right")) {
- x = monitor.width - dash_container.width;
- y = dash_container.y + (dash_container.height - dash_background.height) / 2 - dash_background.y;
- }
+ let parent = this.background_group?.get_parent();
+ if (!parent)
+ return;
- return [x, y];
+ let [parent_stage_x, parent_stage_y] = parent.get_transformed_position();
+ let [bg_stage_x, bg_stage_y] = this.dash_background.get_transformed_position();
+
+ let background_x = monitor.x - parent_stage_x;
+ let background_y = monitor.y - parent_stage_y;
+
+ let clip_x = bg_stage_x - monitor.x;
+ let clip_y = bg_stage_y - monitor.y;
+
+ return {background_x, background_y, clip_x, clip_y};
}
_log(str) {
@@ -234,28 +315,110 @@ export const DashBlur = class DashBlur extends Signals.EventEmitter {
}).forEach(dash_container => this.try_blur(dash_container));
}
+ _has_valid_allocation(actor) {
+ return actor &&
+ (!actor.has_allocation || actor.has_allocation()) &&
+ actor.width > 0 && actor.height > 0;
+ }
+
+ _defer_blur_until_allocated(dash_container, dash) {
+ if (dash_container._bms_pending_blur_setup)
+ return;
+
+ dash_container._bms_pending_blur_setup = true;
+
+ const connections = [];
+ const cleanup = () => {
+ delete dash_container._bms_pending_blur_setup;
+ delete dash_container._bms_defer_cleanup;
+ connections.forEach(({ actor, id }) => {
+ if (actor && id)
+ actor.disconnect(id);
+ });
+ };
+
+ const retry = () => {
+ if (!dash_container._bms_pending_blur_setup)
+ return;
+
+ let current_dash_box = dash_container._slider?.get_child();
+ let current_dash = dash || current_dash_box?.get_children().find(child => child.get_name() === 'dash');
+
+ if (!this._has_valid_allocation(dash_container) ||
+ !this._has_valid_allocation(current_dash_box) ||
+ !this._has_valid_allocation(current_dash))
+ return;
+
+ cleanup();
+ this.try_blur(dash_container);
+ };
+
+ const dash_cont_id = dash_container.connect('notify::allocation', retry);
+ connections.push({ actor: dash_container, id: dash_cont_id });
+
+ if (dash) {
+ const dash_id = dash.connect('notify::allocation', retry);
+ connections.push({ actor: dash, id: dash_id });
+ }
+
+ const destroy_id = dash_container.connect('destroy', cleanup);
+ connections.push({ actor: dash_container, id: destroy_id });
+
+ dash_container._bms_defer_cleanup = cleanup;
+ }
+
// Tries to blur the dash contained in the given actor
try_blur(dash_container) {
- let dash_box = dash_container._slider.get_child();
+ let dash_box = dash_container._slider?.get_child();
+ if (!dash_box){
+ this._defer_blur_until_allocated(dash_container);
+ return;
+ }
- // verify that we did not already blur that dash
- if (!dash_box.get_children().some(child =>
+ let dash = dash_box.get_children().find(child => {
+ return child.get_name() === 'dash';
+ });
+
+ if (!dash ||
+ !this._has_valid_allocation(dash_container) ||
+ !this._has_valid_allocation(dash_box) ||
+ !this._has_valid_allocation(dash)) {
+ this._defer_blur_until_allocated(dash_container, dash);
+ return;
+ }
+
+ let dash_exist = this.dashes.find(info => info.dash === dash);
+ if (dash_exist) {
+ dash_exist.remove_dash_blur();
+ }
+
+ let existing_bg = dash_box.get_children().find(child =>
child.get_name() === "bms-dash-backgroundgroup"
- )) {
- this._log("dash to dock found, blurring it");
+ );
- // finally blur the dash
- let dash = dash_box.get_children().find(child => {
- return child.get_name() === 'dash';
- });
+ if (existing_bg) {
+ if (dash_box.contains(existing_bg))
+ dash_box.remove_child(existing_bg);
+
+ existing_bg.destroy();
+ }
+
+ // verify that we did not already blur that dash
+ this._log("dash to dock found, blurring it");
- this.dashes.push(this.blur_dash_from(dash, dash_container));
+ let infos = this.blur_dash_from(dash, dash_container);
+ if (infos) {
+ this.dashes.push(infos);
}
}
// Blurs the dash and returns a `DashInfos` containing its information
blur_dash_from(dash, dash_container) {
- let [background, background_group, bg_manager, paint_signals] = this.add_blur(dash);
+ let blur_result = this.add_blur(dash, dash_container);
+ if (!blur_result)
+ return null;
+
+ let [background, background_group, bg_manager, paint_signals] = blur_result;
// insert the background group to the right element
dash.get_parent().insert_child_at_index(background_group, 0);
@@ -268,14 +431,32 @@ export const DashBlur = class DashBlur extends Signals.EventEmitter {
);
this.connections.connect(
dash_container,
- ['notify::width', 'notify::height', 'notify::y', 'notify::x'],
+ [
+ 'notify::width',
+ 'notify::height',
+ 'notify::y',
+ 'notify::x',
+ 'notify::style-class-name',
+ 'notify::allocation'
+ ],
_ => this.update_size()
);
- const dash_background = dash.get_children().find(child => {
- return child.get_style_class_name() === 'dash-background';
- });
+ const slider = dash_container._slider;
+ if (slider) {
+ this.connections.connect(
+ slider,
+ ['notify::slide-x', 'notify::allocation'],
+ _ => this.update_size()
+ );
+ }
+
+ const dash_background = dash._background ||
+ dash.get_children().find(child => child.has_style_class_name('dash-background')) ||
+ dash;
+ if (!dash_background)
+ return null;
// create infos
let infos = new DashInfos(
this,
@@ -289,16 +470,23 @@ export const DashBlur = class DashBlur extends Signals.EventEmitter {
);
this.update_size();
+
+ global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this.update_size();
+ return false;
+ });
+
this.update_background();
// returns infos
return infos;
}
- add_blur(dash) {
- const monitor = Main.layoutManager.findMonitorForActor(dash);
+ add_blur(dash, dash_container = null) {
+ const target_actor = dash_container || dash;
+ const monitor = Main.layoutManager.findMonitorForActor(target_actor) || Main.layoutManager.primaryMonitor;
if (!monitor)
- return;
+ return null;
const background_group = new Meta.BackgroundGroup({
name: 'bms-dash-backgroundgroup', width: 0, height: 0
@@ -404,6 +592,12 @@ export const DashBlur = class DashBlur extends Signals.EventEmitter {
this.emit('remove-dashes');
+ for (let child of Main.uiGroup.get_children()) {
+ if (child._bms_defer_cleanup)
+ child._bms_defer_cleanup();
+ delete child._bms_pending_blur_setup;
+ }
+
this.dashes = [];
this.connections.disconnect_all();
@@ -418,4 +612,4 @@ export const DashBlur = class DashBlur extends Signals.EventEmitter {
_warn(str) {
console.warn(`[Blur my Shell > dash manager] ${str}`);
}
-}; \ No newline at end of file
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js
index 3bbde61..c97a459 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js
@@ -104,13 +104,39 @@ export const OverviewBlur = class OverviewBlur {
// this hides windows that are not on the current workspace
if (
outer_this.settings.applications.BLUR
- )
+ ) {
+ // compile blacklist patterns once for this switch
+ const blacklist = outer_this.settings.applications.BLACKLIST || [];
+ const blacklist_regexes = blacklist.map(pattern => {
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
+ return new RegExp('^' + escaped.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', 'i');
+ });
+
for (let i = 0; i < w_m.get_n_workspaces(); i++) {
if (i != w_m.get_active_workspace_index())
w_m.get_workspace_by_index(i)?.list_windows().forEach(
- window => window.get_compositor_private().hide()
+ window => {
+ // skip windows on all workspaces (e.g. secondary
+ // monitor windows under `workspaces-only-on-primary`);
+ // hiding them on any inactive workspace hides them globally
+ if (window.is_on_all_workspaces()) return;
+
+ // skip desktop-type windows (e.g. DING, Nautilus desktop):
+ // they should always remain visible
+ if (window.get_window_type() === Meta.WindowType.DESKTOP)
+ return;
+
+ // skip blacklisted windows:
+ // they are not blurred and should always remain visible
+ const wm_class = window.get_wm_class();
+ if (wm_class && blacklist_regexes.some(re => re.test(wm_class)))
+ return;
+
+ window.get_compositor_private().hide();
+ }
);
}
+ }
Main.uiGroup.remove_child(outer_this.animation_background_group);
};
@@ -149,14 +175,11 @@ export const OverviewBlur = class OverviewBlur {
// add the container widget for the overview only to the overview group
Main.layoutManager.overviewGroup.insert_child_at_index(this.overview_background_group, 0);
// make sure it stays below
- let _reordering = false;
this.connections.connect(Main.layoutManager.overviewGroup, "child-added", (_, child) => {
- if (!_reordering && child !== this.overview_background_group) {
- _reordering = true;
+ if (child !== this.overview_background_group) {
if (this.overview_background_group.get_parent())
Main.layoutManager.overviewGroup.remove_child(this.overview_background_group);
Main.layoutManager.overviewGroup.insert_child_at_index(this.overview_background_group, 0);
- _reordering = false;
}
});
}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js
index bd39166..7d00224 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js
@@ -4,6 +4,7 @@ import Meta from 'gi://Meta';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import { PaintSignals } from '../conveniences/paint_signals.js';
+import * as utils from '../conveniences/utils.js';
import { Pipeline } from '../conveniences/pipeline.js';
import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
@@ -16,9 +17,10 @@ const PANEL_STYLES = [
"contrasted-panel"
];
-// global listener, so we don't miss the panel destruction event
-let isMainPanelAlive = true;
-Main.panel.connect('destroy', () => isMainPanelAlive = false);
+const GRADIENT_PANEL_STYLES = [
+ "gradient-panel",
+ "gradient-panel-reverse"
+];
export const PanelBlur = class PanelBlur {
constructor(connections, settings, effects_manager) {
@@ -27,7 +29,9 @@ export const PanelBlur = class PanelBlur {
this.settings = settings;
this.effects_manager = effects_manager;
this.actors_list = [];
+ this.queued_updates = new Set();
this.enabled = false;
+ this._first_boot = true;
}
enable() {
@@ -36,6 +40,12 @@ export const PanelBlur = class PanelBlur {
return;
}
+ // global listener, so we don't miss the panel destruction event
+ this._isMainPanelAlive = true;
+ this.connections.connect(Main.panel, 'destroy', () => {
+ this._isMainPanelAlive = false;
+ });
+
this._log("blurring top panel");
// check for panels when Dash to Panel is activated
@@ -59,6 +69,9 @@ export const PanelBlur = class PanelBlur {
this.blur_existing_panels();
+ // Hide the panel blur first to avoid the panel background from display on login
+ this.panel_hide_blur_startup();
+
// connect to overview being opened/closed, and dynamically show or not
// the blur when a window is near a panel
this.connect_to_windows_and_overview();
@@ -113,20 +126,16 @@ export const PanelBlur = class PanelBlur {
// blur already existing ones
if (global.dashToPanel.panels)
this.blur_dtp_panels();
-
- // blur main panel if requested in settings
- if (this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL && isMainPanelAlive)
- this.maybe_blur_panel(Main.panel);
} else {
// if no dash-to-panel, blur the main panel
- if (isMainPanelAlive)
+ if (this._isMainPanelAlive)
this.maybe_blur_panel(Main.panel);
// blur panels already created by extension multi-monitors-bar@frederykabryan
Main.uiGroup.get_children().forEach(actor => {
if (actor.get_name() === "panelBox" && actor.get_n_children() === 1) {
let multi_monitor_panel = actor.get_child_at_index(0);
- if (isMainPanelAlive && multi_monitor_panel != Main.panel)
+ if (this._isMainPanelAlive && multi_monitor_panel != Main.panel)
this.maybe_blur_panel(multi_monitor_panel);
}
})
@@ -145,12 +154,27 @@ export const PanelBlur = class PanelBlur {
this._log("Blurring Dash to Panel panels after idle.");
- // blur every panel, except Main.panel (this is handled in blur_existing_panels() method above)
+ // blur every panel found
global.dashToPanel.panels.forEach(p => {
- if (p.panel != Main.panel)
+ if (
+ p.panel != Main.panel ||
+ this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL
+ )
this.maybe_blur_panel(p.panel);
});
+ // if main panel is not included in the previous panels, blur it
+ if (
+ !global.dashToPanel.panels
+ .map(p => p.panel)
+ .includes(Main.panel)
+ &&
+ this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL
+ &&
+ this._isMainPanelAlive
+ )
+ this.maybe_blur_panel(Main.panel);
+
return GLib.SOURCE_REMOVE;
});
};
@@ -169,10 +193,14 @@ export const PanelBlur = class PanelBlur {
/// Blur a panel
blur_panel(panel) {
+ let geometry_actor = panel;
+ let wrapper = null;
let panel_box = panel.get_parent();
let is_dtp_panel = false;
if (!panel_box.name) {
is_dtp_panel = true;
+ wrapper = panel_box;
+ geometry_actor = panel; // Track the inner panel dynamically
panel_box = panel_box.get_parent();
}
@@ -186,9 +214,11 @@ export const PanelBlur = class PanelBlur {
let background, bg_manager;
let static_blur = this.settings.panel.STATIC_BLUR;
+ let pipeline; // Hoist the pipeline variable so our proxy can access it
+
if (static_blur) {
let bg_manager_list = [];
- const pipeline = new Pipeline(
+ pipeline = new Pipeline(
this.effects_manager,
global.blur_my_shell._pipelines_manager,
this.settings.panel.PIPELINE
@@ -200,32 +230,55 @@ export const PanelBlur = class PanelBlur {
bg_manager = bg_manager_list[0];
}
else {
- const pipeline = new DummyPipeline(this.effects_manager, this.settings.panel);
+ pipeline = new DummyPipeline(this.effects_manager, this.settings.panel);
[background, bg_manager] = pipeline.create_background_with_effect(
background_group, 'bms-panel-blurred-widget'
);
+ }
- let paint_signals = new PaintSignals(this.connections);
+ let paint_signals = new PaintSignals(this.connections);
- // HACK
- //
- //`Shell.BlurEffect` does not repaint when shadows are under it. [1]
- //
- // This does not entirely fix this bug (shadows caused by windows
- // still cause artifacts), but it prevents the shadows of the panel
- // buttons to cause artifacts on the panel itself
- //
- // [1]: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2857
+ // HACK
+ //
+ //`Shell.BlurEffect` does not repaint when shadows are under it. [1]
+ //
+ // This does not entirely fix this bug (shadows caused by windows
+ // still cause artifacts), but it prevents the shadows of the panel
+ // buttons to cause artifacts on the panel itself
+ //
+ // [1]: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2857
- {
- if (this.settings.HACKS_LEVEL === 1) {
- this._log("panel hack level 1");
+ {
+ if (this.settings.HACKS_LEVEL === 1) {
+ this._log("panel hack level 1");
- paint_signals.disconnect_all();
- paint_signals.connect(background, pipeline.effect);
- } else {
- paint_signals.disconnect_all();
- }
+ // Proxy object to dynamically resolve the active blur effect.
+ // This ensures repaints continue safely even if pipeline effects are rebuilt.
+ let dynamic_target = {
+ queue_repaint: () => {
+ let active_pipeline = (bg_manager && bg_manager._bms_pipeline) ? bg_manager._bms_pipeline : pipeline;
+
+ if (static_blur && active_pipeline && active_pipeline.effects) {
+ // Repaint all effects in the static pipeline to be agnostic of the active effects
+ for (let i = 0; i < active_pipeline.effects.length; i++) {
+ let eff = active_pipeline.effects[i];
+ if (eff && typeof eff.queue_repaint === 'function') {
+ eff.queue_repaint();
+ }
+ }
+ } else if (!static_blur && active_pipeline) {
+ let eff = active_pipeline.effect; // Dynamic pipeline uses a single effect
+ if (eff && typeof eff.queue_repaint === 'function') {
+ eff.queue_repaint();
+ }
+ }
+ }
+ };
+
+ paint_signals.disconnect_all();
+ paint_signals.connect(background, dynamic_target);
+ } else {
+ paint_signals.disconnect_all();
}
}
@@ -234,75 +287,151 @@ export const PanelBlur = class PanelBlur {
// the object that is used to remembering each elements that is linked to the blur effect
let actors = {
- widgets: { panel, panel_box, background, background_group },
+ widgets: {
+ panel,
+ wrapper,
+ panel_box,
+ background,
+ background_group,
+ geometry_actor
+ },
static_blur,
monitor,
bg_manager,
is_dtp_panel
};
this.actors_list.push(actors);
+ background_group.connect('destroy', () => {
+ actors.widgets.background_group = null;
+ });
- // update the size of the actor
- this.update_size(actors);
+ // defer size update to idle to avoid allocation race
+ this.queue_update_size(actors);
- // connect to panel, panel_box and its parent position or size change
+ // connect to the relevant actors geometry changes
// this should fire update_size every time one of its params change
this.connections.connect(
- panel,
- 'notify::position',
- _ => this.update_size(actors)
+ geometry_actor,
+ ['notify::allocation', 'notify::size', 'notify::position'],
+ _ => this.queue_update_size(actors)
);
+
+ if (wrapper) {
+ this.connections.connect(
+ wrapper,
+ ['notify::allocation', 'notify::size', 'notify::position'],
+ _ => this.queue_update_size(actors)
+ );
+ }
this.connections.connect(
panel_box,
['notify::size', 'notify::position'],
- _ => this.update_size(actors)
+ _ => this.queue_update_size(actors)
);
this.connections.connect(
panel_box.get_parent(),
'notify::position',
- _ => this.update_size(actors)
+ _ => this.queue_update_size(actors)
);
// connect to the panel getting destroyed
this.connections.connect(
panel,
'destroy',
- _ => this.destroy_blur(actors, true)
+ _ => this.destroy_blur(actors)
);
}
+ queue_update_size(actors) {
+ if (this.queued_updates.has(actors))
+ return;
+
+ this.queued_updates.add(actors);
+ GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.queued_updates.delete(actors);
+
+ if (!this.enabled || !this.actors_list.includes(actors))
+ return GLib.SOURCE_REMOVE;
+
+ this.update_size(actors);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
update_size(actors) {
- let panel = actors.widgets.panel;
+ let geometry_actor = actors.widgets.geometry_actor;
let panel_box = actors.widgets.panel_box;
+ let wrapper = actors.widgets.wrapper;
let background = actors.widgets.background;
let [width, height] = panel_box.get_size();
+ let [geometry_width, geometry_height] = geometry_actor.get_size();
+
+ if (!width || !height || !geometry_width || !geometry_height) {
+ this.queue_update_size(actors);
+ return;
+ }
// if static blur, need to clip the background
if (actors.static_blur) {
- let monitor = Main.layoutManager.findMonitorForActor(panel);
- if (!monitor)
+ let monitor = Main.layoutManager.findMonitorForActor(geometry_actor);
+ if (!monitor) {
+ this.queue_update_size(actors);
return;
+ }
- // an alternative to panel.get_transformed_position, because it
- // sometimes yields NaN (probably when the actor is not fully
- // positionned yet)
let [p_x, p_y] = panel_box.get_position();
let [p_p_x, p_p_y] = panel_box.get_parent().get_position();
- let x = p_x + p_p_x - monitor.x + (width - panel.width) / 2;
- let y = p_y + p_p_y - monitor.y + (height - panel.height) / 2;
- background.set_clip(x, y, panel.width, panel.height);
- background.x = (width - panel.width) / 2 - x;
- background.y = .5 + (height - panel.height) / 2 - y;
+ // updated coordinates for static blur
+ let g_x, g_y;
+ if (actors.is_dtp_panel) {
+ let [w_x, w_y] = wrapper.get_position();
+ let [pan_x, pan_y] = geometry_actor.get_position();
+ g_x = w_x + pan_x;
+ g_y = w_y + pan_y;
+ } else {
+ [g_x, g_y] = geometry_actor.get_position();
+ }
+
+ let x = p_x + p_p_x - monitor.x + g_x;
+ let y = p_y + p_p_y - monitor.y + g_y;
+ let is_horizontal = geometry_width >= geometry_height;
+ let distance_to_top = Math.abs(y);
+ let distance_to_bottom = Math.abs(
+ monitor.height - (y + geometry_height)
+ );
+ let is_bottom_panel = is_horizontal &&
+ distance_to_bottom < distance_to_top;
+
+ const inset = utils.static_blur_clip_inset();
+ const offset = utils.subpixel_stage_offset();
+ const workspace_edge_inset = is_bottom_panel
+ ? Math.max(inset, 1)
+ : inset;
+ const clip_x = Math.floor(x) - inset;
+ const clip_y = Math.floor(y) - workspace_edge_inset;
+ const clip_w = Math.ceil(geometry_width) + inset * 2;
+ const clip_h = Math.ceil(geometry_height) +
+ inset + workspace_edge_inset;
+
+ background.set_clip(clip_x, clip_y, clip_w, clip_h);
+ background.x = g_x - x + inset;
+ background.y = offset + g_y - y + inset;
} else {
- background.x = panel.x;
- background.y = panel.y;
- background.width = panel.width;
- background.height = panel.height;
+ // updated coordinates for dynamic blur
+ if (actors.is_dtp_panel) {
+ background.x = wrapper.x + geometry_actor.x;
+ background.y = wrapper.y + geometry_actor.y;
+ } else {
+ background.x = geometry_actor.x;
+ background.y = geometry_actor.y;
+ }
+ background.width = geometry_width;
+ background.height = geometry_height;
}
// update the monitor panel is on
- actors.monitor = Main.layoutManager.findMonitorForActor(panel);
+ actors.monitor = Main.layoutManager.findMonitorForActor(geometry_actor);
}
/// Connect when overview if opened/closed to hide/show the blur accordingly
@@ -322,23 +451,27 @@ export const PanelBlur = class PanelBlur {
this.connections.connect(
Main.overview, 'showing', _ => this.hide()
);
+
this.connections.connect(
- Main.overview, 'hidden', _ => this.show()
+ Main.overview, 'hidden', _ => {
+ this.panel_hide_blur_dynamically();
+ this.update_visibility();
+ }
);
} else {
let appDisplay = Main.overview._overview._controls._appDisplay;
-
+
this.connections.connect(
appDisplay, 'show', _ => this.hide()
);
+
this.connections.connect(
- appDisplay, 'hide', _ => this.show()
+ appDisplay, 'hide', _ => this.update_visibility()
);
this.connections.connect(
- Main.overview, 'hidden', _ => this.show()
+ Main.overview, 'hidden', _ => this.update_visibility()
);
}
-
}
}
@@ -418,13 +551,13 @@ export const PanelBlur = class PanelBlur {
/// Update the css classname of the panel for light theme
update_light_text_classname(disable = false) {
- if (!isMainPanelAlive)
+ if (!this._isMainPanelAlive)
return;
if (this.settings.panel.FORCE_LIGHT_TEXT && !disable)
- Main.panel.add_style_class_name("panel-light-text");
+ Main.uiGroup.add_style_class_name("panel-light-text");
else
- Main.panel.remove_style_class_name("panel-light-text");
+ Main.uiGroup.remove_style_class_name("panel-light-text");
}
/// Callback when a new window is added
@@ -452,7 +585,7 @@ export const PanelBlur = class PanelBlur {
/// Update the visibility of the blur effect
update_visibility() {
if (
- isMainPanelAlive && Main.panel.has_style_pseudo_class('overview')
+ this._isMainPanelAlive && Main.panel.has_style_pseudo_class('overview')
|| !Main.sessionMode.hasWindows
) {
this.actors_list.forEach(
@@ -469,7 +602,13 @@ export const PanelBlur = class PanelBlur {
const windows = workspace.list_windows().filter(meta_window =>
meta_window.showing_on_its_workspace()
&& !meta_window.is_hidden()
- && meta_window.get_window_type() !== Meta.WindowType.DESKTOP
+ // Whitelist only standard application windows.
+ // This prevents system overlays from disabling panel blur.
+ && [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG
+ ].includes(meta_window.get_window_type())
// exclude Desktop Icons NG
&& meta_window.get_gtk_application_id() !== "com.rastersoft.ding"
&& meta_window.get_gtk_application_id() !== "com.desktop.ding"
@@ -499,10 +638,10 @@ export const PanelBlur = class PanelBlur {
// if so, and if in the same monitor, then it overlaps
if (same_monitor
&&
- // check if panel is on top
- ((panel_top === 0 && window_vertical_pos < panel_bottom + 5 * scale) ||
+ // check if panel is on top (relative to the monitor's Y origin)
+ ((panel_top === actors.monitor.y && window_vertical_pos < panel_bottom + 5 * scale) ||
// check if panel is at the bottom
- (panel_top > 0 && window_vertical_bottom > panel_top - 5 * scale))
+ (panel_top > actors.monitor.y && window_vertical_bottom > panel_top - 5 * scale))
)
window_overlap_panel = true;
});
@@ -519,22 +658,100 @@ export const PanelBlur = class PanelBlur {
set_should_override_panel(actors, should_override) {
let panel = actors.widgets.panel;
- PANEL_STYLES.forEach(style => panel.remove_style_class_name(style));
-
- if (
- this.settings.panel.OVERRIDE_BACKGROUND
- &&
- should_override
- ) {
- panel.add_style_class_name(
- PANEL_STYLES[this.settings.panel.STYLE_PANEL]
- );
+ if (this.settings.panel.OVERRIDE_BACKGROUND) {
+ if (this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY) {
+ if (this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_MODE == 0) {
+ // This is an invert of the above behavior,
+ // Blur and all styling is hidden when "should_override" is true.
+ if (!should_override) {
+ this.proximity_show(actors, panel);
+ }
+ else {
+ this.proximity_hide(actors, panel);
+ };
+ }
+ if (this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_MODE == 1) {
+ if (should_override) {
+ this.update_panel_style_class(panel, PANEL_STYLES[this.settings.panel.STYLE_PANEL]);
+ } else {
+ this.update_panel_style_class(panel, null); // Clear custom styles
+ }
+ }
+ }
+ else {
+ this.update_panel_style_class(panel, PANEL_STYLES[this.settings.panel.STYLE_PANEL]);
+ }
+ }
+ else {
+ this.update_panel_style_class(panel, null);
}
// update the classname if the panel to have or have not light text
this.update_light_text_classname(!should_override);
}
+ update_panel_style_class(panel, target_class) {
+ const ALL_STYLES = [...PANEL_STYLES, ...GRADIENT_PANEL_STYLES];
+
+ // Remove all managed classes EXCEPT the target class we are moving to
+ ALL_STYLES.forEach(style => {
+ if (style !== target_class) {
+ panel.remove_style_class_name(style);
+ }
+ });
+
+ // Add the target class if specified and not already present
+ if (target_class && !panel.has_style_class_name(target_class)) {
+ panel.add_style_class_name(target_class);
+ }
+ }
+
+ panel_hide_blur_dynamically() {
+ if (this.settings.panel.OVERRIDE_BACKGROUND && this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY) {
+ if (this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_MODE == 0) {
+ this.hide()
+ }
+ else {this.show()}
+ }
+ else {
+ this.show()
+ }
+ }
+
+ proximity_hide(actors, panel) {
+ let target_style = null;
+ if (this.settings.panel.GRADIENT_PANEL) {
+ if (!Main.overview.visible) {
+ target_style = GRADIENT_PANEL_STYLES[this.settings.panel.GRADIENT_PANEL_MODE];
+ }
+ else {
+ target_style = this.settings.panel.UNBLUR_IN_OVERVIEW
+ ? PANEL_STYLES[0]
+ : GRADIENT_PANEL_STYLES[this.settings.panel.GRADIENT_PANEL_MODE];
+ }
+ }
+ else {
+ target_style = PANEL_STYLES[this.settings.panel.STYLE_PANEL];
+ }
+
+ this.update_panel_style_class(panel, target_style);
+ actors.widgets.background.hide();
+ }
+
+ proximity_show(actors, panel) {
+ this.update_panel_style_class(panel, PANEL_STYLES[this.settings.panel.STYLE_PANEL]);
+ actors.widgets.background.show();
+ }
+
+ panel_hide_blur_startup(){
+ if (this._first_boot) {
+ if (this.settings.panel.UNBLUR_IN_OVERVIEW && Main.overview.visible) {
+ this.hide();
+ }
+ this._first_boot = false;
+ }
+ }
+
update_pipeline() {
this.actors_list.forEach(actors =>
actors.bg_manager._bms_pipeline.change_pipeline_to(
@@ -556,19 +773,22 @@ export const PanelBlur = class PanelBlur {
}
// IMPORTANT: do never call this in a mutable `this.actors_list.forEach`
- destroy_blur(actors, panel_already_destroyed) {
+ destroy_blur(actors) {
this.set_should_override_panel(actors, false);
- actors.bg_manager._bms_pipeline.destroy();
-
- if (panel_already_destroyed)
+ if (actors.bg_manager) {
+ if (actors.bg_manager._bms_pipeline) {
+ actors.bg_manager._bms_pipeline.destroy();
+ actors.bg_manager._bms_pipeline = null;
+ }
actors.bg_manager.backgroundActor = null;
- actors.bg_manager.destroy();
+ actors.bg_manager.destroy();
+ actors.bg_manager = null;
+ }
- if (!panel_already_destroyed) {
- actors.widgets.panel_box.remove_child(actors.widgets.background_group);
- actors.widgets.background_group.destroy_all_children();
+ if (actors.widgets && actors.widgets.background_group) {
actors.widgets.background_group.destroy();
+ actors.widgets.background_group = null;
}
let index = this.actors_list.indexOf(actors);
@@ -589,10 +809,12 @@ export const PanelBlur = class PanelBlur {
this.update_light_text_classname(true);
const immutable_actors_list = [...this.actors_list];
- immutable_actors_list.forEach(actors => this.destroy_blur(actors, false));
+ immutable_actors_list.forEach(actors => this.destroy_blur(actors));
this.actors_list = [];
+ this.queued_updates.clear();
this._dirty = true;
+ this._first_boot = null;
this.connections.disconnect_all();
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup.js
new file mode 100644
index 0000000..959b2f4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup.js
@@ -0,0 +1 @@
+export { PopupBlur } from './popup/index.js';
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/blur_surface.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/blur_surface.js
new file mode 100644
index 0000000..8fcf82b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/blur_surface.js
@@ -0,0 +1,484 @@
+import GLib from 'gi://GLib';
+import Meta from 'gi://Meta';
+import St from 'gi://St';
+
+import { DummyPipeline } from '../../conveniences/dummy_pipeline.js';
+import { PaintSignals } from '../../conveniences/paint_signals.js';
+import { PopupBlurSurfaceFade } from './surface_fade.js';
+import { PopupBlurSurfacePlacement } from './surface_placement.js';
+import { PopupBlurSurfaceSignals } from './surface_signals.js';
+import { PopupBlurSurfaceStyle } from './surface_style.js';
+import { PopupBlurSurfaceTransitions } from './surface_transitions.js';
+import { PopupBlurStaticActor } from './static_actor.js';
+
+const NOTIFICATION_STYLE_CLASSES = ['notification-banner'];
+const FULL_GEOMETRY_STYLE_CLASSES = [
+ 'popup-menu-content', 'candidate-popup-content',
+ 'quick-settings', 'quick-toggle-menu', 'screenshot-ui-panel',
+ 'notification-banner', 'snap-assistant',
+ 'osd-window', 'resize-popup', 'workspace-switcher',
+ 'modal-dialog', 'run-dialog',
+ 'bms-keyboard-surface',
+];
+const IS_HEAVY_SURFACE_STYLE_CLASSES = [
+ 'datemenu-popover', 'quick-settings', 'modal-dialog',
+ 'candidate-popup-content', 'candidate-popup-boxpointer',
+];
+const IS_QUICK_SETTINGS_STYLE_CLASSES = [
+ 'quick-toggle-menu', 'quick-settings', 'datemenu-popover',
+];
+
+export const PopupBlurSurface = class PopupBlurSurface {
+ constructor(connections, settings, effects_manager, target, root_actor, parent, sibling, corner_radius) {
+ this.connections = connections;
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.target = target;
+ this.root_actor = root_actor;
+ this.parent = parent;
+ this.sibling = sibling;
+ this.corner_radius = corner_radius;
+ this.paint_signals = new PaintSignals(connections);
+ this.placement = new PopupBlurSurfacePlacement(this);
+ this.signals = new PopupBlurSurfaceSignals(this);
+ this.style = new PopupBlurSurfaceStyle(this);
+ this.transitions = new PopupBlurSurfaceTransitions(this);
+ this.repaint_id = 0;
+ this.update_id = 0;
+ this.transition_update_id = 0;
+ this.opacity = null;
+ this.static_blur = settings.popup.STATIC_BLUR;
+ this.static_actor = null;
+ this.actor = null;
+ this.blur_actor = null;
+ }
+
+ enable() {
+ if (!this.create_actor()) {
+ if (this.static_blur)
+ this.destroy_static_actor();
+ return false;
+ }
+
+ this.fade = new PopupBlurSurfaceFade(
+ this.actor,
+ this.target,
+ this.root_actor,
+ this.parent
+ );
+ this.actor.hide();
+ this.parent.add_child(this.actor);
+ this.set_actor_position();
+ this.style.capture_target_style();
+ this.style.update_target_style();
+
+ this.connect_repaints();
+ this.signals.connect_actor(this.target);
+ this.signals.connect_actor(this.root_actor);
+ this.signals.connect_ancestors(this.target);
+ this.signals.connect_ancestors(this.root_actor);
+ if (this.style.has_any_style_class(this.target, ['screenshot-ui-panel']))
+ this.signals.connect_actor(this.parent);
+ this.signals.connect_layout();
+ this.signals.connect_settings();
+ this.queue_update();
+
+ return true;
+ }
+
+ create_actor() {
+ if (this.static_blur)
+ return this.create_static_actor();
+
+ this.blur_actor = new St.Widget({
+ name: 'bms-popup-blurred-widget',
+ reactive: false,
+ });
+ this.blur_actor.add_style_class_name('bms-popup-blurred-widget');
+ this.track_owned_actor(this.blur_actor);
+ this.pipeline = new DummyPipeline(
+ this.effects_manager,
+ this.settings.popup,
+ this.blur_actor,
+ {
+ corner_radius_key: this.corner_radius.key,
+ corner_radius_getter: () => this.get_corner_radius(),
+ }
+ );
+ this.actor = this.blur_actor;
+ return true;
+ }
+
+ create_static_actor() {
+ this.static_actor = new PopupBlurStaticActor(
+ this.settings,
+ this.effects_manager,
+ this.target,
+ this.root_actor,
+ this.parent,
+ () => this.get_corner_radius()
+ );
+ if (!this.static_actor.create())
+ return false;
+ this.sync_static_actor();
+ return true;
+ }
+
+ sync_static_actor() {
+ this.actor = this.static_actor.actor;
+ this.blur_actor = this.static_actor.blur_actor;
+ this.pipeline = this.static_actor.pipeline;
+ }
+
+ track_owned_actor(actor) {
+ this.connections.connect(actor, 'destroy', () => {
+ this.actor = null;
+ this.blur_actor = null;
+ });
+ }
+
+ set_actor_position() {
+ if (!this.is_actor_valid())
+ return;
+ const sibling = this.sibling?.get_parent() === this.parent ? this.sibling : null;
+ if (this.is_below_sibling(sibling))
+ return;
+ this.parent.set_child_below_sibling(this.actor, sibling);
+ }
+
+ is_below_sibling(sibling) {
+ const children = this.parent.get_children();
+ const actor_index = children.indexOf(this.actor);
+ if (actor_index < 0)
+ return false;
+ if (!sibling)
+ return actor_index === 0;
+ const sibling_index = children.indexOf(sibling);
+ return sibling_index >= 0 && actor_index < sibling_index;
+ }
+
+ is_quick_settings() {
+ return this.style.has_any_style_class(this.target, IS_QUICK_SETTINGS_STYLE_CLASSES)
+ || this.style.has_any_style_class(this.root_actor, IS_QUICK_SETTINGS_STYLE_CLASSES);
+ }
+
+ is_heavy_surface() {
+ return this.style.has_any_style_class(this.target, IS_HEAVY_SURFACE_STYLE_CLASSES)
+ || this.style.has_any_style_class(this.root_actor, IS_HEAVY_SURFACE_STYLE_CLASSES);
+ }
+
+ update() {
+ if (this.is_notification_surface()) {
+ this.update_live_surface();
+ return;
+ }
+ let transition_state = this.transitions.get_state();
+ let geometry = this.placement.get_surface_geometry();
+ const visible = this.is_visible();
+ const geometry_changed = this.placement.has_surface_geometry_changed(geometry);
+ if (
+ !visible
+ || !this.placement.has_valid_geometry(geometry)
+ || (!transition_state.geometry && geometry_changed)
+ )
+ transition_state = this.transitions.complete_state(transition_state);
+ if (!visible && !transition_state.running) {
+ this.hide_surface();
+ return;
+ }
+ if (!this.placement.has_valid_geometry(geometry)) {
+ if (this.placement.offscreen)
+ return this.hide_surface();
+ if (this.placement.keep_transition_visible(transition_state)) {
+ this.queue_repaint(true);
+ this.queue_transition_update(transition_state);
+ return;
+ }
+
+ if (transition_state.running) {
+ this.queue_transition_update(transition_state);
+ return;
+ }
+ this.hide_surface();
+ return;
+ }
+ this.set_actor_position();
+ if (!this.placement.update_surface_geometry(geometry))
+ return;
+ if (!this.placement.prepare_visible_geometry())
+ return;
+ const opacity = this.update_opacity(transition_state);
+ if (opacity > 0)
+ this.show_actors();
+ else
+ this.hide_actors();
+ this.queue_repaint(!visible);
+ this.queue_transition_update(transition_state);
+ }
+
+ update_live_surface() {
+ if (!this.is_visible()) {
+ this.hide_surface();
+ return;
+ }
+ this.set_actor_position();
+ const geometry = this.placement.get_unclipped_monitor_surface_geometry();
+ if (!this.placement.has_valid_geometry(geometry))
+ return this.hide_surface();
+ if (!this.placement.update_surface_geometry(geometry))
+ return;
+ if (!this.placement.prepare_visible_geometry())
+ return;
+ if (this.update_opacity() > 0)
+ this.show_actors();
+ else
+ this.hide_actors();
+ this.queue_repaint();
+ this.queue_transition_update();
+ }
+
+ hide_surface() {
+ this.opacity = 0;
+ this.update_surface_opacity(0);
+ this.hide_actors();
+ this.placement.hide();
+ }
+
+ update_opacity(transition_state = null) {
+ const opacity = this.fade.get_opacity();
+ const transition_opacity = this.get_transition_opacity(opacity, transition_state);
+ if (
+ this.opacity === transition_opacity
+ && this.has_surface_opacity(transition_opacity)
+ )
+ return transition_opacity;
+ this.update_surface_opacity(transition_opacity);
+ this.opacity = transition_opacity;
+ return transition_opacity;
+ }
+
+ get_transition_opacity(opacity, transition_state) {
+ if (
+ this.static_blur
+ || !transition_state?.geometry
+ || transition_state.opacity
+ || this.opacity <= 0
+ )
+ return opacity;
+ return Math.max(opacity, this.opacity);
+ }
+
+ has_surface_opacity(opacity) {
+ if (this.static_blur)
+ return this.static_actor.has_opacity(opacity);
+ if (!this.is_actor_valid())
+ return false;
+ return this.actor.opacity === opacity;
+ }
+
+ update_surface_opacity(opacity) {
+ const pipeline_opacity = this.get_pipeline_opacity(opacity);
+ if (this.static_blur) {
+ this.static_actor.set_opacity(opacity, pipeline_opacity);
+ return;
+ }
+ this.fade.set_opacity(opacity);
+ this.pipeline?.set_opacity_factor(pipeline_opacity / 255);
+ }
+
+ get_pipeline_opacity(opacity) {
+ if (!this.style.has_any_style_class(this.target, ['screenshot-ui-panel']))
+ return opacity;
+ return Math.round(opacity * (this.parent?.opacity ?? 255) / 255);
+ }
+
+ get_geometry_actor() {
+ return this.target;
+ }
+
+ should_use_content_geometry() {
+ return !this.uses_full_actor_geometry();
+ }
+
+ should_use_margin_geometry() {
+ return !this.uses_full_actor_geometry();
+ }
+
+ uses_full_actor_geometry() {
+ return (
+ this.style.has_any_style_class(this.target, FULL_GEOMETRY_STYLE_CLASSES)
+ || this.style.has_any_style_class(this.root_actor, FULL_GEOMETRY_STYLE_CLASSES)
+ );
+ }
+
+ is_notification_surface() {
+ return (
+ this.style.has_any_style_class(this.target, NOTIFICATION_STYLE_CLASSES)
+ || this.style.has_any_style_class(this.root_actor, NOTIFICATION_STYLE_CLASSES)
+ );
+ }
+
+ show_actors() {
+ if (this.is_actor_valid())
+ this.actor?.show();
+ }
+
+ hide_actors() {
+ if (this.is_actor_valid())
+ this.actor?.hide();
+ }
+
+ is_actor_valid() {
+ return (
+ !!this.actor
+ && (!this.static_blur || !!this.static_actor?.background_group)
+ );
+ }
+
+ queue_update() {
+ if (!this.actor || this.update_id || this.transition_update_id)
+ return;
+ this.update_id = global.compositor.get_laters().add(
+ Meta.LaterType.BEFORE_REDRAW,
+ () => {
+ this.update_id = 0;
+ if (this.actor)
+ this.update();
+ return GLib.SOURCE_REMOVE;
+ }
+ );
+ }
+
+ queue_transition_update(transition_state = null) {
+ const has_running_transition = transition_state?.running ?? this.transitions.has_running();
+ if (!this.actor || this.transition_update_id || this.update_id || !has_running_transition)
+ return;
+ this.transition_update_id = global.compositor.get_laters().add(
+ Meta.LaterType.BEFORE_REDRAW,
+ () => {
+ this.transition_update_id = 0;
+ if (this.actor)
+ this.update();
+ return GLib.SOURCE_REMOVE;
+ }
+ );
+ }
+
+ connect_repaints() {
+ if (this.static_blur || this.settings.HACKS_LEVEL !== 1)
+ return;
+ const repaint_source = {
+ queue_repaint: () => this.queue_repaint(),
+ };
+ [
+ this.blur_actor,
+ this.target,
+ ].forEach(actor => {
+ this.paint_signals.disconnect_all_for_actor(actor);
+ this.paint_signals.connect(actor, repaint_source);
+ });
+ }
+
+ update_settings() {
+ this.style.update_target_style();
+ this.static_actor?.update_settings();
+ }
+
+ get_corner_radius() {
+ return this.settings.popup[this.corner_radius.property] ?? this.settings.popup.CORNER_RADIUS;
+ }
+
+ queue_repaint(force = false) {
+ if (this.static_blur || this.repaint_id || (!force && !this.should_repaint()))
+ return;
+ this.repaint_id = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.repaint_id = 0;
+ if (force || this.should_repaint())
+ this.pipeline?.repaint_effect();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ should_repaint() {
+ return this.is_visible();
+ }
+
+ is_visible() {
+ return this.is_actor_visible(this.target) && this.is_actor_visible(this.root_actor);
+ }
+
+ is_keyboard_surface() {
+ return (
+ this.style.has_style_class(this.target, 'bms-keyboard-surface')
+ || this.style.has_style_class(this.root_actor, 'bms-keyboard-surface')
+ );
+ }
+
+ is_actor_visible(actor) {
+ if (!actor)
+ return false;
+ if (actor.visible && actor.mapped)
+ return true;
+ if (!this.is_keyboard_surface())
+ return false;
+
+ const parent = actor.get_parent();
+ return parent?.visible && parent?.mapped;
+ }
+
+ update_pipeline() {
+ if (this.static_blur) {
+ this.static_actor.update_pipeline();
+ this.sync_static_actor();
+ }
+ }
+
+ destroy() {
+ if (this.update_id) {
+ global.compositor.get_laters().remove(this.update_id);
+ this.update_id = 0;
+ }
+ if (this.transition_update_id) {
+ global.compositor.get_laters().remove(this.transition_update_id);
+ this.transition_update_id = 0;
+ }
+ if (this.repaint_id) {
+ GLib.Source.remove(this.repaint_id);
+ this.repaint_id = 0;
+ }
+
+ this.signals.disconnect_all();
+ this.paint_signals.disconnect_all_for_actor(this.blur_actor);
+ this.paint_signals.disconnect_all_for_actor(this.target);
+ this.style.restore_target_style();
+
+ if (this.static_blur)
+ this.destroy_static_actor();
+ else
+ this.destroy_dynamic_actor();
+
+ this.fade = null;
+ }
+
+ destroy_dynamic_actor() {
+ const pipeline = this.pipeline;
+ const blur_actor = this.blur_actor;
+ this.pipeline = null;
+ this.blur_actor = null;
+ this.actor = null;
+ if (pipeline)
+ pipeline.destroy();
+ if (blur_actor)
+ blur_actor.destroy();
+ }
+
+ destroy_static_actor() {
+ if (this.static_actor)
+ this.static_actor.destroy();
+ this.static_actor = null;
+ this.actor = null;
+ this.blur_actor = null;
+ this.pipeline = null;
+ this.placement.clear();
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/index.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/index.js
new file mode 100644
index 0000000..edc9369
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/index.js
@@ -0,0 +1,556 @@
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import Meta from 'gi://Meta';
+import St from 'gi://St';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import {
+ POPUP_BACKGROUND_STYLES,
+ POPUP_SURFACE_STYLES,
+ PopupBlurTargets,
+} from './targets.js';
+import { PopupBlurSurface } from './blur_surface.js';
+import { PopupBlurMessageStacks } from './message_stacks.js';
+
+const POPUP_INTERNAL_STYLE_CLASSES = ['bms-popup-blurred-widget', 'bms-popup-backgroundgroup'];
+const POPUP_INTERNAL_NAMES = ['bms-popup-blurred-widget', 'bms-popup-backgroundgroup'];
+const KEYBOARD_STYLE_CLASS = 'bms-keyboard-surface';
+
+export const PopupBlur = class PopupBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.surfaces = new Map();
+ this.containers = new Set();
+ this.queued_actors = new Set();
+ this.watched_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ this.interface_settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+ this.message_stacks = new PopupBlurMessageStacks(connections);
+ this.targets = new PopupBlurTargets(this);
+ this.queue_id = 0;
+ this.follow_up_queue_id = 0;
+ this.follow_up_actors = new Set();
+ this.reset_id = 0;
+ this.enabled = false;
+ }
+
+ enable() {
+ if (this.enabled) {
+ this._log("blur already enabled");
+ return;
+ }
+
+ this._log("blurring popup surfaces");
+ this.enabled = true;
+
+ this.update_background();
+ this.message_stacks.enable();
+ this.connections.connect(
+ this.interface_settings,
+ 'changed::color-scheme',
+ () => this.update_background()
+ );
+ this.connections.connect(
+ this.interface_settings,
+ 'changed::gtk-theme',
+ () => this.update_background()
+ );
+ this.connections.connect(
+ Main.sessionMode,
+ 'updated',
+ () => this.update_background()
+ );
+ this.connections.connect(
+ St.Settings.get(),
+ 'notify::color-scheme',
+ () => this.update_background()
+ );
+
+ this.track_container(Main.uiGroup);
+ (Main.osdWindowManager?._osdWindows ?? []).forEach(window => this.track_container(window));
+ this.track_container(Main.layoutManager?.modalDialogGroup);
+ this.track_container(Main.layoutManager?.screenShieldGroup);
+ this.track_container(Main.layoutManager?.unlockDialogGroup);
+ this.track_container(Main.messageTray?._bannerBin);
+ this.track_quick_settings();
+ this.track_container(global.window_group);
+ this.track_keyboard();
+ this.track_container(Main.keyboard?.keyboardActor);
+ }
+
+ track_container(container) {
+ if (!container || this.containers.has(container))
+ return;
+ if (!this.watch_actor(container))
+ return;
+
+ this.containers.add(container);
+ this.message_stacks.track_container(container);
+
+ this.get_children(container).forEach(child => this.try_blur(child));
+
+ this.connections.connect(
+ container,
+ 'child-added',
+ (_, child) => this.queue_try_blur(child)
+ );
+ }
+
+ track_quick_settings() {
+ const menu = Main.panel?.statusArea?.quickSettings?.menu;
+ this.track_container(menu?._overlay);
+ }
+
+ track_keyboard() {
+ const keyboard_box = Main.layoutManager?.keyboardBox;
+ this.track_container(keyboard_box);
+ if (!keyboard_box)
+ return;
+
+ this.connections.connect(
+ keyboard_box,
+ 'child-added',
+ (_, child) => this.update_keyboard_style(child)
+ );
+ }
+
+ try_blur(actor) {
+ if (this.is_internal_actor(actor) || !this.watch_actor(actor))
+ return;
+
+ this.message_stacks.scan(actor);
+ this.track_container(this.get_actor_overlay(actor));
+
+ const targets = this.get_blur_targets(actor);
+
+ if (this.targets.is_blur_target_actor(actor))
+ this.track_container(actor);
+
+ if (this.has_style_class(actor, 'switcher-popup'))
+ this.track_container(actor);
+
+ if (this.is_window_actor(actor))
+ this.track_container(actor);
+
+ targets.forEach(target => this.blur_actor(target, actor));
+ }
+
+ queue_try_blur(actor) {
+ if (this.is_internal_actor(actor) || !this.watch_actor(actor))
+ return;
+
+ this.queued_actors.add(actor);
+ if (this.queue_id)
+ return;
+
+ this.queue_id = global.compositor.get_laters().add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this.queue_id = 0;
+ const actors = [...this.queued_actors];
+ this.queued_actors.clear();
+
+ this.try_blur_actors(actors);
+ this.queue_follow_up_blurs(actors);
+
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ queue_follow_up_blurs(actors) {
+ actors.forEach(actor => this.follow_up_actors.add(actor));
+
+ if (this.follow_up_queue_id || this.follow_up_actors.size === 0)
+ return;
+
+ this.follow_up_queue_id = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.follow_up_queue_id = 0;
+ const actors = [...this.follow_up_actors];
+ this.follow_up_actors.clear();
+
+ this.try_blur_actors(actors);
+
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ try_blur_actors(actors) {
+ if (!this.enabled)
+ return;
+
+ actors.forEach(actor => {
+ if (!this.destroyed_actors.has(actor) && !this.is_internal_actor(actor))
+ this.try_blur(actor);
+ });
+ }
+
+ cancel_queued_blurs() {
+ if (this.queue_id) {
+ global.compositor.get_laters().remove(this.queue_id);
+ this.queue_id = 0;
+ }
+ if (this.follow_up_queue_id) {
+ GLib.source_remove(this.follow_up_queue_id);
+ this.follow_up_queue_id = 0;
+ }
+ this.queued_actors.clear();
+ this.follow_up_actors.clear();
+ }
+
+ blur_actor(target, root_actor) {
+ if (
+ this.is_internal_actor(target)
+ || this.is_internal_actor(root_actor)
+ || !this.watch_actor(target)
+ || !this.watch_actor(root_actor)
+ )
+ return;
+
+ if (this.surfaces.has(target))
+ return;
+
+ this.destroy_blurred_ancestors(target);
+
+ const { parent, sibling } = this.get_overlay_parent(root_actor, target);
+ const surface = new PopupBlurSurface(
+ this.connections,
+ this.settings,
+ this.effects_manager,
+ target,
+ root_actor,
+ parent,
+ sibling,
+ this.get_corner_radius(target, root_actor),
+ () => this.enabled && this.surfaces.has(target)
+ );
+
+ this.surfaces.set(target, surface);
+
+ if (surface.enable())
+ return this.connect_surface(target, root_actor);
+
+ this.surfaces.delete(target);
+ surface.destroy();
+ }
+
+ connect_surface(target, root_actor) {
+ this.connections.connect(
+ target,
+ 'destroy',
+ () => this.destroy_blur(target)
+ );
+
+ if (root_actor !== target) {
+ this.connections.connect(
+ root_actor,
+ 'destroy',
+ () => this.destroy_blur(target)
+ );
+ }
+ }
+
+ destroy_blurred_ancestors(actor) {
+ actor = actor ? actor.get_parent() : null;
+
+ while (actor && !this.destroyed_actors.has(actor)) {
+ if (this.surfaces.has(actor) && this.targets.prefers_descendant_targets(actor)) {
+ this.destroy_blur(actor);
+ }
+
+ actor = actor.get_parent();
+ }
+ }
+
+ get_overlay_parent(root_actor, target) {
+ if (this.has_any_style_class(target, ['screenshot-ui-panel'])) {
+ const parent = Main.screenshotUI ?? Main.uiGroup;
+ let sibling = target;
+ while (sibling && sibling.get_parent() && sibling.get_parent() !== parent) {
+ sibling = sibling.get_parent();
+ }
+ return {
+ parent,
+ sibling: sibling ?? target,
+ };
+ }
+
+ let actor = root_actor;
+ let child = null;
+ while (actor && !this.destroyed_actors.has(actor)) {
+ let parent = null;
+ parent = actor.get_parent();
+
+ if (!parent)
+ break;
+
+ if (parent === Main.layoutManager.unlockDialogGroup ||
+ parent === Main.layoutManager.screenShieldGroup ||
+ parent === Main.uiGroup ||
+ parent === Main.layoutManager.uiGroup)
+ return { parent, sibling: actor };
+
+ if (parent === global.window_group)
+ return { parent: actor, sibling: child ?? actor.get_last_child() };
+
+ child = actor;
+ actor = parent;
+ }
+
+ return { parent: Main.uiGroup, sibling: null };
+ }
+
+ get_corner_radius(target, root_actor) {
+ return this.targets.get_corner_radius(target, root_actor);
+ }
+
+ get_blur_targets(actor) {
+ return this.targets.find(actor);
+ }
+
+ has_any_style_class(actor, style_classes) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+
+ if (actor.get_style_class_name) {
+ const class_names = actor.get_style_class_name();
+ if (typeof class_names === 'string') {
+ const normalized = ` ${class_names.trim().replace(/\s+/g, ' ')} `;
+ return style_classes.some(style_class => normalized.includes(` ${style_class} `));
+ }
+ }
+
+ return style_classes.some(style => this.has_style_class(actor, style));
+ }
+
+ has_style_class(actor, style_class) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+
+ if (actor.has_style_class_name)
+ return actor.has_style_class_name(style_class);
+
+ if (actor.get_style_class_name)
+ return (actor.get_style_class_name() ?? '').split(/\s+/).includes(style_class);
+
+ return false;
+ }
+
+ is_window_actor(actor) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+ return actor.get_parent() === global.window_group;
+ }
+
+ is_internal_actor(actor) {
+ if (!actor)
+ return false;
+
+ if (this.has_any_style_class(actor, POPUP_INTERNAL_STYLE_CLASSES))
+ return true;
+
+ if (this.destroyed_actors.has(actor))
+ return false;
+
+ return POPUP_INTERNAL_NAMES.includes(actor.name ?? actor.get_name());
+ }
+
+ watch_actor(actor) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+ if (this.watched_actors.has(actor))
+ return true;
+
+ this.connections.connect(actor, 'destroy', () => {
+ this.destroyed_actors.add(actor);
+ this.containers.delete(actor);
+ });
+
+ this.watched_actors.add(actor);
+ return true;
+ }
+
+ get_children(actor) {
+ if (!this.watch_actor(actor))
+ return [];
+
+ return actor.get_children();
+ }
+
+ get_actor_delegate(actor) {
+ if (!this.watch_actor(actor))
+ return null;
+
+ return actor._delegate || null;
+ }
+
+ get_actor_overlay(actor) {
+ const delegate = this.get_actor_delegate(actor);
+ return (delegate && delegate._overlay) || null;
+ }
+
+ get_actor_dialog_layout(actor) {
+ if (!this.watch_actor(actor))
+ return null;
+
+ return actor.dialogLayout || null;
+ }
+
+ destroy_blur(actor) {
+ const surface = this.surfaces.get(actor);
+ if (!surface)
+ return;
+
+ this.surfaces.delete(actor);
+ surface.destroy();
+ }
+
+ reset() {
+ if (!this.enabled)
+ return;
+
+ this.cancel_queued_blurs();
+
+ this.disable();
+ this.reset_id = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.reset_id = 0;
+
+ if (this.settings.popup.BLUR)
+ this.enable();
+
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ update_pipeline() {
+ this.surfaces.forEach(surface => surface.update_pipeline());
+ }
+
+ _get_keyboard_actors() {
+ const actors = new Set();
+ if (Main.keyboard && Main.keyboard.keyboardActor)
+ actors.add(Main.keyboard.keyboardActor);
+ if (Main.keyboard && Main.keyboard._keyboard)
+ actors.add(Main.keyboard._keyboard);
+ if (Main.layoutManager.keyboardBox)
+ Main.layoutManager.keyboardBox.get_children().forEach(child => actors.add(child));
+ return [...actors];
+ }
+
+ update_background() {
+ [...POPUP_BACKGROUND_STYLES, ...POPUP_SURFACE_STYLES]
+ .forEach(style => Main.uiGroup.remove_style_class_name(style));
+
+ const keyboard_actors = this._get_keyboard_actors();
+ keyboard_actors.forEach(actor => this.clear_keyboard_style(actor));
+ [...POPUP_BACKGROUND_STYLES, ...POPUP_SURFACE_STYLES]
+ .forEach(style => keyboard_actors.forEach(actor => actor.remove_style_class_name(style)));
+
+ let background_style = null;
+ if (this.settings.popup.OVERRIDE_BACKGROUND) {
+ const style = this.get_background_style();
+ background_style = this.settings.popup.PRESERVE_SHELL_THEME ?
+ POPUP_SURFACE_STYLES[style] :
+ POPUP_BACKGROUND_STYLES[style];
+ Main.uiGroup.add_style_class_name(
+ background_style
+ );
+ }
+ keyboard_actors.forEach(actor => this.update_keyboard_style(actor, background_style));
+ this.surfaces.forEach(surface => surface.update_settings());
+ }
+
+ clear_keyboard_style(actor, remove_marker = false) {
+ if (!actor)
+ return;
+
+ if (remove_marker)
+ actor.remove_style_class_name(KEYBOARD_STYLE_CLASS);
+
+ if (!this.settings.popup.OVERRIDE_BACKGROUND)
+ return;
+
+ [...POPUP_BACKGROUND_STYLES, ...POPUP_SURFACE_STYLES]
+ .forEach(style => actor.remove_style_class_name(style));
+ }
+
+ update_keyboard_style(actor, background_style = null) {
+ if (!actor)
+ return;
+
+ this.clear_keyboard_style(actor);
+ actor.add_style_class_name(KEYBOARD_STYLE_CLASS);
+ if (!this.settings.popup.OVERRIDE_BACKGROUND)
+ return;
+
+ const style = background_style ?? (
+ this.settings.popup.PRESERVE_SHELL_THEME ?
+ POPUP_SURFACE_STYLES[this.get_background_style()] :
+ POPUP_BACKGROUND_STYLES[this.get_background_style()]
+ );
+ actor.add_style_class_name(style);
+ }
+
+ get_background_style() {
+ const style = this.settings.popup.STYLE_POPUP;
+
+ if (style >= 0 && style < POPUP_BACKGROUND_STYLES.length)
+ return style;
+
+ const theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
+ let uri = theme?.default_stylesheet?.get_uri();
+ if (theme?.application_stylesheet)
+ uri = theme?.application_stylesheet?.get_uri();
+ const lower_uri = typeof uri === 'string' ? uri.toLowerCase() : '';
+ if (lower_uri.includes('yaru'))
+ return lower_uri.includes('dark') ? 2 : 1;
+
+ const shell_style = Main.getStyleVariant();
+ if (shell_style === 'light')
+ return 1;
+
+ if (shell_style === 'dark')
+ return 2;
+
+ return this.interface_settings.get_string('color-scheme') === 'prefer-dark' ? 2 : 1;
+ }
+
+ disable() {
+ if (this.reset_id) {
+ GLib.source_remove(this.reset_id);
+ this.reset_id = 0;
+ }
+
+ this.cancel_queued_blurs();
+
+ if (!this.enabled) {
+ this._log("blur already removed");
+ return;
+ }
+
+ this._log("removing blur from popup surfaces");
+ this.enabled = false;
+
+ const keyboard_actors = this._get_keyboard_actors();
+ keyboard_actors.forEach(actor => this.clear_keyboard_style(actor, true));
+ [...POPUP_BACKGROUND_STYLES, ...POPUP_SURFACE_STYLES]
+ .forEach(style => keyboard_actors.forEach(actor => actor.remove_style_class_name(style)));
+
+ [...POPUP_BACKGROUND_STYLES, ...POPUP_SURFACE_STYLES]
+ .forEach(style => Main.uiGroup.remove_style_class_name(style));
+
+ const actors = [...this.surfaces.keys()];
+ actors.forEach(actor => this.destroy_blur(actor));
+ this.surfaces.clear();
+ this.containers.clear();
+ this.watched_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ this.message_stacks.disable();
+
+ this.connections.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > popup blur] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/message_stacks.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/message_stacks.js
new file mode 100644
index 0000000..cc4af30
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/message_stacks.js
@@ -0,0 +1,571 @@
+import GLib from 'gi://GLib';
+
+const STACKED_PSEUDO_CLASSES = ['second-in-stack', 'lower-in-stack'];
+const MESSAGE_CONTAINER_STYLE_CLASSES = ['message-list', 'message-view', 'message-notification-group'];
+const INTERNAL_STYLE_CLASSES = ['bms-popup-blurred-widget', 'bms-popup-backgroundgroup'];
+const INTERNAL_NAMES = ['bms-popup-blurred-widget', 'bms-popup-backgroundgroup'];
+
+export const PopupBlurMessageStacks = class PopupBlurMessageStacks {
+ constructor(connections) {
+ this.connections = connections;
+ this.containers = new Set();
+ this.groups = new Set();
+ this.messages = new Set();
+ this.queued_actors = new WeakSet();
+ this.watched_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ this.bms_clipped = new WeakSet();
+ this.group_connections = new Map();
+ this.update_ids = new Map();
+ this.original_opacity = new WeakMap();
+ this.original_message_opacity = new WeakMap();
+ this.enabled = false;
+ }
+
+ enable() {
+ this.enabled = true;
+ }
+
+ track_container(container) {
+ if (!container || this.containers.has(container))
+ return;
+ if (!this.watch_actor(container))
+ return;
+
+ this.containers.add(container);
+ this.scan(container);
+
+ this.connect(
+ container,
+ 'child-added',
+ (_, child) => {
+ this.queue_scan(child);
+ this.queue_update_all();
+ }
+ );
+
+ this.connect(
+ container,
+ 'child-removed',
+ () => this.queue_update_all()
+ );
+ }
+
+ queue_scan(actor) {
+ if (this.is_internal_actor(actor) || !this.watch_actor(actor) || this.queued_actors.has(actor))
+ return;
+
+ this.queued_actors.add(actor);
+ this.scan(actor);
+ this.queued_actors.delete(actor);
+ }
+
+ scan(actor, seen = new WeakSet()) {
+ if (this.is_internal_actor(actor) || !this.watch_actor(actor) || seen.has(actor))
+ return;
+
+ seen.add(actor);
+
+ if (this.has_any_style_class(actor, MESSAGE_CONTAINER_STYLE_CLASSES))
+ this.track_container(actor);
+
+ if (this.has_style_class(actor, 'message-notification-group'))
+ this.track_group(actor);
+
+ if (this.has_style_class(actor, 'message'))
+ this.track_message(actor);
+
+ this.get_children(actor).forEach(child => this.scan(child, seen));
+
+ const child = this.get_child(actor);
+ if (child)
+ this.scan(child, seen);
+ }
+
+ track_message(message) {
+ if (this.is_internal_actor(message) || !this.watch_actor(message) || this.messages.has(message))
+ return;
+
+ this.messages.add(message);
+ this.update_message(message);
+
+ this.connect(
+ message,
+ 'style-changed',
+ () => this.queue_update_message(message)
+ );
+
+ this.connect(
+ message,
+ 'notify::pseudo-class',
+ () => this.queue_update_message(message)
+ );
+
+ this.connect(
+ message,
+ 'notify::allocation',
+ () => this.queue_update_message(message)
+ );
+
+ this.connect(
+ message,
+ 'destroy',
+ () => {
+ this.cancel_update(message);
+ this.remove_stack_mask(message);
+ this.messages.delete(message);
+ }
+ );
+ }
+
+ track_group(group) {
+ if (!this.watch_actor(group) || this.groups.has(group))
+ return;
+
+ this.groups.add(group);
+ this.update_group_header(group);
+
+ this.connect(
+ group,
+ 'notify::expanded',
+ () => this.update_all()
+ );
+
+ this.connect(
+ group,
+ 'notify::allocation',
+ () => this.update_group_header(group)
+ );
+
+ if (group.layout_manager) {
+ const lm = group.layout_manager;
+ const id = lm.connect('notify::expansion', () => {
+ this.update_group_header(group);
+ this.update_group_messages(group);
+ });
+ this.group_connections.set(group, { lm, id });
+ }
+ }
+
+ untrack_group(group) {
+ if (this.group_connections.has(group)) {
+ const { lm, id } = this.group_connections.get(group);
+ lm.disconnect(id);
+ this.group_connections.delete(group);
+ }
+ this.groups.delete(group);
+ }
+
+ update_all() {
+ this.groups.forEach(group => {
+ this.update_group_header(group);
+ this.update_group_messages(group);
+ });
+ this.messages.forEach(message => {
+ if (!this.get_message_group(message))
+ this.update_message(message);
+ });
+ }
+
+ update_group_header(group) {
+ if (!this.watch_actor(group))
+ return;
+
+ const header = group._headerBox;
+ if (!header)
+ return;
+
+ const expansion = group.layout_manager?.expansion ?? (group.expanded ? 1 : 0);
+ const target_opacity = this.enabled ? Math.round(expansion * 255) : 255;
+
+ header.opacity = target_opacity;
+ }
+
+ get_group_messages(group) {
+ const list = [];
+ const find_messages = actor => {
+ if (this.has_style_class(actor, 'message')) {
+ list.push(actor);
+ return;
+ }
+ this.get_children(actor).forEach(find_messages);
+ };
+ find_messages(group);
+ return list;
+ }
+
+ update_group_messages(group) {
+ if (!this.enabled || !this.watch_actor(group))
+ return;
+
+ const expansion = group.layout_manager?.expansion ?? (group.expanded ? 1.0 : 0.0);
+ const messages = this.get_group_messages(group);
+
+ messages.forEach((message, index) => {
+ this.update_message_with_expansion(message, index, expansion, group);
+ });
+ }
+
+ queue_update_all() {
+ this.messages.forEach(message => this.queue_update_message(message));
+ }
+
+ queue_update_message(message) {
+ if (this.update_ids.has(message))
+ return;
+
+ const id = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ this.update_ids.delete(message);
+
+ if (this.enabled && this.messages.has(message))
+ this.update_message(message);
+
+ return GLib.SOURCE_REMOVE;
+ });
+
+ this.update_ids.set(message, id);
+ }
+
+ update_message(message) {
+ if (!this.enabled) {
+ this.remove_stack_mask(message);
+ return;
+ }
+
+ const group = this.get_message_group(message);
+ if (group) {
+ const expansion = group.layout_manager?.expansion ?? (group.expanded ? 1.0 : 0.0);
+ const index = this.get_message_index_in_group(message);
+ this.update_message_with_expansion(message, index >= 0 ? index : 0, expansion, group);
+ } else {
+ const is_expanded = this.is_in_expanded_group(message);
+ if (this.is_stacked(message) && !is_expanded) {
+ this.apply_stack_mask(message);
+ this.hide_message_content(message);
+ this.restore_message_opacity(message);
+ } else {
+ this.remove_stack_mask(message);
+ }
+ }
+ }
+
+ update_message_with_expansion(message, index, expansion, group) {
+ if (!this.enabled || !this.watch_actor(message))
+ return;
+
+ const is_stacked = this.is_stacked(message) || index > 0;
+
+ if (!is_stacked) {
+ this.remove_stack_mask(message);
+ return;
+ }
+
+ if (expansion >= 0.99) {
+ this.remove_stack_mask(message);
+ return;
+ }
+
+ if (expansion <= 0.01) {
+ this.apply_stack_mask(message);
+ this.hide_message_content(message);
+ this.restore_message_opacity(message);
+ return;
+ }
+
+ // 0.01 < expansion < 0.99: Animating unravel / collapse
+ const height = message.height || message.get_height() || 0;
+ const width = message.width || message.get_width() || 0;
+
+ if (height <= 0 || width <= 0)
+ return;
+
+ const base_edge = (index === 1) ? 10 : (index === 2 ? 7 : 0);
+ const visible_edge = Math.round(base_edge + (height - base_edge) * expansion);
+ const clip_y = Math.max(0, height - visible_edge);
+
+ message.set_clip(0, clip_y, width, visible_edge);
+ this.bms_clipped.add(message);
+
+ // Staggered opacity progress for cards so they smoothly fade in as they unravel
+ const stagger_start = Math.min(0.8, index * 0.04);
+ const progress = Math.min(1.0, Math.max(0.0, (expansion - stagger_start) / (1.0 - stagger_start)));
+ const target_alpha = Math.round(255 * progress);
+
+ this.set_child_opacity(message, target_alpha);
+
+ if (index >= 3) {
+ this.set_message_opacity(message, target_alpha);
+ } else {
+ this.restore_message_opacity(message);
+ }
+ }
+
+ apply_stack_mask(message) {
+ if (!this.watch_actor(message))
+ return;
+
+ const height = message.height || message.get_height() || 0;
+ const width = message.width || message.get_width() || 0;
+
+ if (height <= 0 || width <= 0)
+ return;
+
+ const index = this.get_message_index_in_group(message);
+
+ if (index === 1) {
+ // 2nd card: tight visible strip of 10px (matches exact 10px bottom offset below card 1)
+ const visible_edge = 10;
+ const clip_y = Math.max(0, height - visible_edge);
+ message.set_clip(0, clip_y, width, visible_edge);
+ this.bms_clipped.add(message);
+ } else if (index === 2) {
+ // 3rd card: tight visible strip of 7px (matches exact 7px bottom offset below card 2)
+ const visible_edge = 7;
+ const clip_y = Math.max(0, height - visible_edge);
+ message.set_clip(0, clip_y, width, visible_edge);
+ this.bms_clipped.add(message);
+ } else if (index >= 3) {
+ // 4th+ cards: hidden when collapsed so they don't stack behind card 3
+ message.set_clip(0, height, width, 0);
+ this.bms_clipped.add(message);
+ } else {
+ // Fallback by pseudo class
+ const is_second = this.has_pseudo_class(message, 'second-in-stack');
+ const visible_edge = is_second ? 10 : 6;
+ const clip_y = Math.max(0, height - visible_edge);
+ message.set_clip(0, clip_y, width, visible_edge);
+ this.bms_clipped.add(message);
+ }
+ }
+
+ remove_stack_mask(message) {
+ if (!message || this.destroyed_actors.has(message))
+ return;
+
+ if (this.bms_clipped.has(message)) {
+ message.remove_clip();
+ this.bms_clipped.delete(message);
+ }
+
+ this.restore_message_content(message);
+ this.restore_message_opacity(message);
+ }
+
+ set_child_opacity(message, opacity) {
+ const child = this.get_child(message);
+ if (!child)
+ return;
+
+ if (!this.original_opacity.has(child))
+ this.original_opacity.set(child, child.opacity ?? 255);
+
+ child.opacity = opacity;
+ }
+
+ hide_message_content(message) {
+ this.set_child_opacity(message, 0);
+ }
+
+ restore_message_content(message) {
+ const child = this.get_child(message);
+ if (!child)
+ return;
+
+ if (this.original_opacity.has(child)) {
+ child.opacity = this.original_opacity.get(child);
+ this.original_opacity.delete(child);
+ }
+ }
+
+ set_message_opacity(message, opacity) {
+ if (!this.watch_actor(message))
+ return;
+
+ if (!this.original_message_opacity.has(message))
+ this.original_message_opacity.set(message, message.opacity ?? 255);
+
+ message.opacity = opacity;
+ }
+
+ restore_message_opacity(message) {
+ if (!message || this.destroyed_actors.has(message))
+ return;
+
+ if (this.original_message_opacity.has(message)) {
+ message.opacity = this.original_message_opacity.get(message);
+ this.original_message_opacity.delete(message);
+ }
+ }
+
+ get_message_index_in_group(message) {
+ const group = this.get_message_group(message);
+ if (!group)
+ return -1;
+
+ const list = [];
+ const find_messages = actor => {
+ if (this.has_style_class(actor, 'message')) {
+ list.push(actor);
+ return;
+ }
+ actor.get_children().forEach(find_messages);
+ };
+ find_messages(group);
+ return list.indexOf(message);
+ }
+
+ has_pseudo_class(actor, pseudo_class) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+
+ if (actor.has_style_pseudo_class)
+ return actor.has_style_pseudo_class(pseudo_class);
+
+ if (actor.get_style_pseudo_class)
+ return (actor.get_style_pseudo_class() ?? '').split(/\s+/).includes(pseudo_class);
+
+ return false;
+ }
+
+ get_message_group(message) {
+ let actor = message ? message.get_parent() : null;
+
+ while (actor && !this.destroyed_actors.has(actor)) {
+ if (this.has_style_class(actor, 'message-notification-group'))
+ return actor;
+ actor = actor.get_parent();
+ }
+ return null;
+ }
+
+ is_in_expanded_group(message) {
+ let actor = message ? message.get_parent() : null;
+
+ while (actor && !this.destroyed_actors.has(actor)) {
+ if (
+ this.has_style_class(actor, 'message-notification-group')
+ && actor.expanded
+ )
+ return true;
+ actor = actor.get_parent();
+ }
+
+ return false;
+ }
+
+ is_stacked(message) {
+ if (!message || this.destroyed_actors.has(message))
+ return false;
+ return STACKED_PSEUDO_CLASSES.some(pseudo_class => {
+ if (message.has_style_pseudo_class)
+ return message.has_style_pseudo_class(pseudo_class);
+
+ if (message.get_style_pseudo_class)
+ return (message.get_style_pseudo_class() ?? '').split(/\s+/).includes(pseudo_class);
+
+ return false;
+ });
+ }
+
+ has_any_style_class(actor, style_classes) {
+ return style_classes.some(style_class => this.has_style_class(actor, style_class));
+ }
+
+ is_internal_actor(actor) {
+ if (!actor)
+ return false;
+
+ if (this.has_any_style_class(actor, INTERNAL_STYLE_CLASSES))
+ return true;
+
+ if (this.destroyed_actors.has(actor))
+ return false;
+
+ return INTERNAL_NAMES.includes(actor.name ?? actor.get_name());
+ }
+
+ has_style_class(actor, style_class) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+
+ if (actor.has_style_class_name)
+ return actor.has_style_class_name(style_class);
+
+ if (actor.get_style_class_name)
+ return (actor.get_style_class_name() ?? '').split(/\s+/).includes(style_class);
+
+ return false;
+ }
+
+ watch_actor(actor) {
+ if (!actor || this.destroyed_actors.has(actor))
+ return false;
+ if (this.watched_actors.has(actor))
+ return true;
+
+ this.connections.connect(actor, 'destroy', () => {
+ this.destroyed_actors.add(actor);
+ this.containers.delete(actor);
+ if (this.groups.has(actor)) {
+ this.untrack_group(actor);
+ }
+ this.messages.delete(actor);
+ this.cancel_update(actor);
+ });
+
+ this.watched_actors.add(actor);
+ return true;
+ }
+
+ get_children(actor) {
+ if (this.is_internal_actor(actor) || !this.watch_actor(actor))
+ return [];
+
+ return actor.get_children();
+ }
+
+ get_child(actor) {
+ if (!this.watch_actor(actor))
+ return null;
+
+ return (actor.get_child ? actor.get_child() : actor.child) || null;
+ }
+
+ cancel_update(message) {
+ const id = this.update_ids.get(message);
+ if (!id)
+ return;
+
+ GLib.source_remove(id);
+ this.update_ids.delete(message);
+ }
+
+ connect(actor, signal, handler) {
+ this.connections.connect(actor, signal, handler);
+ }
+
+ disable() {
+ this.update_ids.forEach(id => GLib.source_remove(id));
+ this.update_ids.clear();
+ this.messages.forEach(message => this.remove_stack_mask(message));
+ this.groups.forEach(group => {
+ if (group._headerBox) {
+ group._headerBox.opacity = 255;
+ }
+ this.untrack_group(group);
+ });
+ this.group_connections.forEach(({ lm, id }) => {
+ lm.disconnect(id);
+ });
+ this.group_connections.clear();
+ this.messages.clear();
+ this.groups.clear();
+ this.containers.clear();
+ this.watched_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ this.bms_clipped = new WeakSet();
+ this.original_opacity = new WeakMap();
+ this.original_message_opacity = new WeakMap();
+ this.enabled = false;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_actor.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_actor.js
new file mode 100644
index 0000000..c933b36
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_actor.js
@@ -0,0 +1,301 @@
+import Meta from 'gi://Meta';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { Pipeline } from '../../conveniences/pipeline.js';
+import { transform_to_actor_space } from './surface_geometry.js';
+import { PopupBlurStaticCorner } from './static_corner.js';
+import { has_style_class } from './targets.js';
+
+export const PopupBlurStaticActor = class PopupBlurStaticActor {
+ constructor(settings, effects_manager, target, root_actor, parent, get_corner_radius) {
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.target = target;
+ this.root_actor = root_actor;
+ this.parent = parent;
+ this.get_corner_radius = get_corner_radius;
+ this.static_corner = new PopupBlurStaticCorner(effects_manager, get_corner_radius);
+ this.background_group = null;
+ this.blur_actor = null;
+ this.bg_manager = null;
+ this.pipeline = null;
+ this.monitor_index = null;
+ this.opacity_factor = 1;
+ this.background_opacity = null;
+ this.x = null;
+ this.y = null;
+ this.width = null;
+ this.height = null;
+ }
+
+ create() {
+ this.background_group = new Meta.BackgroundGroup({
+ name: 'bms-popup-backgroundgroup',
+ width: 0,
+ height: 0,
+ });
+ this.background_group.hide();
+ this.background_group.connect('destroy', () => {
+ this.background_group = null;
+ });
+
+ return this.update_background();
+ }
+
+ get actor() {
+ return this.background_group;
+ }
+
+ update_background(monitor_index = null) {
+ const monitor = this.find_monitor(monitor_index);
+ if (!monitor)
+ return false;
+
+ if (monitor.index === this.monitor_index && this.blur_actor)
+ return true;
+
+ this.destroy_background();
+
+ const bg_manager_list = [];
+ const pipeline = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.popup.PIPELINE,
+ null,
+ {
+ effect_overrides: {
+ native_static_gaussian_blur: params => this.get_blur_effect_overrides(params, 'unscaled_radius'),
+ gaussian_blur: params => this.get_blur_effect_overrides(params, 'radius'),
+ monte_carlo_blur: params => this.get_blur_effect_overrides(params, 'radius'),
+ downscale: () => this.get_texture_effect_overrides(),
+ upscale: () => this.get_texture_effect_overrides(),
+ pixelize: () => this.get_texture_effect_overrides(),
+ derivative: () => this.get_texture_effect_overrides(),
+ refraction: () => this.get_texture_effect_overrides(),
+ color: params => this.get_color_effect_overrides(params),
+ luminosity: () => this.get_luminosity_effect_overrides(),
+ noise: params => this.get_noise_effect_overrides(params),
+ rgb_to_hsl: () => this.get_texture_effect_overrides(),
+ hsl_to_rgb: () => this.get_texture_effect_overrides(),
+ corner: () => ({ radius: this.get_corner_radius() }),
+ },
+ }
+ );
+
+ this.blur_actor = pipeline.create_background_with_effects(
+ monitor.index,
+ bg_manager_list,
+ this.background_group,
+ 'bms-popup-blurred-widget'
+ );
+ this.blur_actor.connect('destroy', () => {
+ this.blur_actor = null;
+ });
+ this.blur_actor.hide();
+ this.bg_manager = bg_manager_list[0];
+ this.pipeline = pipeline;
+ this.monitor_index = monitor.index;
+ this.static_corner.bind(this.pipeline, this.blur_actor);
+
+ return true;
+ }
+
+ find_monitor(monitor_index = null) {
+ if (monitor_index !== null)
+ return Main.layoutManager.monitors[monitor_index] || null;
+
+ return (
+ Main.layoutManager.findMonitorForActor(this.target)
+ ?? Main.layoutManager.findMonitorForActor(this.root_actor)
+ ?? Main.layoutManager.primaryMonitor
+ );
+ }
+
+ is_screenshot_ui() {
+ return has_style_class(this.target, 'screenshot-ui-panel')
+ || has_style_class(this.root_actor, 'screenshot-ui-panel');
+ }
+
+ update_geometry(target_x, target_y, width, height, monitor_index = null) {
+ if (!this.update_background(monitor_index))
+ return false;
+ if (!this.blur_actor)
+ return false;
+
+ const monitor = Main.layoutManager.monitors[this.monitor_index];
+ if (!monitor)
+ return false;
+
+ const monitor_geometry = transform_to_actor_space(this.parent, monitor);
+ const target_geometry = transform_to_actor_space(this.parent, {
+ x: target_x,
+ y: target_y,
+ width,
+ height,
+ });
+ if (!monitor_geometry || !target_geometry)
+ return false;
+
+ const clip_x = Math.round(target_geometry.x - monitor_geometry.x);
+ const clip_y = Math.round(target_geometry.y - monitor_geometry.y);
+ const clip_width = Math.ceil(target_geometry.width);
+ const clip_height = Math.ceil(target_geometry.height);
+
+ if (!this.blur_actor)
+ return false;
+
+ if (this.is_screenshot_ui() && this.background_group) {
+ this.background_group.set_position(0, 0);
+ this.background_group.set_size(monitor_geometry.width, monitor_geometry.height);
+ }
+
+ if (this.blur_actor.x !== monitor_geometry.x || this.blur_actor.y !== monitor_geometry.y)
+ this.blur_actor.set_position(monitor_geometry.x, monitor_geometry.y);
+ if (
+ this.blur_actor.width !== monitor_geometry.width
+ || this.blur_actor.height !== monitor_geometry.height
+ )
+ this.blur_actor.set_size(monitor_geometry.width, monitor_geometry.height);
+
+ if (
+ this.x !== clip_x
+ || this.y !== clip_y
+ || this.width !== clip_width
+ || this.height !== clip_height
+ ) {
+ this.blur_actor.set_clip(clip_x, clip_y, clip_width, clip_height);
+ this.x = clip_x;
+ this.y = clip_y;
+ this.width = clip_width;
+ this.height = clip_height;
+ }
+
+ this.blur_actor.show();
+
+ return { x: clip_x, y: clip_y, width: clip_width, height: clip_height };
+ }
+
+ has_opacity(opacity) {
+ if (this.background_opacity !== opacity)
+ return false;
+
+ const background_actor = this.get_background_actor();
+ return !background_actor || background_actor.opacity === opacity;
+ }
+
+ set_opacity(opacity, pipeline_opacity = opacity) {
+ this.set_opacity_factor(pipeline_opacity / 255);
+ if (this.background_group)
+ this.background_group.opacity = 255;
+ if (this.blur_actor)
+ this.blur_actor.opacity = 255;
+
+ this.background_opacity = opacity;
+
+ const background_actor = this.get_background_actor();
+ if (background_actor)
+ background_actor.opacity = opacity;
+
+ this.blur_actor?.get_children().forEach(child => child.opacity = opacity);
+ }
+
+ set_opacity_factor(opacity_factor) {
+ opacity_factor = Math.max(0, Math.min(1, opacity_factor));
+ if (this.opacity_factor === opacity_factor)
+ return;
+
+ this.opacity_factor = opacity_factor;
+ this.pipeline?.apply_effect_overrides();
+ }
+
+ get_blur_effect_overrides(params, radius_key) {
+ const overrides = {};
+
+ if (radius_key in params)
+ overrides[radius_key] = params[radius_key] * this.opacity_factor;
+ if ('brightness' in params)
+ overrides.brightness = 1 - (1 - params.brightness) * this.opacity_factor;
+
+ return overrides;
+ }
+
+ get_color_effect_overrides(params) {
+ const overrides = this.get_texture_effect_overrides();
+
+ if (Array.isArray(params.color) && params.color.length >= 4)
+ overrides.color = params.color;
+
+ return overrides;
+ }
+
+ get_luminosity_effect_overrides() {
+ return this.get_texture_effect_overrides();
+ }
+
+ get_noise_effect_overrides(params) {
+ const overrides = this.get_texture_effect_overrides();
+
+ if ('noise' in params)
+ overrides.noise = params.noise;
+
+ return overrides;
+ }
+
+ get_texture_effect_overrides() {
+ return {
+ opacity_factor: this.opacity_factor,
+ };
+ }
+
+ get_background_actor() {
+ return this.bg_manager ? (this.bg_manager.backgroundActor || null) : null;
+ }
+
+ update_settings() {
+ this.static_corner.update();
+ }
+
+ update_pipeline() {
+ this.bg_manager?._bms_pipeline.change_pipeline_to(this.settings.popup.PIPELINE);
+ this.static_corner.update();
+ }
+
+ destroy() {
+ const background_group = this.background_group;
+ this.destroy_background();
+ this.background_group = null;
+
+ if (background_group)
+ background_group.destroy();
+ }
+
+ destroy_background() {
+ const bg_manager = this.bg_manager;
+ const blur_actor = this.blur_actor;
+ this.bg_manager = null;
+ this.blur_actor = null;
+ this.pipeline = null;
+
+ this.static_corner.destroy();
+
+ if (bg_manager) {
+ if (bg_manager._bms_pipeline) {
+ bg_manager._bms_pipeline.destroy();
+ bg_manager._bms_pipeline = null;
+ }
+ if (!blur_actor)
+ bg_manager.backgroundActor = null;
+ bg_manager.destroy();
+ } else if (this.background_group) {
+ this.background_group.destroy_all_children();
+ }
+ blur_actor?.destroy();
+
+ this.monitor_index = null;
+ this.background_opacity = null;
+ this.x = null;
+ this.y = null;
+ this.width = null;
+ this.height = null;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_corner.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_corner.js
new file mode 100644
index 0000000..e575e09
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/static_corner.js
@@ -0,0 +1,70 @@
+export const PopupBlurStaticCorner = class PopupBlurStaticCorner {
+ constructor(effects_manager, get_radius) {
+ this.effects_manager = effects_manager;
+ this.get_radius = get_radius;
+ this.pipeline = null;
+ this.actor = null;
+ this.effect = null;
+ }
+
+ bind(pipeline, actor) {
+ this.pipeline = pipeline;
+ this.actor = actor;
+ this.update();
+ }
+
+ update() {
+ if (!this.pipeline || !this.actor)
+ return;
+
+ this.pipeline.apply_effect_overrides('corner');
+
+ if (this.pipeline_has_corner_effect()) {
+ this.destroy_external_effect();
+ return;
+ }
+
+ if (!this.effect) {
+ this.effect = this.effects_manager.new_corner_effect({
+ radius: this.get_radius(),
+ });
+ this.attach_external_effect_as_outermost();
+ return;
+ }
+
+ this.effect.radius = this.get_radius();
+ }
+
+ attach_external_effect_as_outermost() {
+ const existing_effects = this.pipeline?.effects ?? [];
+
+ existing_effects.forEach(effect => {
+ this.actor.remove_effect(effect);
+ });
+
+ this.actor.add_effect(this.effect);
+
+ existing_effects.forEach(effect => {
+ this.actor.add_effect(effect);
+ });
+ }
+
+ pipeline_has_corner_effect() {
+ const corner_class = this.effects_manager.SUPPORTED_EFFECTS.corner.class;
+ return this.pipeline.effects.some(effect => effect instanceof corner_class);
+ }
+
+ destroy_external_effect() {
+ if (!this.effect)
+ return;
+
+ this.effects_manager.remove(this.effect);
+ this.effect = null;
+ }
+
+ destroy() {
+ this.destroy_external_effect();
+ this.pipeline = null;
+ this.actor = null;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_fade.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_fade.js
new file mode 100644
index 0000000..2a0b9a5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_fade.js
@@ -0,0 +1,132 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import { has_style_class } from './targets.js';
+
+export const PopupBlurSurfaceFade = class PopupBlurSurfaceFade {
+ constructor(actor, target, root_actor, parent) {
+ this.actor = actor;
+ this.target = target;
+ this.root_actor = root_actor;
+ this.parent = parent;
+ }
+
+ get_opacity() {
+ let opacity = 255;
+ const visited = new WeakSet();
+ let actor = this.target;
+
+ while (actor && actor !== this.parent) {
+ opacity = this.apply_actor_opacity(opacity, actor, visited);
+ actor = actor.get_parent();
+ }
+
+ opacity = this.apply_actor_opacity(opacity, this.root_actor, visited);
+
+ [
+ this.target,
+ this.root_actor,
+ ].forEach(actor => {
+ const paint_opacity = this.get_paint_opacity(actor);
+ if (paint_opacity !== null)
+ opacity = Math.min(opacity, paint_opacity);
+ });
+
+ if (this.is_notification_banner()) {
+ opacity = Math.round(opacity * this.get_banner_position_alpha());
+ }
+
+ return opacity;
+ }
+
+ is_notification_banner() {
+ if (has_style_class(this.target, 'notification-banner'))
+ return true;
+ if (this.root_actor?.name === 'notification-container')
+ return true;
+ let a = this.target;
+ while (a && a !== this.parent) {
+ if (has_style_class(a, 'notification-banner'))
+ return true;
+ a = a.get_parent();
+ }
+ return false;
+ }
+
+ get_banner_position_alpha() {
+ try {
+ const [, stage_y] = this.target.get_transformed_position();
+
+ if (!Main.panel)
+ return 1;
+
+ const monitor = Main.layoutManager.findMonitorForActor(Main.panel)
+ ?? Main.layoutManager.primaryMonitor;
+
+ if (!monitor)
+ return 1;
+
+ const [, panel_y] = Main.panel.get_transformed_position();
+ const panel_width = Main.panel.width || Main.panel.get_width() || 0;
+ const panel_height = Main.panel.height || Main.panel.get_height() || 36;
+
+ // Must be a horizontal panel anchored at the monitor's top edge
+ const is_horizontal = panel_width > panel_height;
+ const is_at_monitor_top = Math.abs(panel_y - monitor.y) <= 5;
+
+ if (!is_horizontal || !is_at_monitor_top)
+ return 1;
+
+ const panel_bottom = panel_y + panel_height;
+ const threshold = panel_bottom + 5;
+
+ if (stage_y <= panel_y)
+ return 0;
+ if (stage_y >= threshold)
+ return 1;
+
+ return Math.clamp((stage_y - panel_y) / (threshold - panel_y), 0, 1);
+ } catch (e) {
+ return 1;
+ }
+ }
+
+ set_opacity(opacity) {
+ this.actor.opacity = opacity;
+ }
+
+ apply_actor_opacity(opacity, actor, visited) {
+ if (!actor || visited.has(actor))
+ return opacity;
+
+ visited.add(actor);
+
+ let visible = actor.visible;
+ let mapped = actor.mapped;
+ if (!visible && this.is_keyboard_actor(actor)) {
+ const parent = actor.get_parent();
+ visible = parent?.visible && parent?.mapped;
+ mapped = parent?.mapped;
+ }
+ if (!visible || !mapped)
+ return 0;
+
+ return Math.round(opacity * (actor.opacity ?? 255) / 255);
+ }
+
+ is_keyboard_actor(actor) {
+ return (
+ has_style_class(actor, 'bms-keyboard-surface')
+ );
+ }
+
+ get_paint_opacity(actor) {
+ if (this.is_keyboard_actor(actor) && (!actor.visible || !actor.mapped)) {
+ const parent = actor.get_parent();
+ if (parent?.visible && parent?.mapped)
+ return null;
+ }
+ if (actor?.get_paint_opacity)
+ return actor.get_paint_opacity();
+
+ return null;
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_geometry.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_geometry.js
new file mode 100644
index 0000000..84832d9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_geometry.js
@@ -0,0 +1,291 @@
+import St from 'gi://St';
+
+export function transform_to_actor_space(actor, { x, y, width, height }) {
+ if (!actor)
+ return null;
+ const [actor_x, actor_y] = actor.get_transformed_position();
+ const [actor_width, actor_height] = actor.get_size();
+ const [transformed_width, transformed_height] = actor.get_transformed_size();
+ const scale_x = actor_width > 0 ? transformed_width / actor_width : 1;
+ const scale_y = actor_height > 0 ? transformed_height / actor_height : 1;
+
+ return {
+ x: (x - actor_x) / scale_x,
+ y: (y - actor_y) / scale_y,
+ width: width / scale_x,
+ height: height / scale_y,
+ };
+}
+
+export const PopupBlurSurfaceGeometry = class PopupBlurSurfaceGeometry {
+ get(actor, { use_content = true, use_margins = true } = {}) {
+ const actor_geometry = this.get_transformed_actor(actor);
+ if (!this.has_valid_geometry(actor_geometry))
+ return actor_geometry;
+
+ const margin_geometry = use_margins
+ ? this.get_margin_adjusted(actor, actor_geometry)
+ : actor_geometry;
+ if (!use_content)
+ return margin_geometry;
+
+ const content_geometry = this.get_transformed_content(actor, margin_geometry);
+
+ if (!this.should_use_content(margin_geometry, content_geometry))
+ return margin_geometry;
+
+ return content_geometry;
+ }
+
+ get_transformed_actor(actor) {
+ const extents = actor.get_transformed_extents();
+ const top_left = extents?.get_top_left();
+ const bottom_right = extents?.get_bottom_right();
+
+ if (top_left && bottom_right) {
+ return {
+ x: top_left.x,
+ y: top_left.y,
+ width: bottom_right.x - top_left.x,
+ height: bottom_right.y - top_left.y,
+ };
+ }
+
+ if (!actor)
+ return null;
+ const [x, y] = actor.get_transformed_position();
+ const [width, height] = actor.get_transformed_size();
+ return { x, y, width, height };
+ }
+
+ get_transformed_clip(actor) {
+ if (!actor?.has_clip)
+ return null;
+
+ const [clip_x, clip_y, clip_width, clip_height] = actor.get_clip();
+ if (clip_width <= 0 || clip_height <= 0)
+ return null;
+
+ const [actor_x, actor_y] = actor.get_transformed_position();
+ const [actor_width, actor_height] = actor.get_transformed_size();
+ const scale_x = this.get_actor_scale(actor, actor_width, 'width');
+ const scale_y = this.get_actor_scale(actor, actor_height, 'height');
+
+ return {
+ x: actor_x + clip_x * scale_x,
+ y: actor_y + clip_y * scale_y,
+ width: clip_width * scale_x,
+ height: clip_height * scale_y,
+ };
+ }
+
+ get_margin_adjusted(actor, geometry) {
+ const margins = this.get_margins(actor);
+ if (!margins)
+ return geometry;
+
+ const scale_x = this.get_actor_scale(actor, geometry.width, 'width');
+ const scale_y = this.get_actor_scale(actor, geometry.height, 'height');
+
+ return this.shrink(geometry,
+ margins.top * scale_y,
+ margins.right * scale_x,
+ margins.bottom * scale_y,
+ margins.left * scale_x
+ ) ?? geometry;
+ }
+
+ get_transformed_content(actor, boundary) {
+ const children = this.get_transformed_children(actor);
+ if (!this.has_valid_geometry(children))
+ return null;
+
+ const insets = this.get_paint_insets(actor, boundary);
+ const geometry = this.inflate(
+ children,
+ insets.top,
+ insets.right,
+ insets.bottom,
+ insets.left
+ );
+
+ return this.intersect(geometry, boundary);
+ }
+
+ get_transformed_children(actor) {
+ let geometry = null;
+
+ this.get_children(actor).forEach(child => {
+ if (!this.is_visible(child))
+ return;
+
+ const child_geometry = this.get_transformed_actor(child);
+ if (!this.has_valid_geometry(child_geometry))
+ return;
+
+ geometry = this.union(geometry, child_geometry);
+ });
+
+ return geometry;
+ }
+
+ get_margins(actor) {
+ const margins = {
+ top: this.get_margin(actor, 'top', St.Side.TOP),
+ right: this.get_margin(actor, 'right', St.Side.RIGHT),
+ bottom: this.get_margin(actor, 'bottom', St.Side.BOTTOM),
+ left: this.get_margin(actor, 'left', St.Side.LEFT),
+ };
+
+ return Object.values(margins).some(margin => margin > 0) ? margins : null;
+ }
+
+ get_margin(actor, side_name, side) {
+ const property_names = [
+ `margin_${side_name}`,
+ `margin${side_name[0].toUpperCase()}${side_name.slice(1)}`,
+ ];
+
+ for (const property of property_names) {
+ const value = actor[property];
+ if (typeof value === 'number')
+ return value;
+ }
+
+ const getter = actor[`get_margin_${side_name}`];
+ if (getter) {
+ const value = getter.call(actor);
+ if (typeof value === 'number')
+ return value;
+ }
+
+ try {
+ const theme_node = actor.get_theme_node();
+ if (!theme_node?.get_margin)
+ return 0;
+
+ return theme_node.get_margin(side);
+ } catch (e) {
+ // get_theme_node() may throw when actor is not yet mapped to a stage
+ return 0;
+ }
+ }
+
+ get_paint_insets(actor, geometry) {
+ const scale_x = this.get_actor_scale(actor, geometry.width, 'width');
+ const scale_y = this.get_actor_scale(actor, geometry.height, 'height');
+
+ return {
+ top: this.get_inset(actor, St.Side.TOP) * scale_y,
+ right: this.get_inset(actor, St.Side.RIGHT) * scale_x,
+ bottom: this.get_inset(actor, St.Side.BOTTOM) * scale_y,
+ left: this.get_inset(actor, St.Side.LEFT) * scale_x,
+ };
+ }
+
+ get_inset(actor, side) {
+ try {
+ const theme_node = actor.get_theme_node();
+ if (!theme_node)
+ return 0;
+
+ let inset = 0;
+ if (theme_node.get_padding)
+ inset += theme_node.get_padding(side);
+ if (theme_node.get_border_width)
+ inset += theme_node.get_border_width(side);
+ return inset;
+ } catch (e) {
+ // get_theme_node() may throw when actor is not yet mapped to a stage
+ return 0;
+ }
+ }
+
+ get_actor_scale(actor, transformed_size, property) {
+ const size = actor ? (actor[property] ?? (actor[`get_${property}`] ? actor[`get_${property}`]() : 0)) : 0;
+ return size > 0 ? transformed_size / size : 1;
+ }
+
+ should_use_content(outer, content) {
+ if (!this.has_valid_geometry(content))
+ return false;
+
+ if (
+ content.width > outer.width + 1
+ || content.height > outer.height + 1
+ )
+ return false;
+
+ return outer.width - content.width > 2 || outer.height - content.height > 2;
+ }
+
+ is_visible(actor) {
+ return actor?.visible && actor.mapped;
+ }
+
+ get_children(actor) {
+ return actor ? actor.get_children() : [];
+ }
+
+ has_valid_geometry(geometry) {
+ return geometry?.width > 0 && geometry?.height > 0;
+ }
+
+ shrink(geometry, top, right, bottom, left) {
+ const width = geometry.width - left - right;
+ const height = geometry.height - top - bottom;
+
+ if (width <= 0 || height <= 0)
+ return null;
+
+ return {
+ x: geometry.x + left,
+ y: geometry.y + top,
+ width,
+ height,
+ };
+ }
+
+ inflate(geometry, top, right, bottom, left) {
+ return {
+ x: geometry.x - left,
+ y: geometry.y - top,
+ width: geometry.width + left + right,
+ height: geometry.height + top + bottom,
+ };
+ }
+
+ intersect(a, b) {
+ const x1 = Math.max(a.x, b.x);
+ const y1 = Math.max(a.y, b.y);
+ const x2 = Math.min(a.x + a.width, b.x + b.width);
+ const y2 = Math.min(a.y + a.height, b.y + b.height);
+
+ if (x2 <= x1 || y2 <= y1)
+ return null;
+
+ return {
+ x: x1,
+ y: y1,
+ width: x2 - x1,
+ height: y2 - y1,
+ };
+ }
+
+ union(a, b) {
+ if (!a)
+ return b;
+
+ const x1 = Math.min(a.x, b.x);
+ const y1 = Math.min(a.y, b.y);
+ const x2 = Math.max(a.x + a.width, b.x + b.width);
+ const y2 = Math.max(a.y + a.height, b.y + b.height);
+
+ return {
+ x: x1,
+ y: y1,
+ width: x2 - x1,
+ height: y2 - y1,
+ };
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_placement.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_placement.js
new file mode 100644
index 0000000..cad36f9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_placement.js
@@ -0,0 +1,328 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { PopupBlurSurfaceGeometry, transform_to_actor_space } from './surface_geometry.js';
+
+export const PopupBlurSurfacePlacement = class PopupBlurSurfacePlacement {
+ constructor(surface) {
+ this.surface = surface;
+ this.geometry = new PopupBlurSurfaceGeometry();
+ this.clear();
+ this.ready = false;
+ }
+
+ get_surface_geometry() {
+ this.offscreen = false;
+
+ const geometry = this.get_unclipped_surface_geometry();
+ if (!this.has_valid_geometry(geometry))
+ return null;
+
+ const clipped_geometry = this.get_clipped_surface_geometry(geometry);
+ if (!this.has_valid_geometry(clipped_geometry))
+ return null;
+
+ const monitor_geometry = this.get_monitor_clipped_surface_geometry(clipped_geometry);
+ if (!this.has_valid_geometry(monitor_geometry)) {
+ this.offscreen = true;
+ return null;
+ }
+
+ return monitor_geometry;
+ }
+
+ get_unclipped_monitor_surface_geometry() {
+ this.offscreen = false;
+
+ const geometry = this.get_unclipped_surface_geometry();
+ if (!this.has_valid_geometry(geometry))
+ return null;
+
+ const monitor_geometry = this.get_monitor_clipped_surface_geometry(geometry);
+ if (!this.has_valid_geometry(monitor_geometry)) {
+ this.offscreen = true;
+ return null;
+ }
+
+ return monitor_geometry;
+ }
+
+ get_unclipped_surface_geometry() {
+ const geometry_actor = this.surface.get_geometry_actor();
+ const geometry = this.geometry.get(geometry_actor, {
+ use_content: this.surface.should_use_content_geometry(),
+ use_margins: this.surface.should_use_margin_geometry(),
+ });
+ if (!this.has_valid_geometry(geometry))
+ return null;
+
+ return this.create_surface_geometry(
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height
+ );
+ }
+
+ create_surface_geometry(target_x, target_y, width, height) {
+ const local_geometry = transform_to_actor_space(this.surface.parent, {
+ x: target_x,
+ y: target_y,
+ width,
+ height,
+ });
+ if (!local_geometry)
+ return null;
+
+ return {
+ target_x,
+ target_y,
+ target_width: width,
+ target_height: height,
+ x: Math.round(local_geometry.x),
+ y: Math.round(local_geometry.y),
+ width: Math.ceil(local_geometry.width),
+ height: Math.ceil(local_geometry.height),
+ };
+ }
+
+ get_clipped_surface_geometry(geometry) {
+ let clipped = {
+ x: geometry.target_x,
+ y: geometry.target_y,
+ width: geometry.target_width,
+ height: geometry.target_height,
+ };
+
+ let actor = this.surface.get_geometry_actor();
+ while (actor && actor !== this.surface.parent) {
+ const clip = this.geometry.get_transformed_clip(actor);
+ if (clip) {
+ clipped = this.geometry.intersect(clipped, clip);
+ if (!this.has_valid_geometry(clipped))
+ return null;
+ }
+
+ actor = actor.get_parent();
+ }
+
+ return this.create_surface_geometry(
+ clipped.x,
+ clipped.y,
+ clipped.width,
+ clipped.height
+ );
+ }
+
+ get_monitor_clipped_surface_geometry(geometry) {
+ const rect = {
+ x: geometry.target_x,
+ y: geometry.target_y,
+ width: geometry.target_width,
+ height: geometry.target_height,
+ };
+ const cached_monitor = this.get_cached_monitor();
+ const match = cached_monitor
+ ? this.get_monitor_intersection(rect, cached_monitor.monitor, cached_monitor.index)
+ : this.find_best_monitor_intersection(rect);
+
+ if (!match)
+ return null;
+
+ this.monitor_index = match.monitor_index;
+
+ const surface_geometry = this.create_surface_geometry(
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height
+ );
+ surface_geometry.monitor_index = match.monitor_index;
+ return surface_geometry;
+ }
+
+ get_cached_monitor() {
+ if (this.monitor_index === null)
+ return null;
+
+ const monitor = Main.layoutManager.monitors?.[this.monitor_index];
+ if (!monitor) {
+ this.monitor_index = null;
+ return null;
+ }
+
+ return { monitor, index: this.monitor_index };
+ }
+
+ find_best_monitor_intersection(rect) {
+ let best_match = null;
+ let best_area = 0;
+
+ (Main.layoutManager.monitors ?? []).forEach((monitor, index) => {
+ const match = this.get_monitor_intersection(rect, monitor, index);
+ if (!match)
+ return;
+
+ const area = match.intersection.width * match.intersection.height;
+ if (area > best_area) {
+ best_match = match;
+ best_area = area;
+ }
+ });
+
+ return best_match;
+ }
+
+ get_monitor_intersection(rect, monitor, monitor_index) {
+ const intersection = this.geometry.intersect(rect, monitor);
+ if (!this.has_valid_geometry(intersection))
+ return null;
+
+ return {
+ monitor_index,
+ intersection,
+ };
+ }
+
+ has_valid_geometry(geometry) {
+ return geometry?.width > 0 && geometry?.height > 0;
+ }
+
+ keep_transition_visible(transition_state) {
+ if (this.offscreen || !this.ready || !this.has_cached_geometry() || !transition_state.running)
+ return false;
+
+ const opacity = this.surface.update_opacity(transition_state);
+ if (opacity <= 0)
+ return false;
+
+ if (this.surface.static_blur) {
+ this.surface.blur_actor?.show();
+ }
+ this.surface.show_actors();
+ return true;
+ }
+
+ has_cached_geometry() {
+ return (
+ this.x !== null
+ && this.y !== null
+ && this.width > 0
+ && this.height > 0
+ );
+ }
+
+ has_cached_surface_geometry() {
+ return (
+ this.surface_x !== null
+ && this.surface_y !== null
+ && this.surface_width > 0
+ && this.surface_height > 0
+ );
+ }
+
+ has_surface_geometry_changed(geometry) {
+ return (
+ this.has_valid_geometry(geometry)
+ && (
+ !this.has_cached_surface_geometry()
+ || this.surface_x !== geometry.x
+ || this.surface_y !== geometry.y
+ || this.surface_width !== geometry.width
+ || this.surface_height !== geometry.height
+ )
+ );
+ }
+
+ store_surface_geometry(geometry) {
+ this.surface_x = geometry.x;
+ this.surface_y = geometry.y;
+ this.surface_width = geometry.width;
+ this.surface_height = geometry.height;
+ }
+
+ prepare_visible_geometry() {
+ if (this.ready)
+ return true;
+
+ this.ready = true;
+ this.surface.opacity = 0;
+ this.surface.update_surface_opacity(0);
+ this.surface.hide_actors();
+ this.surface.queue_update();
+ return false;
+ }
+
+ update_surface_geometry(geometry) {
+ if (this.surface.static_blur) {
+ if (!this.update_static_geometry(
+ geometry.target_x,
+ geometry.target_y,
+ geometry.target_width,
+ geometry.target_height
+ ))
+ return false;
+ } else {
+ if (!this.update_dynamic_geometry(geometry.x, geometry.y, geometry.width, geometry.height))
+ return false;
+ }
+
+ this.store_surface_geometry(geometry);
+ return true;
+ }
+
+ update_dynamic_geometry(x, y, width, height) {
+ if (this.x !== x || this.y !== y) {
+ this.surface.blur_actor.set_position(x, y);
+ this.x = x;
+ this.y = y;
+ }
+
+ if (this.width !== width || this.height !== height) {
+ this.surface.blur_actor.set_size(width, height);
+ this.width = width;
+ this.height = height;
+ }
+
+ return true;
+ }
+
+ update_static_geometry(target_x, target_y, width, height) {
+ const geometry = this.surface.static_actor.update_geometry(
+ target_x,
+ target_y,
+ width,
+ height,
+ this.monitor_index
+ );
+ this.surface.sync_static_actor();
+
+ if (!geometry) {
+ this.surface.hide_actors();
+ return false;
+ }
+
+ this.x = geometry.x;
+ this.y = geometry.y;
+ this.width = geometry.width;
+ this.height = geometry.height;
+ return true;
+ }
+
+ hide() {
+ this.ready = false;
+ this.clear();
+ }
+
+ clear() {
+ this.x = null;
+ this.y = null;
+ this.width = null;
+ this.height = null;
+ this.surface_x = null;
+ this.surface_y = null;
+ this.surface_width = null;
+ this.surface_height = null;
+ this.monitor_index = null;
+ this.offscreen = false;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_signals.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_signals.js
new file mode 100644
index 0000000..47adb91
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_signals.js
@@ -0,0 +1,117 @@
+import Meta from 'gi://Meta';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+const SURFACE_SIGNALS = [
+ 'notify::allocation',
+ 'notify::position',
+ 'notify::size',
+ 'notify::x',
+ 'notify::y',
+ 'notify::width',
+ 'notify::height',
+ 'notify::clip-rect',
+ 'notify::visible',
+ 'notify::mapped',
+ 'notify::opacity',
+ 'notify::translation-x',
+ 'notify::translation-y',
+ 'notify::scale-x',
+ 'notify::scale-y',
+ 'notify::pseudo-class',
+];
+
+export const PopupBlurSurfaceSignals = class PopupBlurSurfaceSignals {
+ constructor(surface) {
+ this.surface = surface;
+ this.signal_ids = [];
+ this.signal_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ }
+
+ connect_actor(actor) {
+ if (!actor || this.signal_actors.has(actor))
+ return;
+
+ this.signal_actors.add(actor);
+ this.track_destroy(actor);
+
+ const is_heavy_surface = this.surface.is_heavy_surface();
+
+ SURFACE_SIGNALS.forEach(signal => {
+ let id = actor.connect(signal, () => {
+ this.clear_pending_idles();
+ const is_visibility_change = signal === 'notify::visible' || signal === 'notify::mapped';
+ if (is_heavy_surface || is_visibility_change) {
+ this.surface.queue_update();
+ return;
+ }
+ else {
+ this.updateId = global.compositor.get_laters().add(Meta.LaterType.IDLE, () => {
+ this.updateId = 0;
+ this.surface.queue_update();
+ return false;
+ });
+ }
+ });
+ this.signal_ids.push([actor, id, signal]);
+ });
+ }
+
+ track_destroy(actor) {
+ const id = actor.connect('destroy', () => this.destroyed_actors.add(actor));
+ this.signal_ids.push([actor, id]);
+ }
+
+ connect_ancestors(actor) {
+ actor = actor ? actor.get_parent() : null;
+
+ while (actor && actor !== this.surface.parent) {
+ this.connect_actor(actor);
+ actor = actor.get_parent();
+ }
+ }
+
+ connect_layout() {
+ if (!this.surface.static_blur)
+ return;
+
+ this.signal_ids.push([
+ Main.layoutManager,
+ Main.layoutManager.connect('monitors-changed', () => this.surface.queue_update()),
+ ]);
+ }
+
+ connect_settings() {
+ [
+ this.surface.corner_radius.key,
+ 'override-background',
+ 'style-popup',
+ ].forEach(key => {
+ const id = this.surface.settings.popup.settings.connect(
+ `changed::${key}`,
+ () => this.surface.update_settings()
+ );
+ this.signal_ids.push([this.surface.settings.popup.settings, id]);
+ });
+ }
+
+ clear_pending_idles() {
+ if (this.updateId) {
+ global.compositor.get_laters().remove(this.updateId);
+ this.updateId = 0;
+ }
+ }
+
+ disconnect_all() {
+ this.clear_pending_idles();
+ this.signal_ids.forEach(([signal_actor, signal_id]) => {
+ if (this.destroyed_actors.has(signal_actor))
+ return;
+
+ signal_actor.disconnect(signal_id);
+ });
+ this.signal_ids = [];
+ this.signal_actors = new WeakSet();
+ this.destroyed_actors = new WeakSet();
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_style.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_style.js
new file mode 100644
index 0000000..1dfb947
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_style.js
@@ -0,0 +1,59 @@
+export const PopupBlurSurfaceStyle = class PopupBlurSurfaceStyle {
+ constructor(surface) {
+ this.surface = surface;
+ this.original_target_style = null;
+ this.target_style_set = false;
+ }
+
+ capture_target_style() {
+ this.original_target_style = this.surface.target ? this.surface.target.get_style() : null;
+ }
+
+ update_target_style() {
+ if (
+ !this.surface.target.set_style
+ || !this.surface.settings.popup.OVERRIDE_BACKGROUND
+ ) {
+ this.restore_target_style();
+ return;
+ }
+
+ if (
+ this.has_style_class(this.surface.target, 'bms-keyboard-surface')
+ )
+ return;
+
+ const base_style = this.original_target_style ?? '';
+ const separator = base_style.trim() && !base_style.trim().endsWith(';') ? '; ' : '';
+
+ this.surface.target.set_style(
+ `${base_style}${separator}border-radius: ${this.surface.get_corner_radius()}px;`
+ );
+ this.target_style_set = true;
+ }
+
+ restore_target_style() {
+ if (!this.target_style_set || !this.surface.target.set_style)
+ return;
+
+ this.surface.target.set_style(this.original_target_style);
+ this.target_style_set = false;
+ }
+
+ has_any_style_class(actor, style_classes) {
+ return style_classes.some(style_class => this.has_style_class(actor, style_class));
+ }
+
+ has_style_class(actor, style_class) {
+ if (!actor)
+ return false;
+
+ if (actor.has_style_class_name)
+ return actor.has_style_class_name(style_class);
+
+ if (actor.get_style_class_name)
+ return (actor.get_style_class_name() ?? '').split(/\s+/).includes(style_class);
+
+ return false;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_transitions.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_transitions.js
new file mode 100644
index 0000000..70fd520
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/surface_transitions.js
@@ -0,0 +1,139 @@
+const GEOMETRY_TRANSITION_PROPERTIES = [
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'clip-rect',
+ 'clip_rect',
+ 'translation-x',
+ 'translation-y',
+ 'translation_x',
+ 'translation_y',
+ 'scale-x',
+ 'scale-y',
+ 'scale_x',
+ 'scale_y',
+];
+
+export const PopupBlurSurfaceTransitions = class PopupBlurSurfaceTransitions {
+ constructor(surface) {
+ this.surface = surface;
+ }
+
+ has_running() {
+ return this.get_state().running;
+ }
+
+ get_state(include_descendants = false) {
+ const surface_actors = this.get_surface_actors();
+ const actors = include_descendants
+ ? this.get_actors(surface_actors)
+ : surface_actors;
+ const geometry_properties = this.get_running_properties(
+ GEOMETRY_TRANSITION_PROPERTIES,
+ actors
+ );
+ const opacity = this.has_running_for(['opacity'], surface_actors);
+
+ return {
+ complete: include_descendants,
+ running: opacity || geometry_properties.size > 0,
+ geometry: geometry_properties.size > 0,
+ geometry_properties,
+ opacity,
+ };
+ }
+
+ complete_state(transition_state) {
+ if (transition_state.complete)
+ return transition_state;
+
+ return this.get_state(true);
+ }
+
+ has_running_for(properties, actors) {
+ return actors.some(actor =>
+ properties.some(property => this.has_transition(actor, property))
+ );
+ }
+
+ get_running_properties(properties, actors) {
+ const running_properties = new Set();
+
+ actors.forEach(actor => {
+ properties.forEach(property => {
+ if (this.has_transition(actor, property))
+ running_properties.add(this.normalize_property(property));
+ });
+ });
+
+ return running_properties;
+ }
+
+ normalize_property(property) {
+ return property.replace(/_/g, '-');
+ }
+
+ get_actors(surface_actors = this.get_surface_actors()) {
+ const actors = [...surface_actors];
+ const seen = new WeakSet();
+ actors.forEach(actor => seen.add(actor));
+
+ if (!this.surface.is_quick_settings()) {
+ this.add_descendants(actors, seen, this.surface.target);
+ this.add_descendants(actors, seen, this.surface.root_actor);
+ }
+
+ return actors;
+ }
+
+ get_surface_actors() {
+ const actors = [];
+ const seen = new WeakSet();
+
+ this.add_actor(actors, seen, this.surface.target);
+ this.add_actor(actors, seen, this.surface.root_actor);
+ this.add_actor(actors, seen, this.surface.get_geometry_actor());
+ this.add_ancestors(actors, seen, this.surface.target);
+ this.add_ancestors(actors, seen, this.surface.root_actor);
+
+ return actors;
+ }
+
+ add_actor(actors, seen, actor) {
+ if (!actor || seen.has(actor))
+ return;
+
+ seen.add(actor);
+ actors.push(actor);
+ }
+
+ add_ancestors(actors, seen, actor) {
+ actor = actor?.get_parent();
+ while (actor && actor !== this.surface.parent) {
+ this.add_actor(actors, seen, actor);
+ actor = actor.get_parent();
+ }
+ }
+
+ add_descendants(actors, seen, actor) {
+ const stack = this.get_children(actor).slice().reverse();
+
+ while (stack.length > 0) {
+ const child = stack.pop();
+ if (seen.has(child))
+ continue;
+
+ this.add_actor(actors, seen, child);
+ stack.push(...this.get_children(child).slice().reverse());
+ }
+ }
+
+ has_transition(actor, property) {
+ return !!actor?.get_transition(property);
+ }
+
+ get_children(actor) {
+ return actor ? actor.get_children() : [];
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/targets.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/targets.js
new file mode 100644
index 0000000..dd537b0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup/targets.js
@@ -0,0 +1,255 @@
+const POPUP_STYLE_CLASSES = ['popup-menu', 'quick-toggle-menu-container', 'candidate-popup-boxpointer'];
+const POPUP_TARGET_STYLE_CLASSES = ['popup-menu-content', 'quick-settings', 'quick-toggle-menu', 'notification-banner', 'candidate-popup-content','screenshot-ui-panel'];
+const POPUP_CHILD_STYLE_CLASSES = ['osd-window', 'resize-popup', 'switcher-list', 'workspace-switcher', 'modal-dialog', 'run-dialog'];
+const POPUP_TARGET_STYLE_CLASSES_OSK = ['bms-keyboard-surface']
+const POPUP_DESCENDANT_TARGET_STYLE_CLASSES = ['switcher-list'];
+
+export const POPUP_BACKGROUND_STYLES = ['bms-popup-background-transparent', 'bms-popup-background-light', 'bms-popup-background-dark'];
+export const POPUP_SURFACE_STYLES = POPUP_BACKGROUND_STYLES.map(
+ style => style.replace('bms-popup-background-', 'bms-popup-surface-')
+);
+export const DEFAULT_CORNER_RADIUS = { key: 'corner-radius', property: 'CORNER_RADIUS' };
+export const POPUP_CORNER_RADII = [
+ {
+ key: 'quick-settings-corner-radius',
+ property: 'QUICK_SETTINGS_CORNER_RADIUS',
+ style_classes: ['quick-settings', 'quick-toggle-menu', 'datemenu-popover','screenshot-ui-panel'],
+ },
+ {
+ key: 'notification-corner-radius',
+ property: 'NOTIFICATION_CORNER_RADIUS',
+ style_classes: ['notification-banner', 'message', 'message-view', 'message-list'],
+ },
+ {
+ key: 'menu-corner-radius',
+ property: 'MENU_CORNER_RADIUS',
+ style_classes: ['popup-menu-content', 'popup-menu', 'candidate-popup-content', 'candidate-popup-boxpointer'],
+ },
+ {
+ key: 'osd-corner-radius',
+ property: 'OSD_CORNER_RADIUS',
+ style_classes: ['osd-window', 'resize-popup', 'switcher-list', 'workspace-switcher'],
+ },
+ {
+ key: 'dialog-corner-radius',
+ property: 'DIALOG_CORNER_RADIUS',
+ style_classes: ['modal-dialog', 'run-dialog'],
+ },
+ {
+ key: 'osk-corner-radius',
+ property: 'OSK_CORNER_RADIUS',
+ style_classes: ['bms-keyboard-surface'],
+ },
+];
+
+export const PopupBlurTargets = class PopupBlurTargets {
+ constructor(actor) {
+ this.actor = actor;
+ this.style_cache = null;
+ }
+
+ find(actor) {
+ return this.with_style_cache(() => this.find_actor(actor));
+ }
+
+ find_actor(actor) {
+ if (this.actor.is_internal_actor(actor) || !this.actor.watch_actor(actor))
+ return [];
+
+ const targets = this.create_targets();
+ const delegate = this.actor.get_actor_delegate(actor);
+
+ if (this.prefers_descendant_targets(actor)) {
+ this.find_target_children(actor, targets);
+ if (targets.actors.length > 0)
+ return targets.actors;
+ }
+
+ if (this.actor.watch_actor(delegate?.box) && (this.has_any_style_class(delegate.box, POPUP_TARGET_STYLE_CLASSES) || this.has_any_style_class(delegate.box, POPUP_TARGET_STYLE_CLASSES_OSK)))
+ this.add(targets, delegate.box);
+
+ if (this.has_any_style_class(actor, POPUP_TARGET_STYLE_CLASSES))
+ this.add(targets, actor);
+
+ if (this.has_any_style_class(actor, POPUP_TARGET_STYLE_CLASSES_OSK))
+ this.add(targets, actor);
+
+ if (this.has_any_style_class(actor, POPUP_CHILD_STYLE_CLASSES))
+ this.add(targets, actor);
+
+ const dialog_layout = this.actor.get_actor_dialog_layout(actor);
+ if (this.actor.watch_actor(dialog_layout) && this.has_any_style_class(dialog_layout, POPUP_CHILD_STYLE_CLASSES))
+ this.add(targets, dialog_layout);
+
+ if (targets.actors.length === 0)
+ this.find_target_children(actor, targets);
+
+ if (
+ targets.actors.length === 0
+ && this.has_any_style_class(actor, POPUP_STYLE_CLASSES)
+ )
+ this.add(targets, actor);
+
+ return targets.actors;
+ }
+
+ find_target_children(actor, targets, seen = new WeakSet()) {
+ const stack = this.get_children(actor)
+ .slice()
+ .reverse()
+ .map(child => ({ actor: child, fallback_count: null }));
+
+ while (stack.length > 0) {
+ const { actor: child, fallback_count } = stack.pop();
+ if (
+ !child
+ || this.actor.is_internal_actor(child)
+ )
+ continue;
+
+ if (fallback_count !== null) {
+ if (
+ targets.actors.length === fallback_count
+ && this.is_blur_target_actor(child)
+ )
+ this.add(targets, child);
+ continue;
+ }
+
+ if (seen.has(child))
+ continue;
+
+ seen.add(child);
+
+ if (this.prefers_descendant_targets(child)) {
+ const children = this.get_children(child);
+ if (children.length > 0) {
+ stack.push({
+ actor: child,
+ fallback_count: targets.actors.length,
+ });
+ this.push_children(stack, children);
+ continue;
+ }
+ }
+
+ if (this.is_blur_target_actor(child)) {
+ this.add(targets, child);
+ continue;
+ }
+
+ this.push_children(stack, this.get_children(child));
+ }
+ }
+
+ prefers_descendant_targets(actor) {
+ return this.has_any_style_class(actor, POPUP_DESCENDANT_TARGET_STYLE_CLASSES);
+ }
+
+ is_blur_target_actor(actor) {
+ return (
+ this.has_any_style_class(actor, POPUP_TARGET_STYLE_CLASSES)
+ || this.has_any_style_class(actor, POPUP_TARGET_STYLE_CLASSES_OSK)
+ || this.has_any_style_class(actor, POPUP_CHILD_STYLE_CLASSES)
+ );
+ }
+
+ get_corner_radius(target, root_actor) {
+ return POPUP_CORNER_RADII.find(radius =>
+ this.has_any_style_class(target, radius.style_classes)
+ || this.has_any_style_class(root_actor, radius.style_classes)
+ ) ?? DEFAULT_CORNER_RADIUS;
+ }
+
+ with_style_cache(callback) {
+ const previous_cache = this.style_cache;
+ this.style_cache = new WeakMap();
+
+ try {
+ return callback();
+ } finally {
+ this.style_cache = previous_cache;
+ }
+ }
+
+ has_any_style_class(actor, style_classes) {
+ if (!actor)
+ return false;
+
+ const classes = this.get_style_classes(actor);
+ if (classes)
+ return style_classes.some(style_class => classes.has(style_class));
+
+ return style_classes.some(style_class => this.has_style_class(actor, style_class));
+ }
+
+ has_style_class(actor, style_class) {
+ return has_style_class(actor, style_class);
+ }
+
+ get_style_classes(actor) {
+ if (!actor || !this.style_cache)
+ return null;
+
+ if (this.style_cache.has(actor))
+ return this.style_cache.get(actor);
+
+ if (!actor.get_style_class_name) {
+ this.style_cache.set(actor, null);
+ return null;
+ }
+
+ const class_names = actor.get_style_class_name();
+ if (typeof class_names !== 'string') {
+ this.style_cache.set(actor, null);
+ return null;
+ }
+
+ const classes = new Set(class_names.split(/\s+/).filter(Boolean));
+ this.style_cache.set(actor, classes);
+ return classes;
+ }
+
+ create_targets() {
+ return {
+ actors: [],
+ seen: new WeakSet(),
+ };
+ }
+
+ get_children(actor) {
+ return actor ? actor.get_children() : [];
+ }
+
+ push_children(stack, children) {
+ children
+ .slice()
+ .reverse()
+ .forEach(child => stack.push({ actor: child, fallback_count: null }));
+ }
+
+ add(targets, target) {
+ if (
+ this.actor.is_internal_actor(target)
+ || !this.actor.watch_actor(target)
+ || targets.seen.has(target)
+ )
+ return;
+
+ targets.seen.add(target);
+ targets.actors.push(target);
+ }
+};
+
+export function has_style_class(actor, style_class) {
+ if (!actor || !style_class)
+ return false;
+
+ if (actor.has_style_class_name)
+ return actor.has_style_class_name(style_class);
+
+ if (actor.get_style_class_name)
+ return (actor.get_style_class_name() ?? '').split(/\s+/).includes(style_class);
+
+ return false;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js
index 6ab0644..33a9be3 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js
@@ -66,7 +66,7 @@ export const WindowListBlur = class WindowListBlur {
this.connections.connect(
actor,
'destroy',
- _ => this.destroy_blur(pipeline, true)
+ _ => this.destroy_blur(pipeline)
);
@@ -98,8 +98,8 @@ export const WindowListBlur = class WindowListBlur {
}
// IMPORTANT: do never call this in a mutable `this.pipelines.forEach`
- destroy_blur(pipeline, actor_destroyed = false) {
- if (!actor_destroyed) {
+ destroy_blur(pipeline) {
+ if (pipeline.actor) {
this.remove_style(pipeline.actor);
this.paint_signals.disconnect_all_for_actor(pipeline.actor);
}
@@ -108,7 +108,7 @@ export const WindowListBlur = class WindowListBlur {
let index = this.pipelines.indexOf(pipeline);
if (index >= 0)
- this.pipelines.splice(pipeline, 1);
+ this.pipelines.splice(index, 1);
}
remove_style(actor) {