summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/popup
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/popup
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/popup')
-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
12 files changed, 3303 insertions, 0 deletions
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;
+}