Linux puskom-ProLiant-DL385-Gen10 5.4.0-150-generic #167~18.04.1-Ubuntu SMP Wed May 24 00:51:42 UTC 2023 x86_64
/
usr
/
share
/
gnome-shell
/
extensions
/
ubuntu-dock@ubuntu.com
/
//usr/share/gnome-shell/extensions/ubuntu-dock@ubuntu.com/theming.js
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Gtk = imports.gi.Gtk; const Signals = imports.signals; const Lang = imports.lang; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const St = imports.gi.St; const Mainloop = imports.mainloop; const AppDisplay = imports.ui.appDisplay; const AppFavorites = imports.ui.appFavorites; const Dash = imports.ui.dash; const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; const PopupMenu = imports.ui.popupMenu; const Tweener = imports.ui.tweener; const Util = imports.misc.util; const Workspace = imports.ui.workspace; const Me = imports.misc.extensionUtils.getCurrentExtension(); const Dock = Me.imports.docking; const Utils = Me.imports.utils; /* * DEFAULT: transparency given by theme * FIXED: constant transparency chosen by user * ADAPTIVE: apply 'transparent' style to dock AND panel when * no windows are close to the dock OR panel. * When dock is hidden, the dock 'transparent' style only * apply to itself. * DYNAMIC: apply 'transparent' style when no windows are close to the dock * */ const TransparencyMode = { DEFAULT: 0, FIXED: 1, ADAPTIVE: 2, DYNAMIC: 3 }; /** * Manage theme customization and custom theme support */ var ThemeManager = new Lang.Class({ Name: 'DashToDock.ThemeManager', _init: function(settings, dock) { this._settings = settings; this._signalsHandler = new Utils.GlobalSignalsHandler(); this._bindSettingsChanges(); this._actor = dock.actor; this._dash = dock.dash; // initialize colors with generic values this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0}; this._customizedBorder = {red: 0, green: 0, blue: 0, alpha: 0}; this._transparency = new Transparency(this._settings, dock); this._signalsHandler.add([ // When theme changes re-obtain default background color St.ThemeContext.get_for_stage (global.stage), 'changed', Lang.bind(this, this.updateCustomTheme) ], [ // update :overview pseudoclass Main.overview, 'showing', Lang.bind(this, this._onOverviewShowing) ], [ Main.overview, 'hiding', Lang.bind(this, this._onOverviewHiding) ]); this._updateCustomStyleClasses(); // destroy themeManager when the managed actor is destroyed (e.g. extension unload) // in order to disconnect signals this._actor.connect('destroy', Lang.bind(this, this.destroy)); }, destroy: function() { this._signalsHandler.destroy(); this._transparency.destroy(); }, _onOverviewShowing: function() { this._actor.add_style_pseudo_class('overview'); }, _onOverviewHiding: function() { this._actor.remove_style_pseudo_class('overview'); }, _updateDashOpacity: function() { let newAlpha = this._settings.get_double('background-opacity'); let [backgroundColor, borderColor] = this._getDefaultColors(); if (backgroundColor==null) return; // Get the background and border alphas. We check the background alpha // for a minimum of .001 to prevent division by 0 errors let backgroundAlpha = Math.max(Math.round(backgroundColor.alpha/2.55)/100, .001); let borderAlpha = Math.round(borderColor.alpha/2.55)/100; // The border and background alphas should remain in sync // We also limit the borderAlpha to a maximum of 1 (full opacity) borderAlpha = Math.min((borderAlpha/backgroundAlpha)*newAlpha, 1); this._customizedBackground = 'rgba(' + backgroundColor.red + ',' + backgroundColor.green + ',' + backgroundColor.blue + ',' + newAlpha + ')'; this._customizedBorder = 'rgba(' + borderColor.red + ',' + borderColor.green + ',' + borderColor.blue + ',' + borderAlpha + ')'; }, _getDefaultColors: function() { // Prevent shell crash if the actor is not on the stage. // It happens enabling/disabling repeatedly the extension if (!this._dash._container.get_stage()) return [null, null]; // Remove custom style let oldStyle = this._dash._container.get_style(); this._dash._container.set_style(null); let themeNode = this._dash._container.get_theme_node(); this._dash._container.set_style(oldStyle); let backgroundColor = themeNode.get_background_color(); // Just in case the theme has different border colors .. // We want to find the inside border-color of the dock because it is // the side most visible to the user. We do this by finding the side // opposite the position let position = Utils.getPosition(this._settings); let side = position + 2; if (side > 3) side = Math.abs(side - 4); let borderColor = themeNode.get_border_color(side); return [backgroundColor, borderColor]; }, _updateDashColor: function() { // Retrieve the color. If needed we will adjust it before passing it to // this._transparency. let [backgroundColor, borderColor] = this._getDefaultColors(); if (backgroundColor==null) return; if (this._settings.get_boolean('custom-background-color')) { // When applying a custom color, we need to check the alpha value, // if not the opacity will always be overridden by the color below. // Note that if using 'adaptive' or 'dynamic' transparency modes, // the opacity will be set by the opaque/transparent styles anyway. let newAlpha = Math.round(backgroundColor.alpha/2.55)/100; if (this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED) newAlpha = this._settings.get_double('background-opacity'); backgroundColor = Clutter.color_from_string(this._settings.get_string('background-color'))[1]; this._customizedBackground = 'rgba(' + backgroundColor.red + ',' + backgroundColor.green + ',' + backgroundColor.blue + ',' + newAlpha + ')'; this._customizedBorder = this._customizedBackground; } this._transparency.setColor(backgroundColor); }, _updateCustomStyleClasses: function() { if (this._settings.get_boolean('apply-custom-theme')) this._actor.add_style_class_name('dashtodock'); else this._actor.remove_style_class_name('dashtodock'); if (this._settings.get_boolean('custom-theme-shrink')) this._actor.add_style_class_name('shrink'); else this._actor.remove_style_class_name('shrink'); if (this._settings.get_enum('running-indicator-style') !== 0) this._actor.add_style_class_name('running-dots'); else this._actor.remove_style_class_name('running-dots'); // If not the built-in theme option is not selected if (!this._settings.get_boolean('apply-custom-theme')) { if (this._settings.get_boolean('force-straight-corner')) this._actor.add_style_class_name('straight-corner'); else this._actor.remove_style_class_name('straight-corner'); } else { this._actor.remove_style_class_name('straight-corner'); } }, updateCustomTheme: function() { this._updateCustomStyleClasses(); this._updateDashOpacity(); this._updateDashColor(); this._adjustTheme(); this._dash._redisplay(); }, /** * Reimported back and adapted from atomdock */ _adjustTheme: function() { // Prevent shell crash if the actor is not on the stage. // It happens enabling/disabling repeatedly the extension if (!this._dash._container.get_stage()) return; // Remove prior style edits this._dash._container.set_style(null); this._transparency.disable(); // If built-in theme is enabled do nothing else if (this._settings.get_boolean('apply-custom-theme')) return; let newStyle = ''; let position = Utils.getPosition(this._settings); if (!this._settings.get_boolean('custom-theme-shrink')) { // obtain theme border settings let themeNode = this._dash._container.get_theme_node(); let borderColor = themeNode.get_border_color(St.Side.TOP); let borderWidth = themeNode.get_border_width(St.Side.TOP); let borderRadius = themeNode.get_border_radius(St.Corner.TOPRIGHT); // We're copying border and corner styles to left border and top-left // corner, also removing bottom border and bottom-right corner styles let borderInner = ''; let borderRadiusValue = ''; let borderMissingStyle = ''; if (this._rtl && (position != St.Side.RIGHT)) borderMissingStyle = 'border-right: ' + borderWidth + 'px solid ' + borderColor.to_string() + ';'; else if (!this._rtl && (position != St.Side.LEFT)) borderMissingStyle = 'border-left: ' + borderWidth + 'px solid ' + borderColor.to_string() + ';'; switch (position) { case St.Side.LEFT: borderInner = 'border-left'; borderRadiusValue = '0 ' + borderRadius + 'px ' + borderRadius + 'px 0;'; break; case St.Side.RIGHT: borderInner = 'border-right'; borderRadiusValue = borderRadius + 'px 0 0 ' + borderRadius + 'px;'; break; case St.Side.TOP: borderInner = 'border-top'; borderRadiusValue = '0 0 ' + borderRadius + 'px ' + borderRadius + 'px;'; break; case St.Side.BOTTOM: borderInner = 'border-bottom'; borderRadiusValue = borderRadius + 'px ' + borderRadius + 'px 0 0;'; break; } newStyle = borderInner + ': none;' + 'border-radius: ' + borderRadiusValue + borderMissingStyle; // I do call set_style possibly twice so that only the background gets the transition. // The transition-property css rules seems to be unsupported this._dash._container.set_style(newStyle); } // Customize background let fixedTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.FIXED; let defaultTransparency = this._settings.get_enum('transparency-mode') == TransparencyMode.DEFAULT; if (!defaultTransparency && !fixedTransparency) { this._transparency.enable(); } else if (!defaultTransparency || this._settings.get_boolean('custom-background-color')) { newStyle = newStyle + 'background-color:'+ this._customizedBackground + '; ' + 'border-color:'+ this._customizedBorder + '; ' + 'transition-delay: 0s; transition-duration: 0.250s;'; this._dash._container.set_style(newStyle); } }, _bindSettingsChanges: function() { let keys = ['transparency-mode', 'customize-alphas', 'min-alpha', 'max-alpha', 'background-opacity', 'custom-background-color', 'background-color', 'apply-custom-theme', 'custom-theme-shrink', 'custom-theme-running-dots', 'extend-height', 'force-straight-corner']; keys.forEach(function(key) { this._signalsHandler.add([ this._settings, 'changed::' + key, Lang.bind(this, this.updateCustomTheme) ]); }, this); } }); /** * The following class is based on the following upstream commit: * https://git.gnome.org/browse/gnome-shell/commit/?id=447bf55e45b00426ed908b1b1035f472c2466956 * Transparency when free-floating */ const Transparency = new Lang.Class({ Name: 'DashToDock.Transparency', _init: function(settings, dock) { this._settings = settings; this._dash = dock.dash; this._actor = this._dash._container; this._dockActor = dock.actor; this._dock = dock; this._panel = Main.panel; this._position = Utils.getPosition(this._settings); this._backgroundColor = '0,0,0'; this._transparentAlpha = '0.2'; this._opaqueAlpha = '1'; this._transparentAlphaBorder = '0.1'; this._opaqueAlphaBorder = '0.5'; this._transparentTransition = '0ms'; this._opaqueTransition = '0ms'; this._updateStyles(); this._signalsHandler = new Utils.GlobalSignalsHandler(); this._injectionsHandler = new Utils.InjectionsHandler(); this._trackedWindows = new Map(); }, enable: function() { // ensure I never double-register/inject // although it should never happen this.disable(); this._signalsHandler.addWithLabel('transparency', [ global.window_group, 'actor-added', Lang.bind(this, this._onWindowActorAdded) ], [ global.window_group, 'actor-removed', Lang.bind(this, this._onWindowActorRemoved) ], [ global.window_manager, 'switch-workspace', Lang.bind(this, this._updateSolidStyle) ], [ Main.overview, 'hiding', Lang.bind(this, this._updateSolidStyle) ], [ Main.overview, 'showing', Lang.bind(this, this._updateSolidStyle) ]); // Window signals global.window_group.get_children().filter(function(child) { // An irrelevant window actor ('Gnome-shell') produces an error when the signals are // disconnected, therefore do not add signals to it. return child instanceof Meta.WindowActor && child.get_meta_window().get_wm_class() !== 'Gnome-shell'; }).forEach(function(win) { this._onWindowActorAdded(null, win); }, this); if (this._settings.get_enum('transparency-mode') === TransparencyMode.ADAPTIVE) this._enableAdaptive(); if (this._actor.get_stage()) this._updateSolidStyle(); this.emit('transparency-enabled'); }, disable: function() { this._disableAdaptive(); // ensure I never double-register/inject // although it should never happen this._signalsHandler.removeWithLabel('transparency'); for (let key of this._trackedWindows.keys()) this._trackedWindows.get(key).forEach(id => { key.disconnect(id); }); this._trackedWindows.clear(); this.emit('transparency-disabled'); }, destroy: function() { this.disable(); this._signalsHandler.destroy(); this._injectionsHandler.destroy(); }, _onWindowActorAdded: function(container, metaWindowActor) { let signalIds = []; ['allocation-changed', 'notify::visible'].forEach(s => { signalIds.push(metaWindowActor.connect(s, Lang.bind(this, this._updateSolidStyle))); }); this._trackedWindows.set(metaWindowActor, signalIds); }, _onWindowActorRemoved: function(container, metaWindowActor) { if (!this._trackedWindows.get(metaWindowActor)) return; this._trackedWindows.get(metaWindowActor).forEach(id => { metaWindowActor.disconnect(id); }); this._trackedWindows.delete(metaWindowActor); this._updateSolidStyle(); }, _updateSolidStyle: function() { let isNear = this._dockIsNear() || this._panelIsNear(); if (isNear) { this._actor.set_style(this._opaque_style); if (this._panel._updateSolidStyle && this._adaptiveEnabled) { if (this._settings.get_boolean('dock-fixed') || this._panelIsNear()) this._panel._addStyleClassName('solid'); else this._panel._removeStyleClassName('solid'); } } else { this._actor.set_style(this._transparent_style); if (this._panel._updateSolidStyle && this._adaptiveEnabled) this._panel._removeStyleClassName('solid'); } this.emit('solid-style-updated', isNear); }, _dockIsNear: function() { if (this._dockActor.has_style_pseudo_class('overview')) return false; /* Get all the windows in the active workspace that are in the primary monitor and visible */ let activeWorkspace = global.screen.get_active_workspace(); let dash = this._dash; let windows = activeWorkspace.list_windows().filter(function(metaWindow) { return metaWindow.get_monitor() === dash._monitorIndex && metaWindow.showing_on_its_workspace() && metaWindow.get_window_type() != Meta.WindowType.DESKTOP; }); /* Check if at least one window is near enough to the panel. * If the dock is hidden, we need to account for the space it would take * up when it slides out. This is avoid an ugly transition. * */ let factor = 0; if (!this._settings.get_boolean('dock-fixed') && this._dock.getDockState() == Dock.State.HIDDEN) factor = 1; let [leftCoord, topCoord] = this._actor.get_transformed_position(); let threshold; if (this._position === St.Side.LEFT) threshold = leftCoord + this._actor.get_width() * (factor + 1); else if (this._position === St.Side.RIGHT) threshold = leftCoord - this._actor.get_width() * factor; else if (this._position === St.Side.TOP) threshold = topCoord + this._actor.get_height() * (factor + 1); else threshold = topCoord - this._actor.get_height() * factor; let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor; let isNearEnough = windows.some(Lang.bind(this, function(metaWindow) { let coord; if (this._position === St.Side.LEFT) { coord = metaWindow.get_frame_rect().x; return coord < threshold + 5 * scale; } else if (this._position === St.Side.RIGHT) { coord = metaWindow.get_frame_rect().x + metaWindow.get_frame_rect().width; return coord > threshold - 5 * scale; } else if (this._position === St.Side.TOP) { coord = metaWindow.get_frame_rect().y; return coord < threshold + 5 * scale; } else { coord = metaWindow.get_frame_rect().y + metaWindow.get_frame_rect().height; return coord > threshold - 5 * scale; } })); return isNearEnough; }, _panelIsNear: function() { if (!this._panel._updateSolidStyle || this._settings.get_enum('transparency-mode') !== TransparencyMode.ADAPTIVE) return false; if (this._panel.actor.has_style_pseudo_class('overview') || !Main.sessionMode.hasWindows) { this._panel._removeStyleClassName('solid'); return false; } /* Get all the windows in the active workspace that are in the * primary monitor and visible */ let activeWorkspace = global.screen.get_active_workspace(); let windows = activeWorkspace.list_windows().filter(function(metaWindow) { return metaWindow.is_on_primary_monitor() && metaWindow.showing_on_its_workspace() && metaWindow.get_window_type() != Meta.WindowType.DESKTOP; }); /* Check if at least one window is near enough to the panel */ let [, panelTop] = this._panel.actor.get_transformed_position(); let panelBottom = panelTop + this._panel.actor.get_height(); let scale = St.ThemeContext.get_for_stage(global.stage).scale_factor; let isNearEnough = windows.some(Lang.bind(this._panel, function(metaWindow) { let verticalPosition = metaWindow.get_frame_rect().y; return verticalPosition < panelBottom + 5 * scale; })); return isNearEnough; }, _updateStyles: function() { this._getAlphas(); this._transparent_style = 'background-color: rgba(' + this._backgroundColor + ', ' + this._transparentAlpha + ');' + 'border-color: rgba(' + this._backgroundColor + ', ' + this._transparentAlphaBorder + ');' + 'transition-duration: ' + this._transparentTransition + 'ms;'; this._opaque_style = 'background-color: rgba(' + this._backgroundColor + ', ' + this._opaqueAlpha + ');' + 'border-color: rgba(' + this._backgroundColor + ',' + this._opaqueAlphaBorder + ');' + 'transition-duration: ' + this._opaqueTransition + 'ms;'; this.emit('styles-updated'); }, setColor: function(color) { this._backgroundColor = color.red + ',' + color.green + ',' + color.blue; this._updateStyles(); }, _getAlphas: function() { // Create dummy object and add to the uiGroup to get it to the stage let dummyObject = new St.Bin({ name: 'dashtodockContainer', }); Main.uiGroup.add_child(dummyObject); dummyObject.add_style_class_name('opaque'); let themeNode = dummyObject.get_theme_node(); this._opaqueAlpha = themeNode.get_background_color().alpha / 255; this._opaqueAlphaBorder = themeNode.get_border_color(0).alpha / 255; this._opaqueTransition = themeNode.get_transition_duration(); dummyObject.add_style_class_name('transparent'); themeNode = dummyObject.get_theme_node(); this._transparentAlpha = themeNode.get_background_color().alpha / 255; this._transparentAlphaBorder = themeNode.get_border_color(0).alpha / 255; this._transparentTransition = themeNode.get_transition_duration(); Main.uiGroup.remove_child(dummyObject); if (this._settings.get_boolean('customize-alphas')) { this._opaqueAlpha = this._settings.get_double('max-alpha'); this._opaqueAlphaBorder = this._opaqueAlpha / 2; this._transparentAlpha = this._settings.get_double('min-alpha'); this._transparentAlphaBorder = this._transparentAlpha / 2; } if (this._settings.get_enum('transparency-mode') === TransparencyMode.ADAPTIVE && this._panel._updateSolidStyle) { themeNode = this._panel.actor.get_theme_node(); if (this._panel.actor.has_style_class_name('solid')) { this._opaqueTransition = themeNode.get_transition_duration(); this._panel._removeStyleClassName('solid'); themeNode = this._panel.actor.get_theme_node(); this._transparentTransition = themeNode.get_transition_duration(); this._panel._addStyleClassName('solid'); } else { this._transparentTransition = themeNode.get_transition_duration(); this._panel._addStyleClassName('solid'); themeNode = this._panel.actor.get_theme_node(); this._opaqueTransition = themeNode.get_transition_duration(); this._panel._removeStyleClassName('solid'); } } }, _enableAdaptive: function() { if (!this._panel._updateSolidStyle || this._dash._monitorIndex !== Main.layoutManager.primaryIndex) return; this._adaptiveEnabled = true; function UpdateSolidStyle() { return; } this._injectionsHandler.addWithLabel('adaptive', [ this._panel, '_updateSolidStyle', UpdateSolidStyle ]); // Once we injected the new function, we need to disconnect and // reconnect all window signals. for (let key of this._panel._trackedWindows.keys()) this._panel._trackedWindows.get(key).forEach(id => { key.disconnect(id); }); for (let win of this._panel._trackedWindows.keys()) this._panel._onWindowActorAdded(null, win); }, _disableAdaptive: function() { if (!this._adaptiveEnabled) return; this._injectionsHandler.removeWithLabel('adaptive'); this._adaptiveEnabled = false; // Once we removed the injection, we need to disconnect and // reconnect all window signals. for (let key of this._panel._trackedWindows.keys()) this._panel._trackedWindows.get(key).forEach(id => { key.disconnect(id); }); for (let win of this._panel._trackedWindows.keys()) this._panel._onWindowActorAdded(null, win); } }); Signals.addSignalMethods(Transparency.prototype);