import St from 'gi://St'; import Shell from 'gi://Shell'; import * as Main from 'resource:///org/gnome/shell/ui/main.js'; const Signals = imports.signals; import { PaintSignals } from '../effects/paint_signals.js'; const DASH_STYLES = [ "transparent-dash", "light-dash", "dark-dash" ]; /// This type of object is created for every dash found, and talks to the main /// DashBlur thanks to signals. /// /// This allows to dynamically track the created dashes for each screen. class DashInfos { constructor(dash_blur, dash, background_parent, effect, settings) { // the parent DashBlur object, to communicate this.dash_blur = dash_blur; // the blurred dash this.dash = dash; this.background_parent = background_parent; this.effect = effect; this.settings = settings; this.old_style = this.dash._background.style; dash_blur.connections.connect(dash_blur, 'remove-dashes', () => { this._log("removing blur from dash"); this.dash.get_parent().remove_child(this.background_parent); this.dash._background.style = this.old_style; DASH_STYLES.forEach( style => this.dash.remove_style_class_name(style) ); }); dash_blur.connections.connect(dash_blur, 'update-sigma', () => { this.effect.sigma = this.dash_blur.sigma; }); dash_blur.connections.connect(dash_blur, 'update-brightness', () => { this.effect.brightness = this.dash_blur.brightness; }); dash_blur.connections.connect(dash_blur, 'override-background', () => { this.dash._background.style = null; DASH_STYLES.forEach( style => this.dash.remove_style_class_name(style) ); this.dash.set_style_class_name( DASH_STYLES[this.settings.dash_to_dock.STYLE_DASH_TO_DOCK] ); }); dash_blur.connections.connect(dash_blur, 'reset-background', () => { this.dash._background.style = this.old_style; DASH_STYLES.forEach( style => this.dash.remove_style_class_name(style) ); }); dash_blur.connections.connect(dash_blur, 'show', () => { this.effect.sigma = this.dash_blur.sigma; }); dash_blur.connections.connect(dash_blur, 'hide', () => { this.effect.sigma = 0; }); } _log(str) { if (this.settings.DEBUG) console.log(`[Blur my Shell > dash] ${str}`); } } export const DashBlur = class DashBlur { constructor(connections, settings, _) { this.dashes = []; this.connections = connections; this.settings = settings; this.paint_signals = new PaintSignals(connections); this.sigma = this.settings.dash_to_dock.CUSTOMIZE ? this.settings.dash_to_dock.SIGMA : this.settings.SIGMA; this.brightness = this.settings.dash_to_dock.CUSTOMIZE ? this.settings.dash_to_dock.BRIGHTNESS : this.settings.BRIGHTNESS; this.enabled = false; } enable() { this.connections.connect(Main.uiGroup, 'actor-added', (_, actor) => { if ( (actor.get_name() === "dashtodockContainer") && (actor.constructor.name === 'DashToDock') ) this.try_blur(actor); }); this.blur_existing_dashes(); this.connect_to_overview(); this.enabled = true; } // Finds all existing dashes on every monitor, and call `try_blur` on them // We cannot only blur `Main.overview.dash`, as there could be several blur_existing_dashes() { this._log("searching for dash"); // blur every dash found, filtered by name Main.uiGroup.get_children().filter((child) => { return (child.get_name() === "dashtodockContainer") && (child.constructor.name === 'DashToDock'); }).forEach(this.try_blur.bind(this)); } // Tries to blur the dash contained in the given actor try_blur(dash_container) { let dash_box = dash_container._slider.get_child(); // verify that we did not already blur that dash if (!dash_box.get_children().some((child) => { return child.get_name() === "dash-blurred-background-parent"; })) { 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'; }); this.dashes.push(this.blur_dash_from(dash, dash_container)); } } // Blurs the dash and returns a `DashInfos` containing its information blur_dash_from(dash, dash_container) { // the effect to be applied let effect = new Shell.BlurEffect({ brightness: this.brightness, sigma: this.sigma, mode: Shell.BlurMode.BACKGROUND }); // dash background parent, not visible let background_parent = new St.Widget({ name: 'dash-blurred-background-parent', style_class: 'dash-blurred-background-parent', width: 0, height: 0 }); // dash background widget let background = new St.Widget({ name: 'dash-blurred-background', style_class: 'dash-blurred-background', x: 0, y: dash_container._slider.y, width: dash.width, height: dash.height, }); // updates size and position on change this.connections.connect(dash_container._slider, 'notify::y', _ => { background.y = dash_container._slider.y; }); this.connections.connect(dash, 'notify::width', _ => { background.width = dash.width; }); this.connections.connect(dash, 'notify::height', _ => { background.height = dash.height; }); // add the widget to the dash background.add_effect(effect); background_parent.add_child(background); dash.get_parent().insert_child_at_index(background_parent, 0); // 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("dash hack level 1"); this.paint_signals.disconnect_all(); let rp = () => { effect.queue_repaint(); }; dash._box.get_children().forEach((icon) => { try { let zone = icon.get_child_at_index(0); this.connections.connect(zone, [ 'enter-event', 'leave-event', 'button-press-event' ], rp); } catch (e) { this._warn(`${e}, continuing`); } }); this.connections.connect(dash._box, 'actor-added', (_, actor) => { try { let zone = actor.get_child_at_index(0); this.connections.connect(zone, [ 'enter-event', 'leave-event', 'button-press-event' ], rp); } catch (e) { this._warn(`${e}, continuing`); } }); let show_apps = dash._showAppsIcon; this.connections.connect(show_apps, [ 'enter-event', 'leave-event', 'button-press-event' ], rp); this.connections.connect(dash, 'leave-event', rp); } else if (this.settings.HACKS_LEVEL === 2) { this._log("dash hack level 2"); this.paint_signals.connect(background, effect); } else { this.paint_signals.disconnect_all(); } // create infos let infos = new DashInfos( this, dash, background_parent, effect, this.settings ); // update the background this.update_background(); // returns infos return infos; } /// Connect when overview if opened/closed to hide/show the blur accordingly connect_to_overview() { this.connections.disconnect_all_for(Main.overview); if (this.settings.dash_to_dock.UNBLUR_IN_OVERVIEW) { this.connections.connect( Main.overview, 'showing', this.hide.bind(this) ); this.connections.connect( Main.overview, 'hidden', this.show.bind(this) ); } }; /// Updates the background to either remove it or not, according to the /// user preferences. update_background() { if (this.settings.dash_to_dock.OVERRIDE_BACKGROUND) this.emit('override-background', true); else this.emit('reset-background', true); } set_sigma(sigma) { this.sigma = sigma; this.emit('update-sigma', true); } set_brightness(brightness) { this.brightness = brightness; this.emit('update-brightness', true); } // not implemented for dynamic blur set_color(c) { } set_noise_amount(n) { } set_noise_lightness(l) { } disable() { this._log("removing blur from dashes"); this.emit('remove-dashes', true); this.dashes = []; this.connections.disconnect_all(); this.enabled = false; } show() { this.emit('show', true); } hide() { this.emit('hide', true); } _log(str) { if (this.settings.DEBUG) console.log(`[Blur my Shell > dash manager] ${str}`); } _warn(str) { console.warn(`[Blur my Shell > dash manager] ${str}`); } }; Signals.addSignalMethods(DashBlur.prototype);