diff --git a/.gitignore b/.gitignore index 062535b..fdc00a8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ zorin-taskbar@zorinos.com*.zip *.mo po/zorin-taskbar.pot ui/*.ui.h +node_modules/ diff --git a/Makefile b/Makefile index 0dfa877..66e451b 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,11 @@ # Basic Makefile UUID = zorin-taskbar@zorinos.com -BASE_MODULES = extension.js stylesheet.css metadata.json COPYING README.md -EXTRA_MODULES = appIcons.js panel.js panelManager.js proximity.js intellihide.js progress.js panelPositions.js panelSettings.js panelStyle.js overview.js taskbar.js transparency.js windowPreview.js prefs.js utils.js desktopIconsIntegration.js -UI_MODULES = ui/BoxDynamicOpacityOptions.ui ui/BoxGroupAppsOptions.ui ui/BoxIntellihideOptions.ui ui/BoxMiddleClickOptions.ui ui/BoxOverlayShortcut.ui ui/BoxShowDateMenuOptions.ui ui/BoxShowDesktopOptions.ui ui/BoxWindowPreviewOptions.ui ui/SettingsAction.ui ui/SettingsBehavior.ui ui/SettingsPosition.ui ui/SettingsStyle.ui +MODULES = src/*.js src/stylesheet.css metadata.json COPYING README.md +UI_MODULES = ui/*.ui +IMAGES = ./* -EXTRA_IMAGES = show-desktop-symbolic.svg - -TOLOCALIZE = prefs.js appIcons.js +TOLOCALIZE = src/extension.js src/prefs.js src/appIcons.js src/taskbar.js MSGSRC = $(wildcard po/*.po) ifeq ($(strip $(DESTDIR)),) INSTALLBASE = $(HOME)/.local/share/gnome-shell/extensions @@ -49,7 +47,7 @@ mergepo: potfile ./po/zorin-taskbar.pot: $(TOLOCALIZE) mkdir -p po xgettext -k_ -kN_ -o po/zorin-taskbar.pot --package-name "Zorin Taskbar" $(TOLOCALIZE) --from-code=UTF-8 - + for l in $(UI_MODULES) ; do \ intltool-extract --type=gettext/glade $$l; \ xgettext -k_ -kN_ -o po/zorin-taskbar.pot $$l.h --join-existing --from-code=UTF-8; \ @@ -79,12 +77,12 @@ zip-file: _build _build: all -rm -fR ./_build mkdir -p _build - cp $(BASE_MODULES) $(EXTRA_MODULES) _build + cp $(MODULES) _build mkdir -p _build/ui cp $(UI_MODULES) _build/ui mkdir -p _build/img - cd img ; cp $(EXTRA_IMAGES) ../_build/img/ + cd img ; cp $(IMAGES) ../_build/img/ mkdir -p _build/schemas cp schemas/*.xml _build/schemas/ cp schemas/gschemas.compiled _build/schemas/ diff --git a/appIcons.js b/appIcons.js deleted file mode 100644 index 60fdbce..0000000 --- a/appIcons.js +++ /dev/null @@ -1,1666 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * Some code was also adapted from the upstream Gnome Shell source code. - */ - - -import Clutter from 'gi://Clutter'; -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import Graphene from 'gi://Graphene'; -import GObject from 'gi://GObject'; -import Mtk from 'gi://Mtk'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js'; -import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import * as Util from 'resource:///org/gnome/shell/misc/util.js'; -import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; - -import * as Utils from './utils.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Taskbar from './taskbar.js'; -import * as Progress from './progress.js'; -import {DTP_EXTENSION, SETTINGS, DESKTOPSETTINGS, EXTENSION_PATH} from './extension.js'; -import {gettext as _, ngettext} from 'resource:///org/gnome/shell/extensions/extension.js'; - -//timeout names -const T2 = 'mouseScrollTimeout'; -const T3 = 'showDotsTimeout'; -const T4 = 'overviewWindowDragEndTimeout'; -const T5 = 'switchWorkspaceTimeout'; -const T6 = 'displayProperIndicatorTimeout'; - -const SCROLL_ICON_DELAY = 400; -const DOT_SIZE = 3; - -//right padding defined for .overview-label in stylesheet.css -const TITLE_RIGHT_PADDING = 8; -const DOUBLE_CLICK_DELAY_MS = 450; - -let LABEL_GAP = 5; -let MAX_INDICATORS = 4; -export const DEFAULT_PADDING_SIZE = 4; - -let DOT_STYLE = { - DOTS: "DOTS", - SQUARES: "SQUARES", - DASHES: "DASHES", - SEGMENTED: "SEGMENTED", - CILIORA: "CILIORA", - METRO: "METRO", - SOLID: "SOLID" -} - -let DOT_POSITION = { - TOP: "TOP", - BOTTOM: "BOTTOM", - LEFT: 'LEFT', - RIGHT: 'RIGHT' -} - -let recentlyClickedAppLoopId = 0; -let recentlyClickedApp = null; -let recentlyClickedAppWindows = null; -let recentlyClickedAppIndex = 0; -let recentlyClickedAppMonitorIndex; - -let tracker = Shell.WindowTracker.get_default(); - -/** - * Extend AppIcon - * - * - Apply a css class based on the number of windows of each application (#N); - * - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N); - * a class of the form "running#N" is applied to the AppWellIcon actor. - * like the original .running one. - * - add a .focused style to the focused app - * - Customize click actions. - * - Update minimization animation target - * - */ - -export const TaskbarAppIcon = GObject.registerClass({ -}, class TaskbarAppIcon extends AppDisplay.AppIcon { - - _init(appInfo, panel, iconParams, previewMenu, iconAnimator) { - this.dtpPanel = panel; - this._nWindows = 0; - this.window = appInfo.window; - this.isLauncher = appInfo.isLauncher; - this._previewMenu = previewMenu; - this.iconAnimator = iconAnimator; - this.lastClick = 0; - - super._init(appInfo.app, iconParams); - - this._timeoutsHandler = new Utils.TimeoutsHandler(); - - // Fix touchscreen issues before the listener is added by the parent constructor. - this._onTouchEvent = function(actor, event) { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) { - // Open the popup menu on long press. - this._setPopupTimeout(); - } else if (this._menuTimeoutId != 0 && (event.type() == Clutter.EventType.TOUCH_END || event.type() == Clutter.EventType.TOUCH_CANCEL)) { - // Activate/launch the application. - this.activate(1); - this._removeMenuTimeout(); - } - // Disable dragging via touch screen as it's buggy as hell. Not perfect for tablet users, but the alternative is way worse. - // Also, EVENT_PROPAGATE launches applications twice with this solution, so this.activate(1) above must only be called if there's already a window. - return Clutter.EVENT_STOP; - }; - // Hack for missing TOUCH_END event. - this._onLeaveEvent = function(actor, event) { - this.fake_release(); - if (this._menuTimeoutId != 0) this.activate(1); // Activate/launch the application if TOUCH_END didn't fire. - this._removeMenuTimeout(); - }; - - - this._dot.set_width(0); - this._isGroupApps = SETTINGS.get_boolean('group-apps'); - - this._container = new St.Widget({ style_class: 'dtp-container', layout_manager: new Clutter.BinLayout() }); - this._dotsContainer = new St.Widget({ layout_manager: new Clutter.BinLayout() }); - this._dtpIconContainer = new St.Widget({ layout_manager: new Clutter.BinLayout(), style: getIconContainerStyle(panel.checkIfVertical()) }); - - this.remove_child(this._iconContainer); - - this._dtpIconContainer.add_child(this._iconContainer); - - if (appInfo.window) { - let box = new St.BoxLayout(); - - this._windowTitle = new St.Label({ - y_align: Clutter.ActorAlign.CENTER, - x_align: Clutter.ActorAlign.START, - style_class: 'overview-label' - }); - - this._updateWindowTitle(); - this._updateWindowTitleStyle(); - - this._scaleFactorChangedId = Utils.getStageTheme().connect('changed', () => this._updateWindowTitleStyle()); - - box.add_child(this._dtpIconContainer); - box.add_child(this._windowTitle); - - this._dotsContainer.add_child(box); - } else { - this._dotsContainer.add_child(this._dtpIconContainer); - } - - this._container.add_child(this._dotsContainer); - this.set_child(this._container); - - if (panel.checkIfVertical()) { - this.set_width(panel.geom.w); - } - - // Monitor windows-changes instead of app state. - // Keep using the same Id and function callback (that is extended) - if(this._stateChangedId > 0) { - this.app.disconnect(this._stateChangedId); - this._stateChangedId = 0; - } - - this._setAppIconPadding(); - this._showDots(); - - this._focusWindowChangedId = global.display.connect('notify::focus-window', - this._onFocusAppChanged.bind(this)); - - this._windowEnteredMonitorId = this._windowLeftMonitorId = 0; - this._stateChangedId = this.app.connect('windows-changed', this.onWindowsChanged.bind(this)); - - if (!this.window) { - if (SETTINGS.get_boolean('isolate-monitors')) { - this._windowEnteredMonitorId = Utils.DisplayWrapper.getScreen().connect('window-entered-monitor', this.onWindowEnteredOrLeft.bind(this)); - this._windowLeftMonitorId = Utils.DisplayWrapper.getScreen().connect('window-left-monitor', this.onWindowEnteredOrLeft.bind(this)); - } - - this._titleWindowChangeId = 0; - this._minimizedWindowChangeId = 0; - } else { - this._titleWindowChangeId = this.window.connect('notify::title', - this._updateWindowTitle.bind(this)); - - this._minimizedWindowChangeId = this.window.connect('notify::minimized', - this._updateWindowTitleStyle.bind(this)); - } - - this._scrollEventId = this.connect('scroll-event', this._onMouseScroll.bind(this)); - - this._overviewWindowDragEndId = Main.overview.connect('window-drag-end', - this._onOverviewWindowDragEnd.bind(this)); - - this._switchWorkspaceId = global.window_manager.connect('switch-workspace', - this._onSwitchWorkspace.bind(this)); - - this._hoverChangeId = this.connect('notify::hover', () => this._onAppIconHoverChanged()); - - this._dtpSettingsSignalIds = [ - SETTINGS.connect('changed::panel-positions', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-style-focused', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::dot-style-unfocused', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::panel-positions', this._settingsChangeRefresh.bind(this)), - SETTINGS.connect('changed::group-apps-label-max-width', this._updateWindowTitleStyle.bind(this)), - SETTINGS.connect('changed::group-apps-use-fixed-width', this._updateWindowTitleStyle.bind(this)), - ] - - this._progressIndicator = new Progress.ProgressIndicator(this, panel.progressManager); - - this._numberOverlay(); - } - - getDragActor() { - return this.app.create_icon_texture(this.dtpPanel.taskbar.iconSize); - } - - shouldShowTooltip() { - if (!SETTINGS.get_boolean('show-tooltip') || - (!this.isLauncher && SETTINGS.get_boolean("show-window-previews") && - this.getAppIconInterestingWindows().length > 0)) { - return false; - } else { - return this.hover && !this.window && - (!this._menu || !this._menu.isOpen) && - (this._previewMenu.getCurrentAppIcon() !== this); - } - } - - _onAppIconHoverChanged() { - if (!SETTINGS.get_boolean('show-window-previews') || - (!this.window && !this._nWindows)) { - return; - } - - if (this.hover) { - this._previewMenu.requestOpen(this); - } else { - this._previewMenu.requestClose(); - } - } - - _onDestroy() { - super._onDestroy(); - - this._timeoutsHandler.destroy(); - - this._previewMenu.close(true); - - // Disconect global signals - if (this._stateChangedId > 0) { - this.app.disconnect(this._stateChangedId); - this._stateChangedId = 0; - } - - if(this._overviewWindowDragEndId) - Main.overview.disconnect(this._overviewWindowDragEndId); - - if(this._focusWindowChangedId) - global.display.disconnect(this._focusWindowChangedId); - - if(this._titleWindowChangeId) - this.window.disconnect(this._titleWindowChangeId); - - if(this._minimizedWindowChangeId) - this.window.disconnect(this._minimizedWindowChangeId); - - if (this._windowEnteredMonitorId) { - Utils.DisplayWrapper.getScreen().disconnect(this._windowEnteredMonitorId); - Utils.DisplayWrapper.getScreen().disconnect(this._windowLeftMonitorId); - } - - if(this._switchWorkspaceId) - global.window_manager.disconnect(this._switchWorkspaceId); - - if(this._scaleFactorChangedId) - Utils.getStageTheme().disconnect(this._scaleFactorChangedId); - - if (this._hoverChangeId) { - this.disconnect(this._hoverChangeId); - } - - if (this._scrollEventId) { - this.disconnect(this._scrollEventId); - } - - for (let i = 0; i < this._dtpSettingsSignalIds.length; ++i) { - SETTINGS.disconnect(this._dtpSettingsSignalIds[i]); - } - } - - onWindowsChanged() { - this._updateWindows(); - this.updateIcon(); - - if (this._isGroupApps) - this._setIconStyle(); - } - - onWindowEnteredOrLeft(display, number, metaWindow) { - if (number > 0 && tracker.get_window_app(metaWindow) == this.app) { - this._updateWindows(); - this._displayProperIndicator(); - } - } - - updateTitleStyle() { - this._updateWindowTitleStyle(); - } - - // Update indicator and target for minimization animation - updateIcon() { - - // If (for unknown reason) the actor is not on the stage the reported size - // and position are random values, which might exceeds the integer range - // resulting in an error when assigned to the a rect. This is a more like - // a workaround to prevent flooding the system with errors. - if (this.get_stage() == null) - return; - - let rect = new Mtk.Rectangle(); - - [rect.x, rect.y] = this.get_transformed_position(); - [rect.width, rect.height] = this.get_transformed_size(); - - let windows = this.window ? [this.window] : this.getAppIconInterestingWindows(true); - windows.forEach(function(w) { - w.set_icon_geometry(rect); - }); - } - - _onMouseScroll(actor, event) { - let scrollAction = SETTINGS.get_string('scroll-icon-action'); - - if (scrollAction === 'NOTHING' || (!this.window && !this._nWindows)) { - return; - } - - let direction = Utils.getMouseScrollDirection(event); - - if (direction && !this._timeoutsHandler.getId(T2)) { - this._timeoutsHandler.add([T2, SCROLL_ICON_DELAY, () => {}]); - - let windows = this.getAppIconInterestingWindows(); - - windows.sort(Taskbar.sortWindowsCompareFunction); - Utils.activateSiblingWindow(windows, direction, this.window); - } - } - - _showDots() { - // Just update style if dots already exist - if (this._focusedDots && this._unfocusedDots) { - this._updateWindows(); - return; - } - - if (!this._isGroupApps) { - this._focusedDots = new St.Widget({ - layout_manager: new Clutter.BinLayout(), - x_expand: true, y_expand: true, - visible: false - }); - - let mappedId = this.connect('notify::mapped', () => { - this._displayProperIndicator(); - this.disconnect(mappedId); - }); - } else { - this._focusedDots = new St.DrawingArea(), - this._unfocusedDots = new St.DrawingArea(); - - this._focusedDots.connect('repaint', () => { - if (!this._dashItemContainer.animatingOut) - // don't draw and trigger more animations if the icon is in the middle of - // being removed from the panel - this._drawRunningIndicator(this._focusedDots, SETTINGS.get_string('dot-style-focused'), true); - }); - - this._unfocusedDots.connect('repaint', () => { - if (!this._dashItemContainer.animatingOut) - this._drawRunningIndicator(this._unfocusedDots, SETTINGS.get_string('dot-style-unfocused'), false); - }); - - this._dotsContainer.add_child(this._unfocusedDots); - - this._updateWindows(); - - this._timeoutsHandler.add([T3, 0, () => { - this._resetDots(); - this._displayProperIndicator(); - }]); - } - - this._dotsContainer.add_child(this._focusedDots); - } - - _resetDots() { - let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; - let sizeProp = isHorizontalDots ? 'width' : 'height'; - let focusedDotStyle = SETTINGS.get_string('dot-style-focused'); - let unfocusedDotStyle = SETTINGS.get_string('dot-style-unfocused'); - - this._focusedIsWide = this._isWideDotStyle(focusedDotStyle); - this._unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle); - - [, this._containerSize] = this._container[`get_preferred_${sizeProp}`](-1); - - [this._focusedDots, this._unfocusedDots].forEach(d => { - d.set_size(-1, -1); - d.x_expand = d.y_expand = false; - - d[sizeProp] = 1; - d[(isHorizontalDots ? 'y' : 'x') + '_expand'] = true; - }); - } - - _settingsChangeRefresh() { - if (this._isGroupApps) { - this._updateWindows(); - this._resetDots(); - this._focusedDots.queue_repaint(); - this._unfocusedDots.queue_repaint(); - } - - this._displayProperIndicator(); - } - - _updateWindowTitleStyle() { - if (this._windowTitle) { - let useFixedWidth = SETTINGS.get_boolean('group-apps-use-fixed-width'); - let scaleFactor = Utils.getScaleFactor(); - let maxLabelWidth = SETTINGS.get_int('group-apps-label-max-width') * scaleFactor; - let variableWidth = !useFixedWidth || this.dtpPanel.checkIfVertical() || this.dtpPanel.taskbar.fullScrollView; - - this._windowTitle[(maxLabelWidth > 0 ? 'show' : 'hide')](); - this._windowTitle.set_width(variableWidth ? -1 : maxLabelWidth + TITLE_RIGHT_PADDING * scaleFactor); - - this._windowTitle.clutter_text.natural_width = useFixedWidth ? maxLabelWidth : 0; - this._windowTitle.clutter_text.natural_width_set = useFixedWidth; - - this._windowTitle.set_style(useFixedWidth ? '' : 'max-width: ' + maxLabelWidth + 'px;'); - } - } - - _updateWindowTitle() { - if (this._windowTitle.text != this.window.title) { - this._windowTitle.text = (this.window.title ? this.window.title : this.app.get_name()).replace(/\r?\n|\r/g, '').trim(); - - if (this._focusedDots) { - this._displayProperIndicator(); - } - } - } - - _setIconStyle(isFocused) { - let inlineStyle = 'margin: 0;'; - - if(this._dotsContainer.get_style() != inlineStyle) { - this._dotsContainer.set_style(inlineStyle); - } - } - - _checkIfFocusedApp() { - return tracker.focus_app == this.app; - } - - _checkIfMonitorHasFocus() { - return global.display.focus_window && - (!SETTINGS.get_boolean('multi-monitors') || // only check same monitor index if multi window is enabled. - !SETTINGS.get_boolean('isolate-monitors') || - global.display.focus_window.get_monitor() === this.dtpPanel.monitor.index); - } - - _setAppIconPadding() { - let padding = getIconPadding(this.dtpPanel.monitor.index); - let margin = Taskbar.APPICON_MARGIN; - - this.set_style('padding:' + (this.dtpPanel.checkIfVertical() ? margin + 'px 0' : '0 ' + margin + 'px;')); - this._iconContainer.set_style('padding: ' + padding + 'px;'); - } - - popupMenu() { - this._removeMenuTimeout(); - this.fake_release(); - - if (!this._menu) { - this._menu = new TaskbarSecondaryMenu(this, this.dtpPanel.geom.position); - this._menu.setApp(this.app); - this._menu.connect('open-state-changed', (menu, isPoppedUp) => { - if (!isPoppedUp) - this._onMenuPoppedDown(); - else - this._previewMenu.close(true); - }); - let id = Main.overview.connect('hiding', () => { - this._menu.close(); - }); - this.connect('destroy', () => { - Main.overview.disconnect(id); - }); - - // We want to keep the item hovered while the menu is up - this._menu.blockSourceEvents = true; - - Main.uiGroup.add_child(this._menu.actor); - this._menuManager.addMenu(this._menu); - } - this._menu.updateQuitText(); - - this.emit('menu-state-changed', true); - - this.set_hover(true); - this._menu.open(BoxPointer.PopupAnimation.FULL); - this._menuManager.ignoreRelease(); - this.emit('sync-tooltip'); - - return false; - } - - _onFocusAppChanged(windowTracker) { - this._displayProperIndicator(); - } - - _onOverviewWindowDragEnd(windowTracker) { - this._timeoutsHandler.add([T4, 0, () => { - if (SETTINGS.get_boolean('isolate-workspaces')) - this._updateWindows() - - this._displayProperIndicator() - }]); - } - - _onSwitchWorkspace(windowTracker) { - if (this._isGroupApps) { - this._timeoutsHandler.add([T5, 0, () => this._displayProperIndicator()]); - } else { - this._displayProperIndicator(); - } - } - - _displayProperIndicator() { - let isFocused = this._isFocusedWindow(); - let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; - - this._setIconStyle(isFocused); - - if(!this._isGroupApps) { - if (this.window && isFocused) { - let align = Clutter.ActorAlign[position == DOT_POSITION.TOP || position == DOT_POSITION.LEFT ? 'START' : 'END']; - - this._focusedDots.set_size(0, 0); - this._focusedDots[isHorizontalDots ? 'height' : 'width'] = this._getRunningIndicatorSize(); - - this._focusedDots.y_align = this._focusedDots.x_align = Clutter.ActorAlign.FILL; - this._focusedDots[(isHorizontalDots ? 'y' : 'x') + '_align'] = align; - this._focusedDots.background_color = this._getRunningIndicatorColor(isFocused); - this._focusedDots.show(); - } else if (this._focusedDots.visible) { - this._focusedDots.hide(); - } - } else { - let sizeProp = isHorizontalDots ? 'width' : 'height'; - let newFocusedDotsSize = 0; - let newFocusedDotsOpacity = 0; - let newUnfocusedDotsSize = 0; - let newUnfocusedDotsOpacity = 0; - - isFocused = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus(); - - this._timeoutsHandler.add([T6, 0, () => { - if(isFocused) - this.add_style_class_name('focused'); - else - this.remove_style_class_name('focused'); - }]); - - if(this._focusedIsWide) { - newFocusedDotsSize = (isFocused && this._nWindows > 0) ? this._containerSize : 0; - newFocusedDotsOpacity = 255; - } else { - newFocusedDotsSize = this._containerSize; - newFocusedDotsOpacity = (isFocused && this._nWindows > 0) ? 255 : 0; - } - - if(this._unfocusedIsWide) { - newUnfocusedDotsSize = (!isFocused && this._nWindows > 0) ? this._containerSize : 0; - newUnfocusedDotsOpacity = 255; - } else { - newUnfocusedDotsSize = this._containerSize; - newUnfocusedDotsOpacity = (!isFocused && this._nWindows > 0) ? 255 : 0; - } - - // Only animate if... - // going from a wide style to a narrow style indicator or vice-versa - // OR going from an open app to a closed app or vice versa - let animate = (this._focusedIsWide != this._unfocusedIsWide) || - (this._focusedDots[sizeProp] != newUnfocusedDotsSize || this._unfocusedDots[sizeProp] != newFocusedDotsSize) - let duration = animate ? Taskbar.DASH_ANIMATION_TIME : 0.001; - - this._animateDotDisplay(this._focusedDots, newFocusedDotsSize, this._unfocusedDots, newUnfocusedDotsOpacity, sizeProp, duration); - this._animateDotDisplay(this._unfocusedDots, newUnfocusedDotsSize, this._focusedDots, newFocusedDotsOpacity, sizeProp, duration); - } - } - - _animateDotDisplay(dots, newSize, otherDots, newOtherOpacity, sizeProp, duration) { - Utils.stopAnimations(dots) - - let tweenOpts = { - time: duration, - transition: 'easeInOutCubic', - onComplete: () => { - if(newOtherOpacity > 0) - otherDots.opacity = newOtherOpacity; - } - }; - - if(newOtherOpacity == 0) - otherDots.opacity = newOtherOpacity; - - tweenOpts[sizeProp] = newSize; - - Utils.animate(dots, tweenOpts); - } - - _isFocusedWindow() { - let focusedWindow = global.display.focus_window; - - while (focusedWindow) { - if (focusedWindow == this.window) { - return true; - } - - focusedWindow = focusedWindow.get_transient_for(); - } - - return false; - } - - _isWideDotStyle(dotStyle) { - return dotStyle == DOT_STYLE.SEGMENTED || - dotStyle == DOT_STYLE.CILIORA || - dotStyle == DOT_STYLE.METRO || - dotStyle == DOT_STYLE.SOLID; - } - - _isThemeProvidingIndicator() { - // This is an attempt to determine if the theme is providing their own - // running indicator by way of a border image on the icon, for example in - // the theme Ciliora - return (this.icon.get_stage() && - this.icon.get_theme_node().get_border_image()); - } - - activate(button, modifiers, handleAsGrouped) { - let event = Clutter.get_current_event(); - - modifiers = event ? event.get_state() : modifiers || 0; - - // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) - modifiers = modifiers & (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK); - - let ctrlPressed = modifiers & Clutter.ModifierType.CONTROL_MASK - - if (ctrlPressed) { - // CTRL-click or hotkey with ctrl - return this._launchNewInstance(true); - } - - // We check what type of click we have and if the modifier SHIFT is - // being used. We then define what buttonAction should be for this - // event. - let buttonAction = 0; - let doubleClick; - - if (button && button == 2 ) { - if (modifiers & Clutter.ModifierType.SHIFT_MASK) - buttonAction = SETTINGS.get_string('shift-middle-click-action'); - else - buttonAction = SETTINGS.get_string('middle-click-action'); - } - // fixed issue #1676 by checking for button 0 or 1 to also handle touchscreen - // input, probably not the proper fix as i'm not aware button 0 should exist - // but from using this fix for months it seems to not create any issues - else if (button === 0 || button === 1) { - let now = global.get_current_time() - - doubleClick = now - this.lastClick < DOUBLE_CLICK_DELAY_MS - this.lastClick = now - - if (modifiers & Clutter.ModifierType.SHIFT_MASK) - buttonAction = SETTINGS.get_string('shift-click-action'); - else - buttonAction = SETTINGS.get_string('click-action'); - } - - let closePreview = () => this._previewMenu.close(true); - let appCount = this.getAppIconInterestingWindows().length; - let previewedAppIcon = this._previewMenu.getCurrentAppIcon(); - - if (this.window || buttonAction != 'TOGGLE-SHOWPREVIEW') - closePreview() - - // We check if the app is running, and that the # of windows is > 0 in - // case we use workspace isolation, - let appIsRunning = this.app.state == Shell.AppState.RUNNING && appCount > 0; - - // We customize the action only when the application is already running - if (appIsRunning && !this.isLauncher) { - if (this.window && !handleAsGrouped) { - //ungrouped applications behaviors - switch (buttonAction) { - case 'RAISE': case 'CYCLE': case 'CYCLE-MIN': case 'MINIMIZE': case 'TOGGLE-SHOWPREVIEW': case 'TOGGLE-CYCLE': - if (!Main.overview._shown && - (buttonAction == 'MINIMIZE' || buttonAction == 'TOGGLE-SHOWPREVIEW' || buttonAction == 'TOGGLE-CYCLE' || buttonAction == 'CYCLE-MIN') && - (this._isFocusedWindow() || (buttonAction == 'MINIMIZE' && (button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK)))) { - this.window.minimize(); - } else { - Main.activateWindow(this.window); - } - - break; - - case "LAUNCH": - this._launchNewInstance(); - break; - - case "QUIT": - this.window.delete(global.get_current_time()); - break; - } - } else { - //grouped application behaviors - let monitor = this.dtpPanel.monitor; - let appHasFocus = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus(); - - switch (buttonAction) { - case "RAISE": - activateAllWindows(this.app, monitor); - break; - - case "LAUNCH": - this._launchNewInstance(); - break; - - case "MINIMIZE": - // In overview just activate the app, unless the acion is explicitely - // requested with a keyboard modifier - if (!Main.overview._shown || modifiers){ - // If we have button=2 or a modifier, allow minimization even if - // the app is not focused - if (appHasFocus || button == 2 || modifiers & Clutter.ModifierType.SHIFT_MASK) { - // minimize all windows on double click and always in the case of primary click without - // additional modifiers - let all_windows = (button == 1 && ! modifiers) || doubleClick; - minimizeWindow(this.app, all_windows, monitor); - } - else - activateAllWindows(this.app, monitor); - } - else - this.app.activate(); - break; - - case "CYCLE": - if (!Main.overview._shown){ - if (appHasFocus) - cycleThroughWindows(this.app, false, false, monitor); - else { - activateFirstWindow(this.app, monitor); - } - } - else - this.app.activate(); - break; - case "CYCLE-MIN": - if (!Main.overview._shown){ - if (appHasFocus || (recentlyClickedApp == this.app && recentlyClickedAppWindows[recentlyClickedAppIndex % recentlyClickedAppWindows.length] == "MINIMIZE")) - cycleThroughWindows(this.app, false, true, monitor); - else { - activateFirstWindow(this.app, monitor); - } - } - else - this.app.activate(); - break; - case "TOGGLE-SHOWPREVIEW": - if (!Main.overview._shown) { - if (appCount == 1) { - closePreview() - - if (appHasFocus) - minimizeWindow(this.app, false, monitor); - else - activateFirstWindow(this.app, monitor); - } else { - if (doubleClick) { - // minimize all windows if double clicked - closePreview() - minimizeWindow(this.app, true, monitor); - } else if (previewedAppIcon != this) { - this._previewMenu.open(this); - } - - this.emit('sync-tooltip'); - } - } - else - this.app.activate(); - break; - case "TOGGLE-CYCLE": - if (!Main.overview._shown) { - if (appCount == 1) { - if (appHasFocus) - minimizeWindow(this.app, false, monitor); - else - activateFirstWindow(this.app, monitor); - } else { - cycleThroughWindows(this.app, false, false, monitor); - } - } - else - this.app.activate(); - break; - case "QUIT": - closeAllWindows(this.app, monitor); - break; - } - } - } - else { - this._launchNewInstance(); - } - - global.display.emit('grab-op-begin', null, null); - Main.overview.hide(); - } - - _launchNewInstance(ctrlPressed) { - if ((ctrlPressed || this.app.state == Shell.AppState.RUNNING) && - this.app.can_open_new_window()) { - this.animateLaunch(); - this.app.open_new_window(-1); - } else { - let windows = this.window ? [this.window] : this.app.get_windows(); - - if (windows.length) { - Main.activateWindow(windows[0]); - } else { - this.animateLaunch(); - this.app.activate(); - } - } - } - - _updateWindows() { - let windows = [this.window]; - - if (!this.window) { - windows = this.getAppIconInterestingWindows(); - - this._nWindows = windows.length; - - for (let i = 1; i <= MAX_INDICATORS; i++){ - let className = 'running'+i; - if(i != this._nWindows) - this.remove_style_class_name(className); - else - this.add_style_class_name(className); - } - } - - this._previewMenu.update(this, windows); - } - - _getRunningIndicatorCount() { - return Math.min(this._nWindows, MAX_INDICATORS); - } - - _getRunningIndicatorSize() { - return DOT_SIZE * Utils.getScaleFactor(); - } - - _getRunningIndicatorColor(isFocused) { - let color; - const fallbackColor = new Utils.ColorUtils.Color({ red: 82, green: 148, blue: 226, alpha: 255 }); - - // Re-use the style - background color, and border width and color - - // of the default dot - let themeNode = this._dot.get_theme_node(); - color = themeNode.get_background_color(); - - // theme didn't provide one, use a default - if(color.alpha == 0) color = fallbackColor; - - return color; - } - - _drawRunningIndicator(area, type, isFocused) { - let n = this._getRunningIndicatorCount(); - - if (!n) { - return; - } - - let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index); - let isHorizontalDots = position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM; - let bodyColor = this._getRunningIndicatorColor(isFocused); - let [areaWidth, areaHeight] = area.get_surface_size(); - let cr = area.get_context(); - let size = this._getRunningIndicatorSize(); - - let areaSize = areaWidth; - let startX = 0; - let startY = 0; - - if (isHorizontalDots) { - if (position == DOT_POSITION.BOTTOM) { - startY = areaHeight - size; - } - } else { - areaSize = areaHeight; - - if (position == DOT_POSITION.RIGHT) { - startX = areaWidth - size; - } - } - - if (type == DOT_STYLE.SOLID || type == DOT_STYLE.METRO) { - if (type == DOT_STYLE.SOLID || n <= 1) { - cr.translate(startX, startY); - cr.setSourceColor(bodyColor); - cr.newSubPath(); - cr.rectangle.apply(cr, [0, 0].concat(isHorizontalDots ? [areaSize, size] : [size, areaSize])); - cr.fill(); - } else { - let blackenedLength = (1 / 48) * areaSize; // need to scale with the SVG for the stacked highlight - let darkenedLength = isFocused ? (2 / 48) * areaSize : (10 / 48) * areaSize; - let blackenedColor = new Utils.ColorUtils.Color({ red: bodyColor.red * .3, green: bodyColor.green * .3, blue: bodyColor.blue * .3, alpha: bodyColor.alpha }); - let darkenedColor = new Utils.ColorUtils.Color({ red: bodyColor.red * .7, green: bodyColor.green * .7, blue: bodyColor.blue * .7, alpha: bodyColor.alpha }); - let solidDarkLength = areaSize - darkenedLength; - let solidLength = solidDarkLength - blackenedLength; - - cr.translate(startX, startY); - - cr.setSourceColor(bodyColor); - cr.newSubPath(); - cr.rectangle.apply(cr, [0, 0].concat(isHorizontalDots ? [solidLength, size] : [size, solidLength])); - cr.fill(); - cr.setSourceColor(blackenedColor); - cr.newSubPath(); - cr.rectangle.apply(cr, isHorizontalDots ? [solidLength, 0, 1, size] : [0, solidLength, size, 1]); - cr.fill(); - cr.setSourceColor(darkenedColor); - cr.newSubPath(); - cr.rectangle.apply(cr, isHorizontalDots ? [solidDarkLength, 0, darkenedLength, size] : [0, solidDarkLength, size, darkenedLength]); - cr.fill(); - } - } else { - let spacing = Math.ceil(areaSize / 18); // separation between the indicators - let length; - let radius; - let dist; - let indicatorSize; - let translate; - let preDraw = () => {}; - let draw; - let drawDash = (i, dashLength) => { - dist = i * dashLength + i * spacing; - cr.rectangle.apply(cr, (isHorizontalDots ? [dist, 0, dashLength, size] : [0, dist, size, dashLength])); - }; - - switch (type) { - // Using the code from Ciliora Round style in pull request https://github.com/home-sweet-gnome/dash-to-panel/pull/1720 - case DOT_STYLE.CILIORA: - spacing = size; - radius = size / 2; - length = areaSize - (size * (n - 1)) - (spacing * (n - 1)); - translate = () => cr.translate(startX, startY); - preDraw = () => { - cr.newSubPath(); - cr.arc.apply(cr, (isHorizontalDots ? [radius, radius] : [radius, radius]).concat([radius, 0, 2 * Math.PI])); - cr.arc.apply(cr, (isHorizontalDots ? [length - radius, radius] : [radius, length - radius]).concat([radius, 0, 2 * Math.PI])); - cr.rectangle.apply(cr, (isHorizontalDots ? [radius, 0, length - size, size] : [0, radius, size, length - size])); - }; - draw = i => { - dist = length + (i * spacing) + ((i - 1) * size) + radius; - cr.arc.apply(cr, (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([radius, 0, 2 * Math.PI])); - }; - break; - case DOT_STYLE.DOTS: - radius = size / 2; - - translate = () => { - indicatorSize = Math.floor((areaSize - n * size - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => { - dist = (2 * i + 1) * radius + i * spacing; - cr.arc.apply(cr, (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([radius, 0, 2 * Math.PI])); - }; - break; - case DOT_STYLE.SQUARES: - translate = () => { - indicatorSize = Math.floor((areaSize - n * size - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => { - dist = i * size + i * spacing; - cr.rectangle.apply(cr, (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size])); - }; - break; - case DOT_STYLE.DASHES: - length = Math.floor(areaSize / 4) - spacing; - translate = () => { - indicatorSize = Math.floor((areaSize - n * length - (n - 1) * spacing) / 2); - cr.translate.apply(cr, isHorizontalDots ? [indicatorSize, startY] : [startX, indicatorSize]); - } - draw = i => drawDash(i, length); - break; - case DOT_STYLE.SEGMENTED: - length = Math.ceil((areaSize - ((n - 1) * spacing)) / n); - translate = () => cr.translate(startX, startY); - draw = i => drawDash(i, length); - break; - } - - translate(); - - cr.setSourceColor(bodyColor); - preDraw(); - for (let i = 0; i < n; i++) { - cr.newSubPath(); - draw(i); - } - cr.fill(); - } - - cr.$dispose(); - } - - _numberOverlay() { - // Add label for a Hot-Key visual aid - this._numberOverlayLabel = new St.Label({ style_class: 'badge' }); - this._numberOverlayBin = new St.Bin({ - child: this._numberOverlayLabel, y: 2 - }); - this._numberOverlayLabel.add_style_class_name('number-overlay'); - this._numberOverlayOrder = -1; - this._numberOverlayBin.hide(); - - this._dtpIconContainer.add_child(this._numberOverlayBin); - } - - updateHotkeyNumberOverlay() { - this.updateNumberOverlay(this._numberOverlayBin, true); - } - - updateNumberOverlay(bin, fixedSize) { - // We apply an overall scale factor that might come from a HiDPI monitor. - // Clutter dimensions are in physical pixels, but CSS measures are in logical - // pixels, so make sure to consider the scale. - // Set the font size to something smaller than the whole icon so it is - // still visible. The border radius is large to make the shape circular - let [minWidth, natWidth] = this._dtpIconContainer.get_preferred_width(-1); - let font_size = Math.round(Math.max(12, 0.2 * natWidth) / Utils.getScaleFactor()); - let size = Math.round(font_size * 1.3); - let label = bin.child; - let style = 'font-size: ' + font_size + 'px;' + - 'border-radius: ' + this.icon.iconSize + 'px;' + - 'height: ' + size +'px;'; - - if (fixedSize) { - style += 'width: ' + size + 'px;'; - } else { - style += 'padding: 0.2em 0.5em;'; - } - - bin.x = 2; - label.set_style(style); - } - - setNumberOverlay(number) { - this._numberOverlayOrder = number; - this._numberOverlayLabel.set_text(number.toString()); - } - - toggleNumberOverlay(activate) { - if (activate && this._numberOverlayOrder > -1) - this._numberOverlayBin.show(); - else - this._numberOverlayBin.hide(); - } - - handleDragOver(source, actor, x, y, time) { - if (source == Main.xdndHandler) { - this._previewMenu.close(true); - } - - return DND.DragMotionResult.CONTINUE; - } - - getAppIconInterestingWindows(isolateMonitors) { - return getInterestingWindows(this.app, this.dtpPanel.monitor, isolateMonitors); - } -}); -TaskbarAppIcon.prototype.scaleAndFade = TaskbarAppIcon.prototype.undoScaleAndFade = () => {}; - -export function minimizeWindow(app, param, monitor){ - // Param true make all app windows minimize - let windows = getInterestingWindows(app, monitor); - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - for (let i = 0; i < windows.length; i++) { - let w = windows[i]; - if (w.get_workspace() == current_workspace && w.showing_on_its_workspace()){ - w.minimize(); - // Just minimize one window. By specification it should be the - // focused window on the current workspace. - if(!param) - break; - } - } -} - -/* - * By default only non minimized windows are activated. - * This activates all windows in the current workspace. - */ -export function activateAllWindows(app, monitor){ - - // First activate first window so workspace is switched if needed, - // then activate all other app windows in the current workspace. - let windows = getInterestingWindows(app, monitor); - let w = windows[0]; - Main.activateWindow(w); - let activeWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index(); - - if (windows.length <= 0) - return; - - for (let i = windows.length - 1; i >= 0; i--){ - if (windows[i].get_workspace().index() == activeWorkspace){ - Main.activateWindow(windows[i]); - } - } -} - -export function activateFirstWindow(app, monitor){ - - let windows = getInterestingWindows(app, monitor); - Main.activateWindow(windows[0]); -} - -export function cycleThroughWindows(app, reversed, shouldMinimize, monitor) { - // Store for a little amount of time last clicked app and its windows - // since the order changes upon window interaction - let MEMORY_TIME=3000; - - let app_windows = getInterestingWindows(app, monitor); - - if(shouldMinimize) - app_windows.push("MINIMIZE"); - - if (recentlyClickedAppLoopId > 0) - GLib.Source.remove(recentlyClickedAppLoopId); - - recentlyClickedAppLoopId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, - MEMORY_TIME, resetRecentlyClickedApp); - - // If there isn't already a list of windows for the current app, - // or the stored list is outdated, use the current windows list. - if (!recentlyClickedApp || - recentlyClickedApp.get_id() != app.get_id() || - recentlyClickedAppWindows.length != app_windows.length || - recentlyClickedAppMonitorIndex != monitor.index) { - recentlyClickedApp = app; - recentlyClickedAppWindows = app_windows; - recentlyClickedAppIndex = 0; - recentlyClickedAppMonitorIndex = monitor.index; - } - - if (reversed) { - recentlyClickedAppIndex--; - if (recentlyClickedAppIndex < 0) recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1; - } else { - recentlyClickedAppIndex++; - } - let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length; - - if(recentlyClickedAppWindows[index] === "MINIMIZE") - minimizeWindow(app, true, monitor); - else - Main.activateWindow(recentlyClickedAppWindows[index]); -} - -export function resetRecentlyClickedApp() { - if (recentlyClickedAppLoopId > 0) - GLib.Source.remove(recentlyClickedAppLoopId); - - recentlyClickedAppLoopId=0; - recentlyClickedApp =null; - recentlyClickedAppWindows = null; - recentlyClickedAppIndex = 0; - recentlyClickedAppMonitorIndex = null; - - return false; -} - -export function closeAllWindows(app, monitor) { - let windows = getInterestingWindows(app, monitor); - for (let i = 0; i < windows.length; i++) - windows[i].delete(global.get_current_time()); -} - -// Filter out unnecessary windows, for instance -// nautilus desktop window. -export function getInterestingWindows(app, monitor, isolateMonitors) { - let windows = ( - app ? - app.get_windows() : - global.get_window_actors().map(wa => wa.get_meta_window()) - ).filter(w => !w.skip_taskbar); - - // When using workspace or monitor isolation, we filter out windows - // that are not in the current workspace or on the same monitor as the appicon - if (SETTINGS.get_boolean('isolate-workspaces')) - windows = windows.filter(function(w) { - return w.get_workspace() && - w.get_workspace() == Utils.getCurrentWorkspace(); - }); - - if (monitor && SETTINGS.get_boolean('multi-monitors') && (isolateMonitors || SETTINGS.get_boolean('isolate-monitors'))) { - windows = windows.filter(function(w) { - return w.get_monitor() == monitor.index; - }); - } - - return windows; -} - -export function cssHexTocssRgba(cssHex, opacity) { - let bigint = parseInt(cssHex.slice(1), 16); - let r = (bigint >> 16) & 255; - let g = (bigint >> 8) & 255; - let b = bigint & 255; - - return 'rgba(' + [r, g, b].join(',') + ',' + opacity + ')'; -} - -export function getIconPadding(monitorIndex) { - let panelSize = PanelSettings.getPanelSize(SETTINGS, monitorIndex); - let padding = Taskbar.APPICON_PADDING; - let availSize = panelSize - Taskbar.MIN_ICON_SIZE - panelSize % 2; - - if (padding * 2 > availSize) { - padding = availSize * .5; - } - - return padding; -} - -/** - * Extend AppMenu (AppIconMenu for pre gnome 41) - * - * - hide 'Show Details' according to setting - * - show windows header only if show-window-previews is disabled - * - Add close windows option based on quitfromdash extension - * (https://github.com/deuill/shell-extension-quitfromdash) - */ - -export class TaskbarSecondaryMenu extends AppMenu.AppMenu { - - constructor(source, side) { - super(source, side); - // constructor parameter does nos work for some reason - this._enableFavorites = true; - this._showSingleWindows = true; - - // replace quit item - delete this._quitItem; - this._quitItem = this.addAction(_('Quit'), () => this._quitFromTaskbar()); - } - - updateQuitText() { - let count = this.sourceActor.window ? 1 : - getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor).length; - - if ( count > 0) { - let quitFromTaskbarMenuText = ""; - if (count == 1) - quitFromTaskbarMenuText = _("Quit"); - else - quitFromTaskbarMenuText = ngettext('Quit %d Window', 'Quit %d Windows', count).format(count); - - this._quitItem.label.set_text(quitFromTaskbarMenuText); - } - } - - _quitFromTaskbar() { - let time = global.get_current_time() - let windows = - this.sourceActor.window ? // ungrouped applications - [this.sourceActor.window] : - getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) - - if (windows.length == this._app.get_windows().length) - this._app.request_quit() - - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - windows.forEach((w) => !!w.get_compositor_private() && w.delete(time++)); - - return GLib.SOURCE_REMOVE; - }); - } -}; - -/** - * This function is used for extendDashItemContainer - */ -export function ItemShowLabel() { - if (!this._labelText) - return; - - this.label.set_text(this._labelText); - this.label.opacity = 0; - this.label.show(); - - let [stageX, stageY] = this.get_transformed_position(); - let node = this.label.get_theme_node(); - - let itemWidth = this.allocation.x2 - this.allocation.x1; - let itemHeight = this.allocation.y2 - this.allocation.y1; - - let labelWidth = this.label.get_width(); - let labelHeight = this.label.get_height(); - - let position = this._dtpPanel.getPosition(); - let labelOffset = node.get_length('-x-offset'); - - let xOffset = Math.floor((itemWidth - labelWidth) / 2); - let x = stageX + xOffset - let y = stageY + (itemHeight - labelHeight) * .5; - - switch(position) { - case St.Side.TOP: - y = stageY + labelOffset + itemHeight; - break; - case St.Side.BOTTOM: - y = stageY - labelHeight - labelOffset; - break; - case St.Side.LEFT: - x = stageX + labelOffset + itemWidth; - break; - case St.Side.RIGHT: - x = stageX - labelWidth - labelOffset; - break; - } - - // keep the label inside the screen border - // Only needed for the x coordinate. - - // Leave a few pixel gap - let gap = LABEL_GAP; - let monitor = Main.layoutManager.findMonitorForActor(this); - if ( x - monitor.x < gap) - x += monitor.x - x + labelOffset; - else if ( x + labelWidth > monitor.x + monitor.width - gap) - x -= x + labelWidth -( monitor.x + monitor.width) + gap; - - this.label.set_position(Math.round(x), Math.round(y)); - - let duration = Dash.DASH_ITEM_LABEL_SHOW_TIME; - - if (duration > 1) { - duration /= 1000; - } - - Utils.animate(this.label, { - opacity: 255, - time: duration, - transition: 'easeOutQuad', - }); -} - -/** - * A wrapper class around the ShowAppsIcon class. - * - * - Pass settings to the constructor - * - set label position based on dash orientation (Note, I am reusing most machinery of the appIcon class) - * - implement a popupMenu based on the AppIcon code (Note, I am reusing most machinery of the appIcon class) - * - * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. - * thus use this pattern where the real showAppsIcon object is encaptulated, and a reference to it will be properly wired upon - * use of this class in place of the original showAppsButton. - * - */ -export const ShowAppsIconWrapper = class extends EventEmitter { - - constructor(dtpPanel) { - super(); - - this.realShowAppsIcon = new Dash.ShowAppsIcon(); - - /* the variable equivalent to toggleButton has a different name in the appIcon class - (actor): duplicate reference to easily reuse appIcon methods */ - this.actor = this.realShowAppsIcon.toggleButton; - this.realShowAppsIcon.show(false); - - // Re-use appIcon methods - this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout; - this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout; - this._onKeyboardPopupMenu = AppDisplay.AppIcon.prototype._onKeyboardPopupMenu; - - // No action on clicked (showing of the appsview is controlled elsewhere) - this._onClicked = (actor, button) => this._removeMenuTimeout(); - - this.actor.connect('leave-event', this._onLeaveEvent.bind(this)); - this.actor.connect('button-press-event', this._onButtonPress.bind(this)); - this.actor.connect('touch-event', this._onTouchEvent.bind(this)); - this.actor.connect('clicked', this._onClicked.bind(this)); - this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this)); - - this._menu = null; - this._menuManager = new PopupMenu.PopupMenuManager(this.actor); - this._menuTimeoutId = 0; - - this.realShowAppsIcon._dtpPanel = dtpPanel; - Taskbar.extendDashItemContainer(this.realShowAppsIcon); - - this.realShowAppsIcon.icon.createIcon = function(size) { - this._iconActor = new St.Icon({ icon_name: 'view-app-grid-symbolic', - icon_size: size, - style_class: 'show-apps-icon', - track_hover: true }); - - return this._iconActor; - }; - - this.setShowAppsPadding(); - } - - _onButtonPress(_actor, event) { - let button = event.get_button(); - if (button == 1) { - this._setPopupTimeout(); - } else if (button == 3) { - this.popupMenu(); - return Clutter.EVENT_STOP; - } - return Clutter.EVENT_PROPAGATE; - } - - _onLeaveEvent(_actor, _event) { - this.actor.fake_release(); - this._removeMenuTimeout(); - } - - _onTouchEvent(actor, event) { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) - this._setPopupTimeout(); - - return Clutter.EVENT_PROPAGATE; - } - - _onMenuPoppedDown() { - this._menu.sourceActor = this.actor; - this.actor.sync_hover(); - this.emit('menu-state-changed', false); - } - - setShowAppsPadding() { - let padding = getIconPadding(this.realShowAppsIcon._dtpPanel.monitor.index); - - this.actor.set_style('padding:' + padding + 'px;'); - } - - createMenu() { - if (!this._menu) { - this._menu = new MyShowAppsIconMenu(this.realShowAppsIcon, this.realShowAppsIcon._dtpPanel); - this._menu.connect('open-state-changed', (menu, isPoppedUp) => { - if (!isPoppedUp) - this._onMenuPoppedDown(); - }); - let id = Main.overview.connect('hiding', () => { - this._menu.close(); - }); - this._menu.actor.connect('destroy', () => { - Main.overview.disconnect(id); - }); - - // We want to keep the item hovered while the menu is up - this._menu.blockSourceEvents = true; - - Main.uiGroup.add_child(this._menu.actor); - this._menuManager.addMenu(this._menu); - } - } - - popupMenu(sourceActor = null) { - this._removeMenuTimeout(); - this.actor.fake_release(); - this.createMenu(); - - this._menu.updateItems(sourceActor == null ? this.realShowAppsIcon : sourceActor); - - this.actor.set_hover(true); - this._menu.open(BoxPointer.PopupAnimation.FULL); - this._menuManager.ignoreRelease(); - this.emit('sync-tooltip'); - - return false; - } - - shouldShowTooltip() { - return SETTINGS.get_boolean('show-tooltip') && - (this.actor.hover && (!this._menu || !this._menu.isOpen)); - } - - destroy() { - this.realShowAppsIcon.destroy(); - } -}; - -/** - * A menu for the showAppsIcon - */ -export const MyShowAppsIconMenu = class extends PopupMenu.PopupMenu { - - constructor(actor, dtpPanel) { - super(actor, 0, dtpPanel.getPosition()); - - this._dtpPanel = dtpPanel; - - this.updateItems(actor); - } - - updateItems(sourceActor) { - this.sourceActor = sourceActor; - - this.removeAll(); - - this._appendItem({ - title: _('System Monitor'), - cmd: ['gnome-system-monitor'] - }); - - this._appendItem({ - title: _('Files'), - cmd: ['nautilus'] - }); - - this._appendItem({ - title: _('Settings'), - cmd: ['gnome-control-center'] - }); - - this._appendItem({ - title: _('Zorin Appearance'), - cmd: ['zorin-appearance'] - }); - - this._appendList( - SETTINGS.get_strv('panel-context-menu-commands'), - SETTINGS.get_strv('panel-context-menu-titles') - ) - - this._appendSeparator(); - - let lockTaskbarMenuItem = this._appendMenuItem(SETTINGS.get_boolean('taskbar-locked') ? _('Unlock taskbar') : _('Lock taskbar')); - lockTaskbarMenuItem.connect('activate', () => { - SETTINGS.set_boolean('taskbar-locked', !SETTINGS.get_boolean('taskbar-locked')); - }); - - let settingsMenuItem = this._appendMenuItem(_('Taskbar Settings')); - settingsMenuItem.connect('activate', () => DTP_EXTENSION.openPreferences()) - - if(this.sourceActor == Main.layoutManager.dummyCursor) { - this._appendSeparator(); - let item = this._appendMenuItem(this._dtpPanel._restoreWindowList ? _('Restore Windows') : _('Show Desktop')); - item.connect('activate', this._dtpPanel._onShowDesktopButtonPress.bind(this._dtpPanel)); - } - } - - - // Only add menu entries for commands that exist in path - _appendItem(info) { - if (GLib.find_program_in_path(info.cmd[0])) { - let item = this._appendMenuItem(_(info.title)); - - item.connect('activate', function() { - print("activated: " + info.title); - Util.spawn(info.cmd); - }); - return item; - } - - return null; - } - - _appendList(commandList, titleList) { - if (commandList.length != titleList.length) { - return; - } - - for (let entry = 0; entry < commandList.length; entry++) { - this._appendItem({ - title: titleList[entry], - cmd: commandList[entry].split(' ') - }); - } - } - - _appendSeparator() { - let separator = new PopupMenu.PopupSeparatorMenuItem(); - this.addMenuItem(separator); - } - - _appendMenuItem(labelText) { - // FIXME: app-well-menu-item style - let item = new PopupMenu.PopupMenuItem(labelText); - this.addMenuItem(item); - return item; - } -}; - - -export const getIconContainerStyle = function(isVertical) { - let style = 'padding: '; - - if (SETTINGS.get_boolean('group-apps')) { - style += (isVertical ? '0;' : '0 ' + DEFAULT_PADDING_SIZE + 'px;'); - } else { - style += (isVertical ? '' : '0 ') + DEFAULT_PADDING_SIZE + 'px;'; - } - - return style; -} diff --git a/debian/changelog b/debian/changelog index 7ee9cf7..50a41a3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +gnome-shell-extension-zorin-taskbar (68.1) noble; urgency=medium + + * Adjusted app icon margins and padding + + -- Artyom Zorin Fri, 02 May 2025 14:19:18 +0100 + +gnome-shell-extension-zorin-taskbar (68) noble; urgency=medium + + * Re-based on upstream version 68 + + -- Artyom Zorin Tue, 29 Apr 2025 20:36:02 +0100 + +gnome-shell-extension-zorin-taskbar (65.3) noble; urgency=medium + + * Changed activities button default position + + -- Artyom Zorin Sun, 02 Mar 2025 19:39:09 +0000 + gnome-shell-extension-zorin-taskbar (65.2) noble; urgency=medium * Separated floating rounded theme from intellihide as an independent diff --git a/desktopIconsIntegration.js b/desktopIconsIntegration.js deleted file mode 100644 index e403516..0000000 --- a/desktopIconsIntegration.js +++ /dev/null @@ -1,165 +0,0 @@ -/* - * The code in this file is distributed under a "1-clause BSD license", - * which makes it compatible with GPLv2 and GPLv3 too, and others. - * - * License text: - * - * Copyright (C) 2021 Sergio Costas (rastersoft@gmail.com) - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/******************************************************************************* - * Integration class - * - * This class must be added to other extensions in order to integrate - * them with Desktop Icons NG. It allows an extension to notify how much margin - * it uses in each side of each monitor. - * - * DON'T SEND PATCHES TO THIS FILE TO THE EXTENSION MAINTAINER. SEND THEM TO - * DESKTOP ICONS NG MAINTAINER: https://gitlab.com/rastersoft/desktop-icons-ng - * - * In the *enable()* function, create a *DesktopIconsUsableAreaClass()* - * object with - * - * new DesktopIconsIntegration.DesktopIconsUsableAreaClass(object); - * - * Now, in the *disable()* function just call to the *destroy()* method before - * nullifying the pointer. You must create a new object in enable() the next - * time the extension is enabled. - * - * In your code, every time you change the margins, you should call first to - * *resetMargins()* method to clear the current margins, and then call to - * *setMargins(...)* method as many times as you need to set the margins in each - * monitor. You don't need to call it for all the monitors, only for those where - * you are painting something. If you don't set values for a monitor, they will - * be considered zero. - * - * The margins values are relative to the monitor border. - * - *******************************************************************************/ - -import GLib from 'gi://GLib'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js'; -import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; - -const IDENTIFIER_UUID = "130cbc66-235c-4bd6-8571-98d2d8bba5e2"; - -export class DesktopIconsUsableAreaClass { - _checkIfExtensionIsEnabled(extension) { - return (extension?.state === ExtensionUtils.ExtensionState.ENABLED) || - (extension?.state === ExtensionUtils.ExtensionState.ACTIVE); - } - - constructor() { - const Me = Extension.lookupByURL(import.meta.url); - this._UUID = Me.uuid; - this._extensionManager = Main.extensionManager; - this._timedMarginsID = 0; - this._margins = {}; - this._emID = this._extensionManager.connect('extension-state-changed', (_obj, extension) => { - if (!extension) - return; - - // If an extension is being enabled and lacks the DesktopIconsUsableArea object, we can avoid launching a refresh - if (this._checkIfExtensionIsEnabled(extension)) { - this._sendMarginsToExtension(extension); - return; - } - // if the extension is being disabled, we must do a full refresh, because if there were other extensions originally - // loaded after that extension, those extensions will be disabled and enabled again without notification - this._changedMargins(); - }); - } - - /** - * Sets or updates the top, bottom, left and right margins for a - * monitor. Values are measured from the monitor border (and NOT from - * the workspace border). - * - * @param {int} monitor Monitor number to which set the margins. - * A negative value means "the primary monitor". - * @param {int} top Top margin in pixels - * @param {int} bottom Bottom margin in pixels - * @param {int} left Left margin in pixels - * @param {int} right Right margin in pixels - */ - setMargins(monitor, top, bottom, left, right) { - this._margins[monitor] = { - 'top': top, - 'bottom': bottom, - 'left': left, - 'right': right - }; - this._changedMargins(); - } - - /** - * Clears the current margins. Must be called before configuring the monitors - * margins with setMargins(). - */ - resetMargins() { - this._margins = {}; - this._changedMargins(); - } - - /** - * Disconnects all the signals and removes the margins. - */ - destroy() { - if (this._emID) { - this._extensionManager.disconnect(this._emID); - this._emID = 0; - } - if (this._timedMarginsID) { - GLib.source_remove(this._timedMarginsID); - this._timedMarginsID = 0; - } - this._margins = null; - this._changedMargins(); - } - - _changedMargins() { - if (this._timedMarginsID) { - GLib.source_remove(this._timedMarginsID); - } - this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, ()=> { - this._sendMarginsToAll(); - this._timedMarginsID = 0; - return GLib.SOURCE_REMOVE; - }); - } - - _sendMarginsToAll() { - this._extensionManager.getUuids().forEach(uuid => - this._sendMarginsToExtension(this._extensionManager.lookup(uuid))); - } - - _sendMarginsToExtension(extension) { - // check that the extension is an extension that has the logic to accept - // working margins - if (!this._checkIfExtensionIsEnabled(extension)) - return; - - const usableArea = extension?.stateObj?.DesktopIconsUsableArea; - if (usableArea?.uuid === IDENTIFIER_UUID) - usableArea.setMarginsForExtension(this._UUID, this._margins); - } -} diff --git a/extension.js b/extension.js deleted file mode 100644 index 9cf6ed9..0000000 --- a/extension.js +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - - -import Gio from 'gi://Gio'; - -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; -import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; - -import * as PanelManager from './panelManager.js'; -import * as AppIcons from './appIcons.js'; - - -const ZORIN_DASH_UUID = 'zorin-dash@zorinos.com'; - -let panelManager; -let extensionChangedHandler; -let startupCompleteHandler; -let disabledZorinDash; -let extensionSystem = Main.extensionManager; - -export let DTP_EXTENSION = null; -export let SETTINGS = null; -export let DESKTOPSETTINGS = null; -export let TERMINALSETTINGS = null; -export let PERSISTENTSTORAGE = null; -export let EXTENSION_UUID = null; -export let EXTENSION_PATH = null; - -export default class ZorinTaskbarExtension extends Extension { - constructor(metadata) { - super(metadata); - - this._realHasOverview = Main.sessionMode.hasOverview; - - //create an object that persists until gnome-shell is restarted, even if the extension is disabled - PERSISTENTSTORAGE = {}; - } - - enable() { - DTP_EXTENSION = this; - - // The Zorin Dash extension might get enabled after this extension - extensionChangedHandler = extensionSystem.connect('extension-state-changed', (data, extension) => { - if (extension.uuid === ZORIN_DASH_UUID && extension.state === 1) { - _enable(this); - } - }); - - //create a global object that can emit signals and conveniently expose functionalities to other extensions - global.zorinTaskbar = new EventEmitter(); - - _enable(this); - } - - disable(reset = false) { - panelManager.disable(); - - DTP_EXTENSION = null; - SETTINGS = null; - DESKTOPSETTINGS = null; - TERMINALSETTINGS = null; - panelManager = null; - - if (!reset) { - extensionSystem.disconnect(extensionChangedHandler); - - if (disabledZorinDash) { - disabledZorinDash = false; - extensionSystem.enableExtension(ZORIN_DASH_UUID); - } - - delete global.zorinTaskbar; - - AppIcons.resetRecentlyClickedApp(); - } - - if (startupCompleteHandler) { - Main.layoutManager.disconnect(startupCompleteHandler); - startupCompleteHandler = null; - } - - Main.sessionMode.hasOverview = this._realHasOverview; - } -} - -function _enable(extension) { - let enabled = global.settings.get_strv('enabled-extensions'); - - if (enabled?.indexOf(ZORIN_DASH_UUID) >= 0) { - disabledZorinDash = true; - extensionSystem.disableExtension(ZORIN_DASH_UUID); - } - - if (panelManager) - return panelManager.toggleDash(); // already initialized but Zorin Dash restored the original dash on disable - - SETTINGS = extension.getSettings('org.gnome.shell.extensions.zorin-taskbar'); - DESKTOPSETTINGS = new Gio.Settings({schema_id: 'org.gnome.desktop.interface'}); - TERMINALSETTINGS = new Gio.Settings({schema_id: 'org.gnome.desktop.default-applications.terminal'}) - EXTENSION_UUID = extension.uuid - EXTENSION_PATH = extension.path - - Main.layoutManager.startInOverview = false; - - if (Main.layoutManager._startingUp) { - Main.sessionMode.hasOverview = false; - startupCompleteHandler = Main.layoutManager.connect('startup-complete', () => { - Main.sessionMode.hasOverview = extension._realHasOverview - }); - } - - panelManager = new PanelManager.PanelManager(); - - panelManager.enable(); -} diff --git a/intellihide.js b/intellihide.js deleted file mode 100644 index f8cdb0f..0000000 --- a/intellihide.js +++ /dev/null @@ -1,434 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Panel extension - */ - -import Clutter from 'gi://Clutter'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as GrabHelper from 'resource:///org/gnome/shell/ui/grabHelper.js'; -import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; -import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js'; - -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; - -var INTELLIHIDE_PRESSURE_THRESHOLD = 100; -var INTELLIHIDE_PRESSURE_TIME = 1000; -var INTELLIHIDE_ANIMATION_TIME = 200; -var INTELLIHIDE_CLOSE_DELAY = 400; -var INTELLIHIDE_ENABLE_START_DELAY = 2000; - -//timeout intervals -const CHECK_POINTER_MS = 200; -const CHECK_GRAB_MS = 400; -const POST_ANIMATE_MS = 50; -const MIN_UPDATE_MS = 250; - -//timeout names -const T1 = 'checkGrabTimeout'; -const T2 = 'limitUpdateTimeout'; -const T3 = 'postAnimateTimeout'; - -const SIDE_CONTROLS_ANIMATION_TIME = OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / (OverviewControls.SIDE_CONTROLS_ANIMATION_TIME > 1 ? 1000 : 1); - -export const Hold = { - NONE: 0, - TEMPORARY: 1, - PERMANENT: 2 -}; - -export const Intellihide = class { - - constructor(dtpPanel) { - this._dtpPanel = dtpPanel; - this._panelBox = dtpPanel.panelBox; - this._panelManager = dtpPanel.panelManager; - this._proximityManager = this._panelManager.proximityManager; - this._holdStatus = Hold.NONE; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); - - this._intellihideChangedId = SETTINGS.connect('changed::intellihide', () => this._changeEnabledStatus()); - this._intellihideOnlySecondaryChangedId = SETTINGS.connect('changed::intellihide-only-secondary', () => this._changeEnabledStatus()); - - this.enabled = false; - this._changeEnabledStatus(); - } - - enable() { - this.enabled = true; - this._monitor = this._dtpPanel.monitor; - this._animationDestination = -1; - this._pendingUpdate = false; - this._hoveredOut = false; - this._windowOverlap = false; - this._translationProp = 'translation_' + (this._dtpPanel.checkIfVertical() ? 'x' : 'y'); - - this._panelBox.translation_y = 0; - this._panelBox.translation_x = 0; - - this._setTrackPanel(true); - this._bindGeneralSignals(); - - if (SETTINGS.get_boolean('intellihide-hide-from-windows')) { - this._proximityWatchId = this._proximityManager.createWatch( - this._panelBox.get_parent(), - this._dtpPanel.monitor.index, - Proximity.Mode[SETTINGS.get_string('intellihide-behaviour')], - 0, 0, - overlap => { - this._windowOverlap = overlap; - this._queueUpdatePanelPosition(); - } - ); - } - - this._setRevealMechanism(); - this._queueUpdatePanelPosition(); - } - - disable(reset) { - if (this._proximityWatchId) { - this._proximityManager.removeWatch(this._proximityWatchId); - } - - this._setTrackPanel(false); - - this._signalsHandler.destroy(); - this._timeoutsHandler.destroy(); - - this._removeRevealMechanism(); - - this._revealPanel(!reset); - - this.enabled = false; - } - - destroy() { - SETTINGS.disconnect(this._intellihideChangedId); - SETTINGS.disconnect(this._intellihideOnlySecondaryChangedId); - - if (this.enabled) { - this.disable(); - } - } - - toggle() { - this[this._holdStatus & Hold.PERMANENT ? 'release' : 'revealAndHold'](Hold.PERMANENT); - } - - revealAndHold(holdStatus) { - if (this.enabled && !this._holdStatus) { - this._revealPanel(); - } - - this._holdStatus |= holdStatus; - } - - release(holdStatus) { - this._holdStatus -= holdStatus; - - if (this.enabled && !this._holdStatus) { - this._queueUpdatePanelPosition(); - } - } - - reset() { - this.disable(true); - this.enable(); - } - - _changeEnabledStatus() { - let intellihide = SETTINGS.get_boolean('intellihide'); - let onlySecondary = SETTINGS.get_boolean('intellihide-only-secondary'); - let enabled = intellihide && !(this._dtpPanel.isPrimary && onlySecondary); - - if (this.enabled !== enabled) { - this[enabled ? 'enable' : 'disable'](); - } - } - - _bindGeneralSignals() { - this._signalsHandler.add( - [ - this._dtpPanel.taskbar, - ['menu-closed', 'end-drag'], - () => { - this._panelBox.sync_hover(); - this._onHoverChanged(); - } - ], - [ - SETTINGS, - [ - 'changed::intellihide-use-pressure', - 'changed::intellihide-hide-from-windows', - 'changed::intellihide-behaviour' - ], - () => this.reset() - ], - [ - this._panelBox, - 'notify::hover', - () => this._onHoverChanged() - ], - [ - this._dtpPanel.taskbar.previewMenu, - 'open-state-changed', - () => this._queueUpdatePanelPosition() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._queueUpdatePanelPosition() - ] - ); - - if (Meta.is_wayland_compositor()) { - this._signalsHandler.add([ - this._panelBox, - 'notify::visible', - () => Utils.setDisplayUnredirect(!this._panelBox.visible) - ]); - } - } - - _onHoverChanged() { - this._hoveredOut = !this._panelBox.hover; - this._queueUpdatePanelPosition(); - } - - _setTrackPanel(enable) { - let actorData = Utils.getTrackedActorData(this._panelBox) - - actorData.affectsStruts = !enable; - actorData.trackFullscreen = !enable; - - this._panelBox.track_hover = enable; - this._panelBox.reactive = enable; - this._panelBox.visible = enable ? enable : this._panelBox.visible; - - Main.layoutManager._queueUpdateRegions(); - } - - _setRevealMechanism() { - let barriers = Meta.BackendCapabilities.BARRIERS - - if ((global.backend.capabilities & barriers) === barriers && SETTINGS.get_boolean('intellihide-use-pressure')) { - this._edgeBarrier = this._createBarrier(); - this._pressureBarrier = new Layout.PressureBarrier( - INTELLIHIDE_PRESSURE_THRESHOLD, - INTELLIHIDE_PRESSURE_TIME, - Shell.ActionMode.NORMAL - ); - this._pressureBarrier.addBarrier(this._edgeBarrier); - this._signalsHandler.add([this._pressureBarrier, 'trigger', () => this._queueUpdatePanelPosition(true)]); - } else { - this._pointerWatch = PointerWatcher.getPointerWatcher() - .addWatch(CHECK_POINTER_MS, (x, y) => this._checkMousePointer(x, y)); - } - } - - _removeRevealMechanism() { - if (this._pointerWatch) { - PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch); - } - - if (this._pressureBarrier) { - this._pressureBarrier.destroy(); - this._edgeBarrier.destroy(); - - this._pressureBarrier = 0; - } - } - - _createBarrier() { - let position = this._dtpPanel.geom.position; - let opts = { backend: global.backend }; - - if (this._dtpPanel.checkIfVertical()) { - opts.y1 = this._monitor.y; - opts.y2 = this._monitor.y + this._monitor.height; - opts.x1 = opts.x2 = this._monitor.x; - } else { - opts.x1 = this._monitor.x; - opts.x2 = this._monitor.x + this._monitor.width; - opts.y1 = opts.y2 = this._monitor.y; - } - - if (position == St.Side.TOP) { - opts.directions = Meta.BarrierDirection.POSITIVE_Y; - } else if (position == St.Side.BOTTOM) { - opts.y1 = opts.y2 = opts.y1 + this._monitor.height; - opts.directions = Meta.BarrierDirection.NEGATIVE_Y; - } else if (position == St.Side.LEFT) { - opts.directions = Meta.BarrierDirection.POSITIVE_X; - } else { - opts.x1 = opts.x2 = opts.x1 + this._monitor.width; - opts.directions = Meta.BarrierDirection.NEGATIVE_X; - } - - return new Meta.Barrier(opts); - } - - _checkMousePointer(x, y) { - let position = this._dtpPanel.geom.position; - - if (!this._panelBox.hover && !Main.overview.visible && - ((position == St.Side.TOP && y <= this._monitor.y + 1) || - (position == St.Side.BOTTOM && y >= this._monitor.y + this._monitor.height - 1) || - (position == St.Side.LEFT && x <= this._monitor.x + 1) || - (position == St.Side.RIGHT && x >= this._monitor.x + this._monitor.width - 1)) && - ((x >= this._monitor.x && x < this._monitor.x + this._monitor.width) && - (y >= this._monitor.y && y < this._monitor.y + this._monitor.height))) { - this._queueUpdatePanelPosition(true); - } - } - - _queueUpdatePanelPosition(fromRevealMechanism) { - if (!fromRevealMechanism && this._timeoutsHandler.getId(T2) && !Main.overview.visible) { - //unless this is a mouse interaction or entering/leaving the overview, limit the number - //of updates, but remember to update again when the limit timeout is reached - this._pendingUpdate = true; - } else if (!this._holdStatus) { - this._checkIfShouldBeVisible(fromRevealMechanism) ? this._revealPanel() : this._hidePanel(); - this._timeoutsHandler.add([T2, MIN_UPDATE_MS, () => this._endLimitUpdate()]); - } - } - - _endLimitUpdate() { - if (this._pendingUpdate) { - this._pendingUpdate = false; - this._queueUpdatePanelPosition(); - } - } - - _checkIfShouldBeVisible(fromRevealMechanism) { - if (Main.overview.visibleTarget || this._dtpPanel.taskbar.previewMenu.opened || - this._dtpPanel.taskbar._dragMonitor || this._panelBox.get_hover() || this._checkIfGrab()) { - return true; - } - - if (fromRevealMechanism) { - let mouseBtnIsPressed = global.get_pointer()[2] & Clutter.ModifierType.BUTTON1_MASK; - - //the user is trying to reveal the panel - if (this._monitor.inFullscreen && !mouseBtnIsPressed) { - return SETTINGS.get_boolean('intellihide-show-in-fullscreen'); - } - - return !mouseBtnIsPressed; - } - - if (!SETTINGS.get_boolean('intellihide-hide-from-windows')) { - return this._panelBox.hover; - } - - return !this._windowOverlap; - } - - _checkIfGrab() { - let isGrab - - if (GrabHelper._grabHelperStack) - // gnome-shell < 42 - isGrab = GrabHelper._grabHelperStack.some(gh => gh._owner == this._dtpPanel.panel) - else if (global.stage.get_grab_actor) { - // gnome-shell >= 42 - let grabActor = global.stage.get_grab_actor() - let sourceActor = grabActor?._sourceActor || grabActor - - isGrab = sourceActor && - (sourceActor == Main.layoutManager.dummyCursor || - this._dtpPanel.statusArea.quickSettings?.menu.actor.contains(sourceActor) || - this._dtpPanel.panel.contains(sourceActor)) - } - - if (isGrab) - //there currently is a grab on a child of the panel, check again soon to catch its release - this._timeoutsHandler.add([T1, CHECK_GRAB_MS, () => this._queueUpdatePanelPosition()]); - - return isGrab; - } - - _revealPanel(immediate) { - if (!this._panelBox.visible) { - this._panelBox.visible = true; - this._dtpPanel.taskbar._shownInitially = false; - } - - this._animatePanel(0, immediate); - } - - _hidePanel(immediate) { - let position = this._dtpPanel.geom.position; - let size = this._panelBox[position == St.Side.LEFT || position == St.Side.RIGHT ? 'width' : 'height']; - let coefficient = position == St.Side.TOP || position == St.Side.LEFT ? -1 : 1; - - this._animatePanel(size * coefficient, immediate); - } - - _animatePanel(destination, immediate) { - let animating = Utils.isAnimating(this._panelBox, this._translationProp); - - if (!((animating && destination === this._animationDestination) || - (!animating && destination === this._panelBox[this._translationProp]))) { - //the panel isn't already at, or animating to the asked destination - if (animating) { - Utils.stopAnimations(this._panelBox); - } - - this._animationDestination = destination; - - if (immediate) { - this._panelBox[this._translationProp] = destination; - this._panelBox.visible = !destination; - } else { - let tweenOpts = { - //when entering/leaving the overview, use its animation time instead of the one from the settings - time: Main.overview.visible ? - SIDE_CONTROLS_ANIMATION_TIME : - INTELLIHIDE_ANIMATION_TIME * 0.001, - //only delay the animation when hiding the panel after the user hovered out - delay: destination != 0 && this._hoveredOut ? INTELLIHIDE_CLOSE_DELAY * 0.001 : 0, - transition: 'easeOutQuad', - onComplete: () => { - this._panelBox.visible = !destination; - Main.layoutManager._queueUpdateRegions(); - this._timeoutsHandler.add([T3, POST_ANIMATE_MS, () => this._queueUpdatePanelPosition()]); - } - }; - - tweenOpts[this._translationProp] = destination; - Utils.animate(this._panelBox, tweenOpts); - } - } - - this._hoveredOut = false; - } -} diff --git a/metadata.json b/metadata.json index 0eef43e..358e6d7 100644 --- a/metadata.json +++ b/metadata.json @@ -3,7 +3,7 @@ "uuid": "zorin-taskbar@zorinos.com", "name": "Zorin Taskbar", "description": "A taskbar extension for the Zorin OS desktop.", - "shell-version": [ "46", "47" ], + "shell-version": [ "46", "47", "48" ], "gettext-domain": "zorin-taskbar", - "version": 65 + "version": 68 } diff --git a/overview.js b/overview.js deleted file mode 100644 index 2e3c20d..0000000 --- a/overview.js +++ /dev/null @@ -1,455 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * Some code was also adapted from the upstream Gnome Shell source code. - */ - -import * as Intellihide from './intellihide.js'; -import * as Utils from './utils.js'; - -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js'; -import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js'; -import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; -import {SETTINGS} from './extension.js'; - -const GS_HOTKEYS_KEY = 'switch-to-application-'; -const OVERLAY_TIMEOUT = 750; -const SHORTCUT_TIMEOUT = 2000; - -// When the dash is shown, workspace window preview bottom labels go over it (default -// gnome-shell behavior), but when the extension hides the dash, leave some space -// so those labels don't go over a bottom panel -const LABEL_MARGIN = 60; - -//timeout names -const T1 = 'swipeEndTimeout'; -const T2 = 'numberOverlayTimeout'; - -export const Overview = class { - - constructor() { - this._injectionManager = new InjectionManager(); - this._numHotkeys = 10; - } - - enable (primaryPanel) { - this._panel = primaryPanel; - this.taskbar = primaryPanel.taskbar; - - this._injectionsHandler = new Utils.InjectionsHandler(); - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); - - this._optionalWorkspaceIsolation(); - this._optionalHotKeys(); - this._optionalNumberOverlay(); - - this.toggleDash(); - this._adaptAlloc(); - - this._signalsHandler.add([ - SETTINGS, - [ - 'changed::panel-sizes' - ], - () => this.toggleDash() - ]); - } - - disable() { - this._signalsHandler.destroy(); - this._injectionsHandler.destroy(); - this._timeoutsHandler.destroy(); - this._injectionManager.clear(); - - this.toggleDash(true); - - // Remove key bindings - this._disableHotKeys(); - this._disableExtraShortcut(); - } - - toggleDash(visible) { - if (visible === undefined) { - visible = false; - } - - let visibilityFunc = visible ? 'show' : 'hide'; - let height = visible ? -1 : LABEL_MARGIN * Utils.getScaleFactor(); - let overviewControls = Main.overview._overview._controls; - - overviewControls.dash[visibilityFunc](); - overviewControls.dash.set_height(height); - } - - _adaptAlloc() { - let overviewControls = Main.overview._overview._controls - - this._injectionManager.overrideMethod(Object.getPrototypeOf(overviewControls), 'vfunc_allocate', - (originalAllocate) => - (box) => { - let focusedPanel = this._panel.panelManager.focusedMonitorPanel - - if (focusedPanel) { - let position = focusedPanel.geom.position - let isBottom = position == St.Side.BOTTOM - - if (focusedPanel.intellihide?.enabled) { - // Panel intellihide is enabled (struts aren't taken into account on overview allocation), - // dynamically modify the overview box to follow the reveal/hide animation - let { transitioning, finalState, progress } = overviewControls._stateAdjustment.getStateTransitionParams() - let size = focusedPanel.geom[focusedPanel.checkIfVertical() ? 'w' : 'h'] * - (transitioning ? Math.abs((finalState != 0 ? 0 : 1) - progress) : 1) - - if (isBottom || position == St.Side.RIGHT) - box[focusedPanel.fixedCoord.c2] -= size - else - box[focusedPanel.fixedCoord.c1] += size - } else if (isBottom) - // The default overview allocation is very good and takes into account external - // struts, everywhere but the bottom where the dash is usually fixed anyway. - // If there is a bottom panel under the dash location, give it some space here - box.y2 -= focusedPanel.geom.h - } - - originalAllocate.call(overviewControls, box) - } - ); - } - - /** - * Isolate overview to open new windows for inactive apps - */ - _optionalWorkspaceIsolation() { - let label = 'optionalWorkspaceIsolation'; - - let enable = () => { - this._injectionsHandler.removeWithLabel(label); - - this._injectionsHandler.addWithLabel(label, [ - Shell.App.prototype, - 'activate', - IsolatedOverview - ]); - - this._signalsHandler.removeWithLabel(label); - - this._signalsHandler.addWithLabel(label, [ - global.window_manager, - 'switch-workspace', - () => this._panel.panelManager.allPanels.forEach(p => p.taskbar.handleIsolatedWorkspaceSwitch()) - ]); - } - - let disable = () => { - this._signalsHandler.removeWithLabel(label); - this._injectionsHandler.removeWithLabel(label); - } - - function IsolatedOverview() { - // These lines take care of Nautilus for icons on Desktop - let activeWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = this.get_windows().filter(w => w.get_workspace().index() == activeWorkspace.index()); - - if (windows.length > 0 && - (!(windows.length == 1 && windows[0].skip_taskbar) || - this.is_on_workspace(activeWorkspace))) - return Main.activateWindow(windows[0]); - - return this.open_new_window(-1); - } - - this._signalsHandler.add([ - SETTINGS, - 'changed::isolate-workspaces', - () => { - this._panel.panelManager.allPanels.forEach(p => p.taskbar.resetAppIcons()); - - if (SETTINGS.get_boolean('isolate-workspaces')) - enable(); - else - disable(); - } - ]); - - if (SETTINGS.get_boolean('isolate-workspaces')) - enable(); - } - - // Hotkeys - _activateApp(appIndex, modifiers) { - let seenApps = {}; - let apps = []; - - this.taskbar._getAppIcons().forEach(appIcon => { - if (!seenApps[appIcon.app] || this.taskbar.allowSplitApps) { - apps.push(appIcon); - } - - seenApps[appIcon.app] = (seenApps[appIcon.app] || 0) + 1; - }); - - this._showOverlay(); - - if (appIndex < apps.length) { - let appIcon = apps[appIndex]; - let seenAppCount = seenApps[appIcon.app]; - let windowCount = appIcon.window || appIcon._hotkeysCycle ? seenAppCount : appIcon._nWindows; - - if (SETTINGS.get_boolean('shortcut-previews') && windowCount > 1 && - !(modifiers & ~(Clutter.ModifierType.MOD1_MASK | Clutter.ModifierType.SUPER_MASK))) { //ignore the alt (MOD1_MASK) and super key (SUPER_MASK) - if (this._hotkeyPreviewCycleInfo && this._hotkeyPreviewCycleInfo.appIcon != appIcon) { - this._endHotkeyPreviewCycle(); - } - - if (!this._hotkeyPreviewCycleInfo) { - this._hotkeyPreviewCycleInfo = { - appIcon: appIcon, - currentWindow: appIcon.window, - keyFocusOutId: appIcon.connect('key-focus-out', () => appIcon.grab_key_focus()), - capturedEventId: global.stage.connect('captured-event', (actor, e) => { - if (e.type() == Clutter.EventType.KEY_RELEASE && e.get_key_symbol() == (Clutter.KEY_Super_L || Clutter.Super_L)) { - this._endHotkeyPreviewCycle(true); - } - - return Clutter.EVENT_PROPAGATE; - }) - }; - - appIcon._hotkeysCycle = appIcon.window; - appIcon.window = null; - appIcon._previewMenu.open(appIcon, true); - appIcon.grab_key_focus(); - } - - appIcon._previewMenu.focusNext(); - } else { - // Activate with button = 1, i.e. same as left click - let button = 1; - this._endHotkeyPreviewCycle(); - appIcon.activate(button, modifiers, !this.taskbar.allowSplitApps); - } - } - } - - _endHotkeyPreviewCycle(focusWindow) { - if (this._hotkeyPreviewCycleInfo) { - global.stage.disconnect(this._hotkeyPreviewCycleInfo.capturedEventId); - this._hotkeyPreviewCycleInfo.appIcon.disconnect(this._hotkeyPreviewCycleInfo.keyFocusOutId); - - if (focusWindow) { - this._hotkeyPreviewCycleInfo.appIcon._previewMenu.activateFocused(); - } else - this._hotkeyPreviewCycleInfo.appIcon._previewMenu.close() - - this._hotkeyPreviewCycleInfo.appIcon.window = this._hotkeyPreviewCycleInfo.currentWindow; - delete this._hotkeyPreviewCycleInfo.appIcon._hotkeysCycle; - this._hotkeyPreviewCycleInfo = 0; - } - } - - _optionalHotKeys() { - this._hotKeysEnabled = false; - if (SETTINGS.get_boolean('hot-keys')) - this._enableHotKeys(); - - this._signalsHandler.add([ - SETTINGS, - 'changed::hot-keys', - () => { - if (SETTINGS.get_boolean('hot-keys')) - this._enableHotKeys(); - else - this._disableHotKeys(); - } - ]); - } - - _resetHotkeys() { - this._disableHotKeys(); - this._enableHotKeys(); - } - - _enableHotKeys() { - if (this._hotKeysEnabled) - return; - - //3.32 introduced app hotkeys, disable them to prevent conflicts - if (Main.wm._switchToApplication) { - for (let i = 1; i < 10; ++i) { - Utils.removeKeybinding(GS_HOTKEYS_KEY + i); - } - } - - // Setup keyboard bindings for taskbar elements - let shortcutNumKeys = SETTINGS.get_string('shortcut-num-keys'); - let bothNumKeys = shortcutNumKeys == 'BOTH'; - let keys = []; - let prefixModifiers = Clutter.ModifierType.SUPER_MASK - - if (SETTINGS.get_string('hotkey-prefix-text') == 'SuperAlt') - prefixModifiers |= Clutter.ModifierType.MOD1_MASK - - if (bothNumKeys || shortcutNumKeys == 'NUM_ROW') { - keys.push('app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'); // Regular numbers - } - - if (bothNumKeys || shortcutNumKeys == 'NUM_KEYPAD') { - keys.push('app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-'); // Key-pad numbers - } - - keys.forEach( function(key) { - let modifiers = prefixModifiers - - // for some reason, in gnome-shell >= 40 Clutter.get_current_event() is now empty - // for keyboard events. Create here the modifiers that are needed in appicon.activate - modifiers |= (key.indexOf('-shift-') >= 0 ? Clutter.ModifierType.SHIFT_MASK : 0) - modifiers |= (key.indexOf('-ctrl-') >= 0 ? Clutter.ModifierType.CONTROL_MASK : 0) - - for (let i = 0; i < this._numHotkeys; i++) { - let appNum = i; - - Utils.addKeybinding(key + (i + 1), SETTINGS, () => this._activateApp(appNum, modifiers)); - } - }, this); - - this._hotKeysEnabled = true; - - if (SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.taskbar.toggleNumberOverlay(true); - } - - _disableHotKeys() { - if (!this._hotKeysEnabled) - return; - - let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-', // Regular numbers - 'app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-']; // Key-pad numbers - keys.forEach( function(key) { - for (let i = 0; i < this._numHotkeys; i++) { - Utils.removeKeybinding(key + (i + 1)); - } - }, this); - - if (Main.wm._switchToApplication) { - let gsSettings = new Gio.Settings({ schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA }); - - for (let i = 1; i < 10; ++i) { - Utils.addKeybinding(GS_HOTKEYS_KEY + i, gsSettings, Main.wm._switchToApplication.bind(Main.wm)); - } - } - - this._hotKeysEnabled = false; - - this.taskbar.toggleNumberOverlay(false); - } - - _optionalNumberOverlay() { - // Enable extra shortcut - if (SETTINGS.get_boolean('hot-keys')) - this._enableExtraShortcut(); - - this._signalsHandler.add([ - SETTINGS, - 'changed::hot-keys', - this._checkHotkeysOptions.bind(this) - ], [ - SETTINGS, - 'changed::hotkeys-overlay-combo', - () => { - if (SETTINGS.get_boolean('hot-keys') && SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.taskbar.toggleNumberOverlay(true); - else - this.taskbar.toggleNumberOverlay(false); - } - ], [ - SETTINGS, - 'changed::shortcut-num-keys', - () => this._resetHotkeys() - ]); - } - - _checkHotkeysOptions() { - if (SETTINGS.get_boolean('hot-keys')) - this._enableExtraShortcut(); - else - this._disableExtraShortcut(); - } - - _enableExtraShortcut() { - Utils.addKeybinding('shortcut', SETTINGS, () => this._showOverlay(true)); - } - - _disableExtraShortcut() { - Utils.removeKeybinding('shortcut'); - } - - _showOverlay(overlayFromShortcut) { - //wait for intellihide timeout initialization - if (!this._panel.intellihide) { - return; - } - - // Restart the counting if the shortcut is pressed again - let hotkey_option = SETTINGS.get_string('hotkeys-overlay-combo'); - - if (hotkey_option === 'NEVER') - return; - - if (hotkey_option === 'TEMPORARILY' || overlayFromShortcut) - this.taskbar.toggleNumberOverlay(true); - - this._panel.intellihide.revealAndHold(Intellihide.Hold.TEMPORARY); - - let timeout = OVERLAY_TIMEOUT; - - if (overlayFromShortcut) { - timeout = SHORTCUT_TIMEOUT; - } - - // Hide the overlay/dock after the timeout - this._timeoutsHandler.add([T2, timeout, () => { - if (hotkey_option != 'ALWAYS') { - this.taskbar.toggleNumberOverlay(false); - } - - this._panel.intellihide.release(Intellihide.Hold.TEMPORARY); - }]); - } - - _onSwipeBegin() { - this._swiping = true; - return true; - } - - _onSwipeEnd() { - this._timeoutsHandler.add([ - T1, - 0, - () => this._swiping = false - ]); - return true; - } - -} diff --git a/panel.js b/panel.js deleted file mode 100644 index b74e080..0000000 --- a/panel.js +++ /dev/null @@ -1,1257 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * - * Code to re-anchor the panel was taken from Thoma5 BottomPanel: - * https://github.com/Thoma5/gnome-shell-extension-bottompanel - * - * Pattern for moving clock based on Frippery Move Clock by R M Yorston - * http://frippery.org/extensions/ - * - * Some code was also adapted from the upstream Gnome Shell source code. - */ - -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import GObject from 'gi://GObject'; -import * as AppIcons from './appIcons.js'; -import * as Utils from './utils.js'; -import * as Taskbar from './taskbar.js'; -import * as TaskbarItemContainer from './taskbar.js'; -import * as Pos from './panelPositions.js'; -import * as PanelSettings from './panelSettings.js'; -import * as PanelStyle from './panelStyle.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as CtrlAltTab from 'resource:///org/gnome/shell/ui/ctrlAltTab.js'; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import St from 'gi://St'; -import Meta from 'gi://Meta'; -import Pango from 'gi://Pango'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import Shell from 'gi://Shell'; -import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; -import * as DateMenu from 'resource:///org/gnome/shell/ui/dateMenu.js'; -import * as Volume from 'resource:///org/gnome/shell/ui/status/volume.js'; -import * as Progress from './progress.js'; - -import * as Intellihide from './intellihide.js'; -import * as Transparency from './transparency.js'; -import {SETTINGS, DESKTOPSETTINGS, PERSISTENTSTORAGE, EXTENSION_PATH} from './extension.js'; -import {gettext as _, InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; - -let tracker = Shell.WindowTracker.get_default(); -export const panelBoxes = ['_leftBox', '_centerBox', '_rightBox']; - -//timeout names -const T2 = 'startIntellihideTimeout'; -const T4 = 'showDesktopTimeout'; -const T5 = 'trackerFocusAppTimeout'; -const T6 = 'waitPanelBoxAllocation'; - -const SHOW_SHOWDESKTOP_DELAY = 1000; -const SHOW_SHOWDESKTOP_TIME = 200; - -const FLOATING_MARGIN = 8; - -export const Panel = GObject.registerClass({ -}, class Panel extends St.Widget { - - _init(panelManager, monitor, panelBox, isStandalone) { - super._init({ layout_manager: new Clutter.BinLayout() }); - - this._timeoutsHandler = new Utils.TimeoutsHandler(); - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._injectionManager = new InjectionManager(); - - this.panelManager = panelManager; - this.panelStyle = new PanelStyle.PanelStyle(); - - this.monitor = monitor; - this.panelBox = panelBox; - - // when the original gnome-shell top panel is kept, all panels are "standalone", - // so in this case use isPrimary to get the panel on the primary dtp monitor, which - // might be different from the system's primary monitor. - this.isStandalone = isStandalone; - this.isPrimary = !isStandalone || (SETTINGS.get_boolean('stockgs-keep-top-panel') && - monitor == panelManager.dtpPrimaryMonitor); - - this._sessionStyle = null; - this._unmappedButtons = []; - this._elementGroups = []; - - let systemMenuInfo = Utils.getSystemMenuInfo(); - - if (isStandalone) { - this.panel = new SecondaryPanel({ name: 'panel', reactive: true }); - this.statusArea = this.panel.statusArea = {}; - - //next 3 functions are needed by other extensions to add elements to the secondary panel - this.panel.addToStatusArea = function(role, indicator, position, box) { - return Main.panel.addToStatusArea.call(this, role, indicator, position, box); - }; - - this.panel._addToPanelBox = function(role, indicator, position, box) { - Main.panel._addToPanelBox.call(this, role, indicator, position, box); - }; - - this.panel._onMenuSet = function(indicator) { - Main.panel._onMenuSet.call(this, indicator); - }; - - this._leftBox = this.panel._leftBox = new St.BoxLayout({ name: 'panelLeft' }); - this._centerBox = this.panel._centerBox = new St.BoxLayout({ name: 'panelCenter' }); - this._rightBox = this.panel._rightBox = new St.BoxLayout({ name: 'panelRight' }); - - this.menuManager = this.panel.menuManager = new PopupMenu.PopupMenuManager(this.panel); - - this._setPanelMenu(systemMenuInfo.name, systemMenuInfo.constructor, this.panel); - this._setPanelMenu('dateMenu', DateMenu.DateMenuButton, this.panel); - this._setPanelMenu('activities', Main.panel.statusArea.activities.constructor, this.panel); - - this.panel.add_child(this._leftBox); - this.panel.add_child(this._centerBox); - this.panel.add_child(this._rightBox); - } else { - this.panel = Main.panel; - this.statusArea = Main.panel.statusArea; - this.menuManager = Main.panel.menuManager; - - panelBoxes.forEach(p => this[p] = Main.panel[p]); - - ['activities', systemMenuInfo.name, 'dateMenu'].forEach(b => { - let container = this.statusArea[b].container; - let parent = container.get_parent(); - let siblings = parent.get_children(); - let index = siblings.indexOf(container); - - container._dtpOriginalParent = parent; - container._dtpOriginalIndex = index && index == siblings.length - 1 ? -1: index; - parent ? parent.remove_child(container) : null; - this.panel.add_child(container); - }); - } - - // Create a wrapper around the real showAppsIcon in order to add a popupMenu. Most of - // its behavior is handled by the taskbar, but its positioning is done at the panel level - this.showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this); - this.panel.add_child(this.showAppsIconWrapper.realShowAppsIcon); - - this.panel._delegate = this; - - this.add_child(this.panel); - - if (Main.panel._onButtonPress || Main.panel._tryDragWindow) { - this._signalsHandler.add([ - this.panel, - [ - 'button-press-event', - 'touch-event' - ], - this._onButtonPress.bind(this) - ]); - } - - if (Main.panel._onKeyPress) { - this._signalsHandler.add([this.panel, 'key-press-event', Main.panel._onKeyPress.bind(this)]); - } - - Main.ctrlAltTabManager.addGroup(this, _("Top Bar")+" "+ monitor.index, 'focus-top-bar-symbolic', - { sortGroup: CtrlAltTab.SortGroup.TOP }); - } - - enable () { - let { name: systemMenuName } = Utils.getSystemMenuInfo(); - - if (this.statusArea[systemMenuName] && this.statusArea[systemMenuName]._volumeOutput) { - Utils.getIndicators(this.statusArea[systemMenuName]._volumeOutput)._dtpIgnoreScroll = 1; - } - - this._toggleFloatingRoundedTheme(); - - this.geom = this.getGeometry(); - - this._setPanelPosition(); - - if (!this.isStandalone) { - this._injectionManager.overrideMethod(Object.getPrototypeOf(this.panel), 'vfunc_allocate', () => (box) => this._mainPanelAllocate(box)); - - // remove the extra space before the clock when the message-indicator is displayed - if (DateMenu.IndicatorPad) { - this._injectionManager.overrideMethod(DateMenu.IndicatorPad.prototype, 'vfunc_get_preferred_width', () => () => [0,0]); - this._injectionManager.overrideMethod(DateMenu.IndicatorPad.prototype, 'vfunc_get_preferred_height', () => () => [0,0]); - } - } - - if (!DateMenu.IndicatorPad && this.statusArea.dateMenu) { - //3.36 switched to a size constraint applied on an anonymous child - let indicatorPad = this.statusArea.dateMenu.get_first_child().get_first_child(); - - this._dateMenuIndicatorPadContraints = indicatorPad.get_constraints(); - indicatorPad.clear_constraints(); - } - - this.menuManager._oldChangeMenu = this.menuManager._changeMenu; - this.menuManager._changeMenu = (menu) => { - if (!SETTINGS.get_boolean('stockgs-panelbtn-click-only')) { - this.menuManager._oldChangeMenu(menu); - } - }; - - this.dynamicTransparency = new Transparency.DynamicTransparency(this); - - this.taskbar = new Taskbar.Taskbar(this); - - this.panel.add_child(this.taskbar.actor); - - this._setShowDesktopButton(true); - - this._setAllocationMap(); - - this.panel.add_style_class_name('zorintaskbarMainPanel ' + this.getOrientation()); - - this._timeoutsHandler.add([T2, Intellihide.INTELLIHIDE_ENABLE_START_DELAY, () => this.intellihide = new Intellihide.Intellihide(this)]); - - this._signalsHandler.add( - // this is to catch changes to the theme or window scale factor - [ - Utils.getStageTheme(), - 'changed', - () => (this._resetGeometry(), this._setShowDesktopButtonStyle()), - ], - [ - // sync hover after a popupmenu is closed - this.taskbar, - 'menu-closed', - () => this.panel.sync_hover() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._adjustForOverview() - ], - [ - Main.overview, - 'hidden', - () => { - if (this.isPrimary) { - //reset the primary monitor when exiting the overview - this.panelManager.setFocusedMonitor(this.monitor); - } - } - ], - [ - this.statusArea.activities, - 'captured-event', - (actor, e) => { - if (e.type() == Clutter.EventType.BUTTON_PRESS || e.type() == Clutter.EventType.TOUCH_BEGIN) { - //temporarily use as primary the monitor on which the activities btn was clicked - this.panelManager.setFocusedMonitor(this.monitor); - } - } - ], - [ - this._centerBox, - 'child-added', - () => this._onBoxActorAdded(this._centerBox) - ], - [ - this._rightBox, - 'child-added', - () => this._onBoxActorAdded(this._rightBox) - ], - [ - Main.layoutManager, - 'startup-complete', - () => this._resetGeometry() - ] - ); - - this._bindSettingsChanges(); - - this.panelStyle.enable(this); - - if (this.checkIfVertical()) { - this._signalsHandler.add([ - this.panelBox, - 'notify::visible', - () => { - if (this.panelBox.visible) { - this._refreshVerticalAlloc(); - } - } - ]); - - if (this.statusArea.dateMenu) { - this._formatVerticalClock(); - - this._signalsHandler.add([ - this.statusArea.dateMenu._clock, - 'notify::clock', - () => this._formatVerticalClock() - ]); - } - } - - // Since we are usually visible but not usually changing, make sure - // most repaint requests don't actually require us to repaint anything. - // This saves significant CPU when repainting the screen. - this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); - - this._initProgressManager(); - } - - disable() { - this.panelStyle.disable(); - - this._timeoutsHandler.destroy(); - this._signalsHandler.destroy(); - - this.panel.remove_child(this.taskbar.actor); - - if (this.intellihide) { - this.intellihide.destroy(); - } - - this.dynamicTransparency.destroy(); - - this.progressManager.destroy(); - - this.taskbar.destroy(); - this.showAppsIconWrapper.destroy(); - - this.menuManager._changeMenu = this.menuManager._oldChangeMenu; - - this._unmappedButtons.forEach(a => this._disconnectVisibleId(a)); - - if (this.statusArea.dateMenu) { - this.statusArea.dateMenu._clockDisplay.text = this.statusArea.dateMenu._clock.clock; - this.statusArea.dateMenu._clockDisplay.clutter_text.set_width(-1); - - if (this._dateMenuIndicatorPadContraints) { - let indicatorPad = this.statusArea.dateMenu.get_first_child().get_first_child(); - - this._dateMenuIndicatorPadContraints.forEach(c => indicatorPad.add_constraint(c)); - } - } - - this._setVertical(this.panel, false); - this._setVertical(this._centerBox, false); - this._setVertical(this._rightBox, false); - - let { name: systemMenuName } = Utils.getSystemMenuInfo(); - - if (!this.isStandalone) { - ['vertical', 'horizontal', 'zorintaskbarMainPanel'].forEach(c => this.panel.remove_style_class_name(c)); - - if (!Main.sessionMode.isLocked) { - ['activities', systemMenuName, 'dateMenu'].forEach(b => { - let container = this.statusArea[b].container; - let originalParent = container._dtpOriginalParent; - - this.panel.remove_child(container); - - originalParent && originalParent.insert_child_at_index( - container, - Math.min(container._dtpOriginalIndex, originalParent.get_children().length - 1) - ); - - delete container._dtpOriginalParent; - delete container._dtpOriginalIndex; - }); - } - - this._setShowDesktopButton(false); - - delete Utils.getIndicators(this.statusArea[systemMenuName]._volumeOutput)._dtpIgnoreScroll; - - this._injectionManager.clear(); - - this.panel._delegate = this.panel; - } else { - this._removePanelMenu('dateMenu'); - this._removePanelMenu(systemMenuName); - this._removePanelMenu('activities'); - } - - Main.ctrlAltTabManager.removeGroup(this); - } - - getPosition() { - let position = PanelSettings.getPanelPosition(SETTINGS, this.monitor.index); - - if (position == Pos.TOP) { - return St.Side.TOP; - } else if (position == Pos.RIGHT) { - return St.Side.RIGHT; - } else if (position == Pos.BOTTOM) { - return St.Side.BOTTOM; - } - - return St.Side.LEFT; - } - - checkIfVertical() { - let position = this.getPosition(); - - return (position == St.Side.LEFT || position == St.Side.RIGHT); - } - - getOrientation() { - return (this.checkIfVertical() ? 'vertical' : 'horizontal'); - } - - updateElementPositions() { - let panelPositions = this.panelManager.panelsElementPositions[this.monitor.index] || Pos.defaults; - - this._updateGroupedElements(panelPositions); - - this.panel.hide(); - this.panel.show(); - } - - _updateGroupedElements(panelPositions) { - let previousPosition = 0; - let previousCenteredPosition = 0; - let currentGroup = -1; - - this._elementGroups = []; - - panelPositions.forEach(pos => { - let allocationMap = this.allocationMap[pos.element]; - - if (allocationMap.actor) { - allocationMap.actor.visible = pos.visible; - - if (!pos.visible) { - return; - } - - let currentPosition = pos.position; - let isCentered = Pos.checkIfCentered(currentPosition); - - if (currentPosition == Pos.STACKED_TL && previousPosition == Pos.STACKED_BR) { - currentPosition = Pos.STACKED_BR; - } - - if (!previousPosition || - (previousPosition == Pos.STACKED_TL && currentPosition != Pos.STACKED_TL) || - (previousPosition != Pos.STACKED_BR && currentPosition == Pos.STACKED_BR) || - (isCentered && previousPosition != currentPosition && previousPosition != Pos.STACKED_BR)) { - this._elementGroups[++currentGroup] = { elements: [], index: this._elementGroups.length, expandableIndex: -1 }; - previousCenteredPosition = 0; - } - - if (pos.element == Pos.TASKBAR) { - this._elementGroups[currentGroup].expandableIndex = this._elementGroups[currentGroup].elements.length; - } - - if (isCentered && !this._elementGroups[currentGroup].isCentered) { - this._elementGroups[currentGroup].isCentered = 1; - previousCenteredPosition = currentPosition; - } - - this._elementGroups[currentGroup].position = previousCenteredPosition || currentPosition; - this._elementGroups[currentGroup].elements.push(allocationMap); - - allocationMap.position = currentPosition; - previousPosition = currentPosition; - } - }); - } - - _bindSettingsChanges() { - let isVertical = this.checkIfVertical(); - - this._signalsHandler.add( - [ - SETTINGS, - [ - 'changed::panel-sizes', - 'changed::group-apps' - ], - () => this._resetGeometry() - ], - [ - SETTINGS, - [ - 'changed::showdesktop-button-width', - 'changed::show-showdesktop-icon' - ], - () => this._setShowDesktopButtonStyle() - ], - [ - SETTINGS, - 'changed::floating-rounded-theme', - () => { - this._resetGeometry(); - } - ], - [ - DESKTOPSETTINGS, - 'changed::clock-format', - () => { - this._clockFormat = null; - - if (isVertical) { - this._formatVerticalClock(); - } - } - ], - [ - SETTINGS, - 'changed::progress-show-bar', - () => this._initProgressManager() - ], - [ - SETTINGS, - 'changed::progress-show-count', - () => this._initProgressManager() - ] - ); - - if (isVertical) { - this._signalsHandler.add([SETTINGS, 'changed::group-apps-label-max-width', () => this._resetGeometry()]); - } - } - - _setPanelMenu(propName, constr, container) { - if (!this.statusArea[propName]) { - this.statusArea[propName] = this._getPanelMenu(propName, constr); - this.menuManager.addMenu(this.statusArea[propName].menu); - container.insert_child_at_index(this.statusArea[propName].container, 0); - } - } - - _removePanelMenu(propName) { - if (this.statusArea[propName]) { - let parent = this.statusArea[propName].container.get_parent(); - - if (parent) { - parent.remove_child(this.statusArea[propName].container); - } - - //calling this.statusArea[propName].destroy(); is buggy for now, gnome-shell never - //destroys those panel menus... - //since we can't destroy the menu (hence properly disconnect its signals), let's - //store it so the next time a panel needs one of its kind, we can reuse it instead - //of creating a new one - let panelMenu = this.statusArea[propName]; - - this.menuManager.removeMenu(panelMenu.menu); - PERSISTENTSTORAGE[propName].push(panelMenu); - this.statusArea[propName] = null; - } - } - - _getPanelMenu(propName, constr) { - PERSISTENTSTORAGE[propName] = PERSISTENTSTORAGE[propName] || []; - - if (!PERSISTENTSTORAGE[propName].length) { - PERSISTENTSTORAGE[propName].push(new constr()); - } - - return PERSISTENTSTORAGE[propName].pop(); - } - - _adjustForOverview() { - let isFocusedMonitor = this.panelManager.checkIfFocusedMonitor(this.monitor); - let isOverview = !!Main.overview.visibleTarget; - let isOverviewFocusedMonitor = isOverview && isFocusedMonitor; - let isShown = !isOverview || isOverviewFocusedMonitor; - let actorData = Utils.getTrackedActorData(this.panelBox) - - // prevent the "chrome" to update the panelbox visibility while in overview - actorData.trackFullscreen = !isOverview - - this.panelBox[isShown ? 'show' : 'hide'](); - } - - _toggleFloatingRoundedTheme() { - if (this.panelBox.has_style_class_name('top')) { - this.panelBox.remove_style_class_name('top'); - } else if (this.panelBox.has_style_class_name('left')) { - this.panelBox.remove_style_class_name('left'); - } else if (this.panelBox.has_style_class_name('right')) { - this.panelBox.remove_style_class_name('right'); - } else if (this.panelBox.has_style_class_name('bottom')) { - this.panelBox.remove_style_class_name('bottom'); - } - - if (SETTINGS.get_boolean('floating-rounded-theme')) { - if (!this.panelBox.has_style_class_name('floating')) { - this.panelBox.add_style_class_name('floating'); - } - - let position = this.getPosition(); - if (position == St.Side.TOP) { - this.panelBox.add_style_class_name('top'); - } else if (position == St.Side.RIGHT) { - this.panelBox.add_style_class_name('right'); - } else if (position == St.Side.BOTTOM) { - this.panelBox.add_style_class_name('bottom'); - } else { - this.panelBox.add_style_class_name('left'); - } - } else { - if (this.panelBox.has_style_class_name('floating')) - this.panelBox.remove_style_class_name('floating'); - } - } - - _resetGeometry() { - this._toggleFloatingRoundedTheme() - this.geom = this.getGeometry(); - this._setPanelPosition(); - this.taskbar.resetAppIcons(true); - this.dynamicTransparency.updateExternalStyle(); - - if (this.intellihide && this.intellihide.enabled) { - this.intellihide.reset(); - } - - if (this.checkIfVertical()) { - this.showAppsIconWrapper.realShowAppsIcon.toggleButton.set_width(this.geom.w); - this._refreshVerticalAlloc(); - } - } - - getGeometry() { - let scaleFactor = Utils.getScaleFactor(); - let panelBoxTheme = this.panelBox.get_theme_node(); - let lrPadding = panelBoxTheme.get_padding(St.Side.RIGHT) + panelBoxTheme.get_padding(St.Side.LEFT); - let topPadding = panelBoxTheme.get_padding(St.Side.TOP); - let tbPadding = topPadding + panelBoxTheme.get_padding(St.Side.BOTTOM); - let position = this.getPosition(); - let length = PanelSettings.getPanelLength(SETTINGS, this.monitor.index) / 100; - let anchor = PanelSettings.getPanelAnchor(SETTINGS, this.monitor.index); - let anchorPlaceOnMonitor = 0; - let gsTopPanelOffset = 0; - let x = 0, y = 0; - let w = 0, h = 0; - - const panelSize = PanelSettings.getPanelSize(SETTINGS, this.monitor.index); - this.dtpSize = panelSize * scaleFactor; - - if (SETTINGS.get_boolean('stockgs-keep-top-panel') && Main.layoutManager.primaryMonitor == this.monitor) { - gsTopPanelOffset = Main.layoutManager.panelBox.height - topPadding; - } - - if (this.checkIfVertical()) { - if (!SETTINGS.get_boolean('group-apps')) { - // add window title width and side padding of _dtpIconContainer when vertical - this.dtpSize += SETTINGS.get_int('group-apps-label-max-width') + AppIcons.DEFAULT_PADDING_SIZE * 2 / scaleFactor; - } - - this.sizeFunc = 'get_preferred_height', - this.fixedCoord = { c1: 'x1', c2: 'x2' } - this.varCoord = { c1: 'y1', c2: 'y2' }; - - w = this.dtpSize; - h = this.monitor.height * length - tbPadding - gsTopPanelOffset; - } else { - this.sizeFunc = 'get_preferred_width'; - this.fixedCoord = { c1: 'y1', c2: 'y2' }; - this.varCoord = { c1: 'x1', c2: 'x2' }; - - w = this.monitor.width * length - lrPadding; - h = this.dtpSize; - } - - if (position == St.Side.TOP || position == St.Side.LEFT) { - x = this.monitor.x; - y = this.monitor.y + gsTopPanelOffset; - } else if (position == St.Side.RIGHT) { - x = this.monitor.x + this.monitor.width - this.dtpSize - lrPadding; - y = this.monitor.y + gsTopPanelOffset; - } else { //BOTTOM - x = this.monitor.x; - y = this.monitor.y + this.monitor.height - this.dtpSize - tbPadding; - } - - if (this.checkIfVertical()) { - let viewHeight = this.monitor.height - gsTopPanelOffset; - - if (anchor === Pos.MIDDLE) { - anchorPlaceOnMonitor = (viewHeight - h) / 2; - } else if (anchor === Pos.END) { - anchorPlaceOnMonitor = viewHeight - h; - } else { // Pos.START - anchorPlaceOnMonitor = 0; - } - y = y + anchorPlaceOnMonitor; - } else { - if (anchor === Pos.MIDDLE) { - anchorPlaceOnMonitor = (this.monitor.width - w) / 2; - } else if (anchor === Pos.END) { - anchorPlaceOnMonitor = this.monitor.width - w; - } else { // Pos.START - anchorPlaceOnMonitor = 0; - } - x = x + anchorPlaceOnMonitor; - } - - if (SETTINGS.get_boolean('floating-rounded-theme')) { - if (position == St.Side.BOTTOM || position == St.Side.TOP) { - x -= FLOATING_MARGIN; - } else { // LEFT or RIGHT - y -= FLOATING_MARGIN; - } - } - - return { - x, y, - w, h, - lrPadding, - tbPadding, - position - }; - } - - _setAllocationMap() { - this.allocationMap = {}; - let setMap = (name, actor) => this.allocationMap[name] = { - actor: actor, - box: new Clutter.ActorBox() - }; - - setMap(Pos.SHOW_APPS_BTN, this.showAppsIconWrapper.realShowAppsIcon); - setMap(Pos.ACTIVITIES_BTN, this.statusArea.activities ? this.statusArea.activities.container : 0); - setMap(Pos.LEFT_BOX, this._leftBox); - setMap(Pos.TASKBAR, this.taskbar.actor); - setMap(Pos.CENTER_BOX, this._centerBox); - setMap(Pos.DATE_MENU, this.statusArea.dateMenu.container); - setMap(Pos.SYSTEM_MENU, this.statusArea[Utils.getSystemMenuInfo().name].container); - setMap(Pos.RIGHT_BOX, this._rightBox); - setMap(Pos.DESKTOP_BTN, this._showDesktopButton); - } - - _mainPanelAllocate(box) { - this.panel.set_allocation(box); - } - - vfunc_allocate(box) { - this.set_allocation(box); - - let fixed = 0; - let centeredMonitorGroup; - let panelAlloc = new Clutter.ActorBox({ x1: 0, y1: 0, x2: this.geom.w, y2: this.geom.h }); - let assignGroupSize = (group, update) => { - group.size = 0; - group.tlOffset = 0; - group.brOffset = 0; - - group.elements.forEach(element => { - if (!update) { - element.box[this.fixedCoord.c1] = panelAlloc[this.fixedCoord.c1]; - element.box[this.fixedCoord.c2] = panelAlloc[this.fixedCoord.c2]; - element.natSize = element.actor[this.sizeFunc](-1)[1]; - } - - if (!group.isCentered || Pos.checkIfCentered(element.position)) { - group.size += element.natSize; - } else if (element.position == Pos.STACKED_TL) { - group.tlOffset += element.natSize; - } else { // Pos.STACKED_BR - group.brOffset += element.natSize; - } - }); - - if (group.isCentered) { - group.size += Math.max(group.tlOffset, group.brOffset) * 2; - group.tlOffset = Math.max(group.tlOffset - group.brOffset, 0); - } - }; - let allocateGroup = (group, tlLimit, brLimit) => { - let startPosition = tlLimit; - let currentPosition = 0; - - if (group.expandableIndex >= 0) { - let availableSize = brLimit - tlLimit; - let expandable = group.elements[group.expandableIndex]; - let i = 0; - let l = this._elementGroups.length; - let tlSize = 0; - let brSize = 0; - - if (centeredMonitorGroup && (centeredMonitorGroup != group || expandable.position != Pos.CENTERED_MONITOR)) { - if (centeredMonitorGroup.index < group.index || (centeredMonitorGroup == group && expandable.position == Pos.STACKED_TL)) { - i = centeredMonitorGroup.index; - } else { - l = centeredMonitorGroup.index; - } - } - - for (; i < l; ++i) { - let refGroup = this._elementGroups[i]; - - if (i < group.index && (!refGroup.fixed || refGroup[this.varCoord.c2] > tlLimit)) { - tlSize += refGroup.size; - } else if (i > group.index && (!refGroup.fixed || refGroup[this.varCoord.c1] < brLimit)) { - brSize += refGroup.size; - } - } - - if (group.isCentered) { - availableSize -= Math.max(tlSize, brSize) * 2; - } else { - availableSize -= tlSize + brSize; - } - - if (availableSize < group.size) { - expandable.natSize -= (group.size - availableSize) * (group.isCentered && !Pos.checkIfCentered(expandable.position) ? .5 : 1); - assignGroupSize(group, true); - } - } - - if (group.isCentered) { - startPosition = tlLimit + (brLimit - tlLimit - group.size) * .5; - } else if (group.position == Pos.STACKED_BR) { - startPosition = brLimit - group.size; - } - - currentPosition = group.tlOffset + startPosition; - - group.elements.forEach(element => { - element.box[this.varCoord.c1] = Math.round(currentPosition); - element.box[this.varCoord.c2] = Math.round((currentPosition += element.natSize)); - - element.actor.allocate(element.box); - }); - - group[this.varCoord.c1] = startPosition; - group[this.varCoord.c2] = currentPosition; - group.fixed = 1; - ++fixed; - }; - - this.panel.allocate(panelAlloc); - - this._elementGroups.forEach(group => { - group.fixed = 0; - - assignGroupSize(group); - - if (group.position == Pos.CENTERED_MONITOR) { - centeredMonitorGroup = group; - } - }); - - if (centeredMonitorGroup) { - allocateGroup(centeredMonitorGroup, panelAlloc[this.varCoord.c1], panelAlloc[this.varCoord.c2]); - } - - let iterations = 0; //failsafe - while (fixed < this._elementGroups.length && ++iterations < 10) { - for (let i = 0, l = this._elementGroups.length; i < l; ++i) { - let group = this._elementGroups[i]; - - if (group.fixed) { - continue; - } - - let prevGroup = this._elementGroups[i - 1]; - let nextGroup = this._elementGroups[i + 1]; - let prevLimit = prevGroup && prevGroup.fixed ? prevGroup[this.varCoord.c2] : - centeredMonitorGroup && group.index > centeredMonitorGroup.index ? centeredMonitorGroup[this.varCoord.c2] : panelAlloc[this.varCoord.c1]; - let nextLimit = nextGroup && nextGroup.fixed ? nextGroup[this.varCoord.c1] : - centeredMonitorGroup && group.index < centeredMonitorGroup.index ? centeredMonitorGroup[this.varCoord.c1] : panelAlloc[this.varCoord.c2]; - - if (group.position == Pos.STACKED_TL) { - allocateGroup(group, panelAlloc[this.varCoord.c1], nextLimit); - } else if (group.position == Pos.STACKED_BR) { - allocateGroup(group, prevLimit, panelAlloc[this.varCoord.c2]); - } else if ((!prevGroup || prevGroup.fixed) && (!nextGroup || nextGroup.fixed)) { // CENTERED - allocateGroup(group, prevLimit, nextLimit); - } - } - } - } - - _setPanelPosition() { - let clipContainer = this.panelBox.get_parent(); - - this.set_size(this.geom.w, this.geom.h); - clipContainer.set_position(this.geom.x, this.geom.y); - - this._setVertical(this.panel, this.checkIfVertical()); - - // styles for theming - Object.keys(St.Side).forEach(p => { - let cssName = 'zorintaskbar' + p.charAt(0) + p.slice(1).toLowerCase(); - - this.panel[(St.Side[p] == this.geom.position ? 'add' : 'remove') + '_style_class_name'](cssName); - }); - - this._setPanelClip(clipContainer); - - Main.layoutManager._updateHotCorners(); - Main.layoutManager._updatePanelBarrier(this); - } - - _setPanelClip(clipContainer) { - clipContainer = clipContainer || this.panelBox.get_parent(); - this._timeoutsHandler.add([T6, 0, () => Utils.setClip(clipContainer, clipContainer.x, clipContainer.y, this.panelBox.width, this.panelBox.height)]); - } - - _onButtonPress(actor, event) { - let type = event.type(); - let isPress = type == Clutter.EventType.BUTTON_PRESS; - let button = isPress ? event.get_button() : -1; - let [stageX, stageY] = event.get_coords(); - - if (button == 3 && global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, stageX, stageY) == this.panel) { - //right click on an empty part of the panel, temporarily borrow and display the showapps context menu - Main.layoutManager.setDummyCursorGeometry(stageX, stageY, 0, 0); - - this.showAppsIconWrapper.createMenu(); - this.showAppsIconWrapper.popupMenu(Main.layoutManager.dummyCursor); - - return Clutter.EVENT_STOP; - } else { - const targetActor = global.stage.get_event_actor(event); - - if (Main.modalCount > 0 || targetActor != actor || - (!isPress && type != Clutter.EventType.TOUCH_BEGIN) || - (isPress && button != 1)) { - return Clutter.EVENT_PROPAGATE; - } - } - - let params = this.checkIfVertical() ? [stageY, 'y', 'height'] : [stageX, 'x', 'width']; - let dragWindow = this._getDraggableWindowForPosition.apply(this, params.concat(['maximized_' + this.getOrientation() + 'ly'])); - - if (!dragWindow) - return Clutter.EVENT_PROPAGATE; - - global.display.begin_grab_op(dragWindow, - Meta.GrabOp.MOVING, - false, /* pointer grab */ - true, /* frame action */ - button, - event.get_state(), - event.get_time(), - stageX, stageY); - - return Clutter.EVENT_STOP; - } - - _getDraggableWindowForPosition(stageCoord, coord, dimension, maximizedProp) { - let workspace = Utils.getCurrentWorkspace(); - let allWindowsByStacking = global.display.sort_windows_by_stacking( - workspace.list_windows() - ).reverse(); - - return Utils.find(allWindowsByStacking, metaWindow => { - let rect = metaWindow.get_frame_rect(); - - return metaWindow.get_monitor() == this.monitor.index && - metaWindow.showing_on_its_workspace() && - metaWindow.get_window_type() != Meta.WindowType.DESKTOP && - metaWindow[maximizedProp] && - stageCoord > rect[coord] && stageCoord < rect[coord] + rect[dimension]; - }); - } - - _onBoxActorAdded(box) { - if (this.checkIfVertical()) { - this._setVertical(box, true); - } - } - - _refreshVerticalAlloc() { - this._setVertical(this._centerBox, true); - this._setVertical(this._rightBox, true); - this._formatVerticalClock(); - } - - _setVertical(actor, isVertical) { - let _set = (actor, isVertical) => { - if (!actor || actor instanceof Dash.DashItemContainer || actor instanceof TaskbarItemContainer.TaskbarItemContainer) { - return; - } - - if (actor instanceof St.BoxLayout) { - actor.vertical = isVertical; - } else if ( - actor != this.statusArea.appMenu && - ((actor._delegate || actor) instanceof PanelMenu.ButtonBox || actor == this.statusArea.quickSettings) - ) { - let child = actor.get_first_child(); - - if (isVertical && !actor.visible && !actor._dtpVisibleId) { - this._unmappedButtons.push(actor); - actor._dtpVisibleId = actor.connect('notify::visible', () => { - this._disconnectVisibleId(actor); - this._refreshVerticalAlloc(); - }); - actor._dtpDestroyId = actor.connect('destroy', () => this._disconnectVisibleId(actor)); - } - - if (child) { - let [, natWidth] = actor.get_preferred_width(-1); - - child.x_align = Clutter.ActorAlign[isVertical ? 'CENTER' : 'START']; - actor.set_width(isVertical ? this.dtpSize : -1); - isVertical = isVertical && (natWidth > this.dtpSize); - actor[(isVertical ? 'add' : 'remove') + '_style_class_name']('vertical'); - } - } - - actor.get_children().forEach(c => _set(c, isVertical)); - }; - - _set(actor, false); - - if (isVertical) - _set(actor, isVertical); - } - - _disconnectVisibleId(actor) { - actor.disconnect(actor._dtpVisibleId); - actor.disconnect(actor._dtpDestroyId); - - delete actor._dtpVisibleId; - delete actor._dtpDestroyId; - - this._unmappedButtons.splice(this._unmappedButtons.indexOf(actor), 1); - } - - _formatVerticalClock() { - // https://github.com/GNOME/gnome-desktop/blob/master/libgnome-desktop/gnome-wall-clock.c#L310 - if (this.statusArea.dateMenu) { - let datetime = this.statusArea.dateMenu._clock.clock; - let datetimeParts = datetime.split(' '); - let time = datetimeParts[1]; - let clockText = this.statusArea.dateMenu._clockDisplay.clutter_text; - let setClockText = (text, useTimeSeparator) => { - let stacks = text instanceof Array; - let separator = `\n ${useTimeSeparator ? '‧‧' : '—' } \n`; - - clockText.set_text((stacks ? text.join(separator) : text).trim()); - clockText.set_use_markup(stacks); - clockText.get_allocation_box(); - - return !clockText.get_layout().is_ellipsized(); - }; - - if (clockText.ellipsize == Pango.EllipsizeMode.NONE) { - //on gnome-shell 3.36.4, the clockdisplay isn't ellipsize anymore, so set it back - clockText.ellipsize = Pango.EllipsizeMode.END; - } - - clockText.natural_width = this.dtpSize; - - if (!time) { - datetimeParts = datetime.split(' '); - time = datetimeParts.pop(); - datetimeParts = [datetimeParts.join(' '), time]; - } - - if (!setClockText(datetime) && - !setClockText(datetimeParts) && - !setClockText(time)) { - let timeParts = time.split('∶'); - - if (!this._clockFormat) { - this._clockFormat = DESKTOPSETTINGS.get_string('clock-format'); - } - - if (this._clockFormat == '12h') { - timeParts.push.apply(timeParts, timeParts.pop().split(' ')); - } - - setClockText(timeParts, true); - } - } - } - - _setShowDesktopButton(add) { - if (add) { - if(this._showDesktopButton) - return; - - this._showDesktopButton = new St.Bin({ style_class: 'panel-button', - reactive: true, - can_focus: true, - // x_fill: true, - // y_fill: true, - track_hover: true }); - - this._showDesktopButton.icon = new St.Icon({ gicon: Gio.icon_new_for_string(`${EXTENSION_PATH}/img/show-desktop-symbolic.svg`), style_class: 'system-status-icon' }); - - this._setShowDesktopButtonStyle(); - - this._showDesktopButton.connect('touch-event', (actor, event) => { - if (event.type() == Clutter.EventType.TOUCH_BEGIN) { - this._onShowDesktopButtonPress(); - } - }); - this._showDesktopButton.connect('button-press-event', () => this._onShowDesktopButtonPress()); - this._showDesktopButton.connect('enter-event', () => { - if (SETTINGS.get_boolean('show-showdesktop-hover')) { - this._timeoutsHandler.add([T4, SHOW_SHOWDESKTOP_DELAY, () => { - this._hiddenDesktopWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - this._toggleWorkspaceWindows(true, this._hiddenDesktopWorkspace); - }]); - } - }); - - this._showDesktopButton.connect('leave-event', () => { - if (SETTINGS.get_boolean('show-showdesktop-hover')) { - if (this._timeoutsHandler.getId(T4)) { - this._timeoutsHandler.remove(T4); - } else if (this._hiddenDesktopWorkspace) { - this._toggleWorkspaceWindows(false, this._hiddenDesktopWorkspace); - } - } - }); - - this.panel.add_child(this._showDesktopButton); - } else { - if(!this._showDesktopButton) - return; - - this.panel.remove_child(this._showDesktopButton); - this._showDesktopButton.destroy(); - this._showDesktopButton = null; - } - } - - _setShowDesktopButtonStyle() { - let rgb = this._getBackgroundBrightness() ? "rgba(55, 55, 55, .2)" : "rgba(200, 200, 200, .2)"; - - for (let i = 0; i < this._showDesktopButton.get_children().length; i++) { - if (this._showDesktopButton.get_children()[i] == this._showDesktopButton.icon) { - this._showDesktopButton.remove_child(this._showDesktopButton.icon); - } - } - - if (this._showDesktopButton) { - if (SETTINGS.get_boolean('show-showdesktop-icon')) { - this._showDesktopButton.add_child(this._showDesktopButton.icon); - - let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px'; - let isVertical = this.checkIfVertical(); - let buttonPadding = isVertical ? buttonSize + ' 0;' : '0 ' + buttonSize + ';'; - - this._showDesktopButton.set_style('padding: ' + buttonPadding); - } else { - let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px;'; - let isVertical = this.checkIfVertical(); - - let style = "border: 0 solid " + rgb + "; padding: 0;"; - style += isVertical ? 'border-top-width:1px;height:' + buttonSize : 'border-left-width:1px;width:' + buttonSize; - - this._showDesktopButton.set_style(style); - this._showDesktopButton[(isVertical ? 'x' : 'y') + '_expand'] = true; - } - } - } - - // _getBackgroundBrightness: return true if panel has a bright background color - _getBackgroundBrightness() { - return Utils.checkIfColorIsBright(this.dynamicTransparency.backgroundColorRgb); - } - - _toggleWorkspaceWindows(hide, workspace) { - let time = SHOW_SHOWDESKTOP_TIME * .001; - - workspace.list_windows().forEach(w => { - if (!w.minimized && !w.customJS_ding && w.window_type != Meta.WindowType.DESKTOP) { - let tweenOpts = { - opacity: hide ? 0 : 255, - time: time, - transition: 'easeOutQuad' - }; - - Utils.animateWindowOpacity(w.get_compositor_private(), tweenOpts); - } - }); - } - - _onShowDesktopButtonPress() { - let label = 'trackerFocusApp'; - - this._signalsHandler.removeWithLabel(label); - this._timeoutsHandler.remove(T5); - - if(this._restoreWindowList && this._restoreWindowList.length) { - this._timeoutsHandler.remove(T4); - - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = current_workspace.list_windows(); - this._restoreWindowList.forEach(function(w) { - if(windows.indexOf(w) > -1) - Main.activateWindow(w); - }); - this._restoreWindowList = null; - } else { - let current_workspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - let windows = current_workspace.list_windows().filter(function (w) { - return w.showing_on_its_workspace() && !w.skip_taskbar; - }); - windows = global.display.sort_windows_by_stacking(windows); - - windows.forEach(function(w) { - w.minimize(); - }); - - this._restoreWindowList = windows; - - this._timeoutsHandler.add([T5, 20, () => this._signalsHandler.addWithLabel( - label, - [ - tracker, - 'notify::focus-app', - () => this._restoreWindowList = null - ] - )]); - } - - Main.overview.hide(); - } - - _initProgressManager() { - const progressVisible = SETTINGS.get_boolean('progress-show-bar'); - const countVisible = SETTINGS.get_boolean('progress-show-count'); - const pm = this.progressManager; - - if(!pm && (progressVisible || countVisible)) - this.progressManager = new Progress.ProgressManager(); - else if (pm) - Object.keys(pm._entriesByDBusName).forEach((k) => pm._entriesByDBusName[k].setCountVisible(countVisible)); - } -}); - -export const SecondaryPanel = GObject.registerClass({ -}, class SecondaryPanel extends St.Widget { - - _init(params) { - super._init(params); - } - - vfunc_allocate(box) { - this.set_allocation(box); - } -}); diff --git a/panelManager.js b/panelManager.js deleted file mode 100644 index 5a08c43..0000000 --- a/panelManager.js +++ /dev/null @@ -1,756 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * - * Code to re-anchor the panel was taken from Thoma5 BottomPanel: - * https://github.com/Thoma5/gnome-shell-extension-bottompanel - * - * Pattern for moving clock based on Frippery Move Clock by R M Yorston - * http://frippery.org/extensions/ - * - * Some code was also adapted from the upstream Gnome Shell source code. - */ - -import * as Overview from './overview.js'; -import * as Panel from './panel.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import * as DesktopIconsIntegration from './desktopIconsIntegration.js'; - -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Clutter from 'gi://Clutter'; -import Meta from 'gi://Meta'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; -import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import * as Layout from 'resource:///org/gnome/shell/ui/layout.js'; -import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; -import {SETTINGS} from './extension.js'; -import {SecondaryMonitorDisplay, WorkspacesView} from 'resource:///org/gnome/shell/ui/workspacesView.js'; - - -export const PanelManager = class { - - constructor() { - this.overview = new Overview.Overview(); - this.panelsElementPositions = {}; - this._injectionManager = new InjectionManager(); - - this._saveMonitors(); - } - - enable(reset) { - this.allPanels = []; - this.dtpPrimaryMonitor = Main.layoutManager.primaryMonitor; // The real primary monitor should always have the main panel - this.proximityManager = new Proximity.ProximityManager(); - - if (this.dtpPrimaryMonitor) { - this.primaryPanel = this._createPanel(this.dtpPrimaryMonitor, SETTINGS.get_boolean('stockgs-keep-top-panel')); - this.allPanels.push(this.primaryPanel); - this.overview.enable(this.primaryPanel); - - this.setFocusedMonitor(this.dtpPrimaryMonitor); - } - - if (SETTINGS.get_boolean('multi-monitors')) { - Main.layoutManager.monitors.filter(m => m != this.dtpPrimaryMonitor).forEach(m => { - this.allPanels.push(this._createPanel(m, true)); - }); - } - - global.zorinTaskbar.panels = this.allPanels; - global.zorinTaskbar.emit('panels-created'); - - this.allPanels.forEach(p => { - let panelPosition = p.getPosition(); - let leftOrRight = (panelPosition == St.Side.LEFT || panelPosition == St.Side.RIGHT); - - p.panelBox.set_size( - leftOrRight ? -1 : p.geom.w + p.geom.lrPadding, - leftOrRight ? p.geom.h + p.geom.tbPadding : -1 - ); - - this._findPanelMenuButtons(p.panelBox).forEach(pmb => this._adjustPanelMenuButton(pmb, p.monitor, panelPosition)); - - p.taskbar.iconAnimator.start(); - }); - - this._setDesktopIconsMargins(); - //in 3.32, BoxPointer now inherits St.Widget - if (BoxPointer.BoxPointer.prototype.vfunc_get_preferred_height) { - let panelManager = this; - - this._injectionManager.overrideMethod(BoxPointer.BoxPointer.prototype, 'vfunc_get_preferred_height', () => function(forWidth) { - let alloc = { min_size: 0, natural_size: 0 }; - - [alloc.min_size, alloc.natural_size] = this.vfunc_get_preferred_height(forWidth); - - return panelManager._getBoxPointerPreferredHeight(this, alloc); - }); - } - - this._updatePanelElementPositions(); - - if (reset) return; - - this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass(); - - this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier; - Main.layoutManager._updatePanelBarrier = (panel) => { - let panelUpdates = panel ? [panel] : this.allPanels; - - panelUpdates.forEach(p => newUpdatePanelBarrier.call(Main.layoutManager, p)); - }; - Main.layoutManager._updatePanelBarrier(); - - this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners; - Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind(Main.layoutManager); - Main.layoutManager._updateHotCorners(); - - if (Main.layoutManager._interfaceSettings) { - this._enableHotCornersId = Main.layoutManager._interfaceSettings.connect('changed::enable-hot-corners', () => Main.layoutManager._updateHotCorners()); - } - - this._oldUpdateWorkspacesViews = Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews; - Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._newUpdateWorkspacesViews.bind(Main.overview._overview._controls._workspacesDisplay); - - this._oldSetPrimaryWorkspaceVisible = Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible - Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._newSetPrimaryWorkspaceVisible.bind(Main.overview._overview._controls._workspacesDisplay); - - LookingGlass.LookingGlass.prototype._oldResize = LookingGlass.LookingGlass.prototype._resize; - LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize; - - LookingGlass.LookingGlass.prototype._oldOpen = LookingGlass.LookingGlass.prototype.open; - LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - - //listen settings - this._signalsHandler.add( - [ - SETTINGS, - [ - 'changed::multi-monitors', - 'changed::isolate-monitors', - 'changed::panel-positions', - 'changed::panel-lengths', - 'changed::panel-anchors', - 'changed::stockgs-keep-top-panel' - ], - () => this._reset() - ], - [ - SETTINGS, - 'changed::panel-element-positions', - () => this._updatePanelElementPositions() - ], - [ - SETTINGS, - 'changed::intellihide-key-toggle-text', - () => this._setKeyBindings(true) - ], - [ - SETTINGS, - 'changed::panel-sizes', - () => { - GLib.idle_add(GLib.PRIORITY_LOW, () => { - this._setDesktopIconsMargins(); - return GLib.SOURCE_REMOVE; - }); - } - ], - [ - Utils.DisplayWrapper.getMonitorManager(), - 'monitors-changed', - () => { - if (Main.layoutManager.primaryMonitor) { - this._saveMonitors(); - this._reset(); - } - } - ] - ); - - Panel.panelBoxes.forEach(c => this._signalsHandler.add( - [ - Main.panel[c], - 'child-added', - (parent, child) => { - this.primaryPanel && - child instanceof St.Bin && - this._adjustPanelMenuButton(this._getPanelMenuButton(child.get_first_child()), this.primaryPanel.monitor, this.primaryPanel.getPosition()) - } - ] - )); - - this._setKeyBindings(true); - - // keep GS overview.js from blowing away custom panel styles - if(!SETTINGS.get_boolean('stockgs-keep-top-panel')) - Object.defineProperty(Main.panel, "style", {configurable: true, set(v) {}}); - } - - disable(reset) { - this.primaryPanel && this.overview.disable(); - this.proximityManager.destroy(); - - this.allPanels.forEach(p => { - p.taskbar.iconAnimator.pause(); - - this._findPanelMenuButtons(p.panelBox).forEach(pmb => { - if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { - pmb.menu._boxPointer._container.disconnect(pmb.menu._boxPointer._dtpGetPreferredHeightId); - } - - pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor; - delete pmb.menu._boxPointer._dtpSourceActor; - pmb.menu._boxPointer._userArrowSide = St.Side.TOP; - }) - - this._removePanelBarriers(p); - - p.disable(); - - let clipContainer = p.panelBox.get_parent(); - - Main.layoutManager._untrackActor(p.panelBox); - Main.layoutManager.removeChrome(clipContainer); - - if (p.isStandalone) { - p.panelBox.destroy(); - } else { - p.panelBox.remove_child(p); - p.remove_child(p.panel); - p.panelBox.add_child(p.panel); - - p.panelBox.set_position(clipContainer.x, clipContainer.y); - - clipContainer.remove_child(p.panelBox); - Main.layoutManager.addChrome(p.panelBox, { affectsStruts: true, trackFullscreen: true }); - } - }); - - this._injectionManager.clear(); - - if (Main.layoutManager.primaryMonitor) { - Main.layoutManager.panelBox.set_position(Main.layoutManager.primaryMonitor.x, Main.layoutManager.primaryMonitor.y); - Main.layoutManager.panelBox.set_size(Main.layoutManager.primaryMonitor.width, -1); - } - - if (reset) return; - - this._setKeyBindings(false); - - this._signalsHandler.destroy(); - - Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners; - Main.layoutManager._updateHotCorners(); - - if (this._enableHotCornersId) { - Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId); - } - - Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier; - Main.layoutManager._updatePanelBarrier(); - - Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = this._oldUpdateWorkspacesViews; - Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = this._oldSetPrimaryWorkspaceVisible; - - LookingGlass.LookingGlass.prototype._resize = LookingGlass.LookingGlass.prototype._oldResize; - delete LookingGlass.LookingGlass.prototype._oldResize; - - LookingGlass.LookingGlass.prototype.open = LookingGlass.LookingGlass.prototype._oldOpen; - delete LookingGlass.LookingGlass.prototype._oldOpen - - delete Main.panel.style; - this._desktopIconsUsableArea.destroy(); - this._desktopIconsUsableArea = null; - } - - toggleDash() { - this.overview.toggleDash(); - } - - _setDesktopIconsMargins() { - this._desktopIconsUsableArea?.resetMargins(); - this.allPanels.forEach(p => { - switch(p.geom.position) { - case St.Side.TOP: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, p.geom.h, 0, 0, 0); - break; - case St.Side.BOTTOM: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, p.geom.h, 0, 0); - break; - case St.Side.LEFT: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, p.geom.w, 0); - break; - case St.Side.RIGHT: - this._desktopIconsUsableArea?.setMargins(p.monitor.index, 0, 0, 0, p.geom.w); - break; - } - }); - } - - setFocusedMonitor(monitor) { - this.focusedMonitorPanel = this.allPanels.find(p => p.monitor == monitor) - - if (!this.checkIfFocusedMonitor(monitor)) { - Main.overview._overview.clear_constraints(); - Main.overview._overview.add_constraint(new Layout.MonitorConstraint({ index: monitor.index })); - - Main.overview._overview._controls._workspacesDisplay._primaryIndex = monitor.index; - } - } - - _newSetPrimaryWorkspaceVisible(visible) { - if (this._primaryVisible === visible) - return; - - this._primaryVisible = visible; - - const primaryIndex = Main.overview._overview._controls._workspacesDisplay._primaryIndex; - const primaryWorkspace = this._workspacesViews[primaryIndex]; - if (primaryWorkspace) - primaryWorkspace.visible = visible; - } - - _newUpdateWorkspacesViews() { - for (let i = 0; i < this._workspacesViews.length; i++) - this._workspacesViews[i].destroy(); - - this._workspacesViews = []; - let monitors = Main.layoutManager.monitors; - for (let i = 0; i < monitors.length; i++) { - let view; - if (i === this._primaryIndex) { - view = new WorkspacesView(i, - this._controls, - this._scrollAdjustment, - this._fitModeAdjustment, - this._overviewAdjustment); - - view.visible = this._primaryVisible; - this.bind_property('opacity', view, 'opacity', GObject.BindingFlags.SYNC_CREATE); - this.add_child(view); - } else { - // No idea why atm, but we need the import at the top of this file and to use the - // full imports ns here, otherwise SecondaryMonitorDisplay can't be used ¯\_(ツ)_/¯ - view = new SecondaryMonitorDisplay(i, - this._controls, - this._scrollAdjustment, - this._fitModeAdjustment, - this._overviewAdjustment); - Main.layoutManager.overviewGroup.add_child(view); - } - - this._workspacesViews.push(view); - } - } - - _saveMonitors() { - //Mutter meta_monitor_manager_get_primary_monitor (global.display.get_primary_monitor()) doesn't return the same - //monitor as GDK gdk_screen_get_primary_monitor (imports.gi.Gdk.Screen.get_default().get_primary_monitor()). - //Since the Mutter function is what's used in gnome-shell and we can't access it from the settings dialog, store - //the monitors information in a setting so we can use the same monitor indexes as the ones in gnome-shell - let keyMonitors = 'available-monitors'; - let primaryIndex = Main.layoutManager.primaryIndex; - let newMonitors = [primaryIndex]; - - Main.layoutManager.monitors.filter(m => m.index != primaryIndex).forEach(m => newMonitors.push(m.index)); - - SETTINGS.set_value(keyMonitors, new GLib.Variant('ai', newMonitors)); - } - - checkIfFocusedMonitor(monitor) { - return Main.overview._overview._controls._workspacesDisplay._primaryIndex == monitor.index; - } - - _createPanel(monitor, isStandalone) { - let panelBox; - let panel; - let clipContainer = new Clutter.Actor(); - - if (isStandalone) { - panelBox = new St.BoxLayout({ name: 'panelBox' }); - } else { - panelBox = Main.layoutManager.panelBox; - Main.layoutManager._untrackActor(panelBox); - panelBox.remove_child(Main.panel); - Main.layoutManager.removeChrome(panelBox); - } - - Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }); - clipContainer.add_child(panelBox); - Main.layoutManager.trackChrome(panelBox, { trackFullscreen: true, affectsStruts: true, affectsInputRegion: true }); - - panel = new Panel.Panel(this, monitor, panelBox, isStandalone); - panelBox.add_child(panel); - panel.enable(); - - panelBox.visible = true; - if (monitor.inFullscreen) { - panelBox.hide(); - } - panelBox.set_position(0, 0); - - return panel; - } - - _reset() { - this.disable(true); - this.allPanels = []; - this.enable(true); - } - - _updatePanelElementPositions() { - this.panelsElementPositions = PanelSettings.getSettingsJson(SETTINGS, 'panel-element-positions'); - this.allPanels.forEach(p => p.updateElementPositions()); - } - - _adjustPanelMenuButton(button, monitor, arrowSide) { - if (button) { - button.menu._boxPointer._dtpSourceActor = button.menu._boxPointer.sourceActor; - button.menu._boxPointer.sourceActor = button; - button.menu._boxPointer._userArrowSide = arrowSide; - button.menu._boxPointer._dtpInPanel = 1; - - if (!button.menu._boxPointer.vfunc_get_preferred_height) { - button.menu._boxPointer._dtpGetPreferredHeightId = button.menu._boxPointer._container.connect('get-preferred-height', (actor, forWidth, alloc) => { - this._getBoxPointerPreferredHeight(button.menu._boxPointer, alloc, monitor); - }); - } - } - } - - _getBoxPointerPreferredHeight(boxPointer, alloc, monitor) { - if (boxPointer._dtpInPanel && boxPointer.sourceActor && SETTINGS.get_boolean('intellihide')) { - monitor = monitor || Main.layoutManager.findMonitorForActor(boxPointer.sourceActor); - let panel = Utils.find(global.zorinTaskbar.panels, p => p.monitor == monitor); - let excess = alloc.natural_size + panel.dtpSize + 10 - monitor.height; // 10 is arbitrary - - if (excess > 0) { - alloc.natural_size -= excess; - } - } - - return [alloc.min_size, alloc.natural_size]; - } - - _findPanelMenuButtons(container) { - let panelMenuButtons = []; - let panelMenuButton; - - let find = parent => parent.get_children().forEach(c => { - if ((panelMenuButton = this._getPanelMenuButton(c))) { - panelMenuButtons.push(panelMenuButton); - } - - find(c); - }); - - find(container); - - return panelMenuButtons; - } - - _removePanelBarriers(panel) { - if (panel.isStandalone && panel._rightPanelBarrier) { - panel._rightPanelBarrier.destroy(); - } - - if (panel._leftPanelBarrier) { - panel._leftPanelBarrier.destroy(); - delete panel._leftPanelBarrier; - } - } - - _getPanelMenuButton(obj) { - return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0; - } - - _setKeyBindings(enable) { - let keys = { - 'intellihide-key-toggle': () => this.allPanels.forEach(p => p.intellihide.toggle()) - }; - - Object.keys(keys).forEach(k => { - Utils.removeKeybinding(k); - - if (enable) { - Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL); - } - }); - } - -}; - -// This class drives long-running icon animations, to keep them running in sync -// with each other. -export const IconAnimator = class { - - constructor(actor) { - this._count = 0; - this._started = false; - this._animations = { - dance: [], - }; - this._timeline = new Clutter.Timeline({ - duration: 3000, - repeat_count: -1, - }); - - /* Just use the construction property when no need to support 3.36 */ - if (this._timeline.set_actor) - this._timeline.set_actor(actor); - - this._timeline.connect('new-frame', () => { - const progress = this._timeline.get_progress(); - const danceRotation = progress < 1/6 ? 15*Math.sin(progress*24*Math.PI) : 0; - const dancers = this._animations.dance; - for (let i = 0, iMax = dancers.length; i < iMax; i++) { - dancers[i].target.rotation_angle_z = danceRotation; - } - }); - } - - destroy() { - this._timeline.stop(); - this._timeline = null; - for (let name in this._animations) { - const pairs = this._animations[name]; - for (let i = 0, iMax = pairs.length; i < iMax; i++) { - const pair = pairs[i]; - pair.target.disconnect(pair.targetDestroyId); - } - } - this._animations = null; - } - - pause() { - if (this._started && this._count > 0) { - this._timeline.stop(); - } - this._started = false; - } - - start() { - if (!this._started && this._count > 0) { - this._timeline.start(); - } - this._started = true; - } - - addAnimation(target, name) { - const targetDestroyId = target.connect('destroy', () => this.removeAnimation(target, name)); - this._animations[name].push({ target: target, targetDestroyId: targetDestroyId }); - if (this._started && this._count === 0) { - this._timeline.start(); - } - this._count++; - } - - removeAnimation(target, name) { - const pairs = this._animations[name]; - for (let i = 0, iMax = pairs.length; i < iMax; i++) { - const pair = pairs[i]; - if (pair.target === target) { - target.disconnect(pair.targetDestroyId); - pairs.splice(i, 1); - this._count--; - if (this._started && this._count === 0) { - this._timeline.stop(); - } - return; - } - } - } -}; - -function newUpdateHotCorners() { - // destroy old hot corners - this.hotCorners.forEach(function(corner) { - if (corner) - corner.destroy(); - }); - this.hotCorners = []; - - //global.settings is ubuntu specific setting to disable the hot corner (Tweak tool > Top Bar > Activities Overview Hot Corner) - //this._interfaceSettings is for the setting to disable the hot corner introduced in gnome-shell 3.34 - if ((global.settings.list_keys().indexOf('enable-hot-corners') >= 0 && !global.settings.get_boolean('enable-hot-corners')) || - (this._interfaceSettings && !this._interfaceSettings.get_boolean('enable-hot-corners'))) { - this.emit('hot-corners-changed'); - return; - } - - // build new hot corners - for (let i = 0; i < this.monitors.length; i++) { - let panel = Utils.find(global.zorinTaskbar.panels, p => p.monitor.index == i); - let panelPosition = panel ? panel.getPosition() : St.Side.BOTTOM; - let panelTopLeft = panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT; - let monitor = this.monitors[i]; - let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; - let cornerY = monitor.y; - - let haveTopLeftCorner = true; - - // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft - // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse - // as you are dragging across. In the future, maybe we will automatically move the - // hotcorner to the bottom when the panel is positioned at the bottom - if (i != this.primaryIndex || !panelTopLeft) { - // Check if we have a top left (right for RTL) corner. - // I.e. if there is no monitor directly above or to the left(right) - let besideX = this._rtl ? monitor.x + 1 : cornerX - 1; - let besideY = cornerY; - let aboveX = cornerX; - let aboveY = cornerY - 1; - - for (let j = 0; j < this.monitors.length; j++) { - if (i == j) - continue; - let otherMonitor = this.monitors[j]; - if (besideX >= otherMonitor.x && - besideX < otherMonitor.x + otherMonitor.width && - besideY >= otherMonitor.y && - besideY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - if (aboveX >= otherMonitor.x && - aboveX < otherMonitor.x + otherMonitor.width && - aboveY >= otherMonitor.y && - aboveY < otherMonitor.y + otherMonitor.height) { - haveTopLeftCorner = false; - break; - } - } - } - - if (haveTopLeftCorner) { - let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY); - - corner.setBarrierSize = size => Object.getPrototypeOf(corner).setBarrierSize.call(corner, Math.min(size, 32)); - corner.setBarrierSize(panel ? panel.dtpSize : 32); - this.hotCorners.push(corner); - } else { - this.hotCorners.push(null); - } - } - - this.emit('hot-corners-changed'); -} - -function newUpdatePanelBarrier(panel) { - let barriers = { - _rightPanelBarrier: [(panel.isStandalone ? panel : this)], - _leftPanelBarrier: [panel] - }; - - Object.keys(barriers).forEach(k => { - let obj = barriers[k][0]; - - if (obj[k]) { - obj[k].destroy(); - obj[k] = null; - } - }); - - if (!this.primaryMonitor || !panel.panelBox.height) { - return; - } - - let barrierSize = Math.min(10, panel.panelBox.height); - let fixed1 = panel.monitor.y; - let fixed2 = panel.monitor.y + barrierSize; - - if (panel.checkIfVertical()) { - barriers._rightPanelBarrier.push(panel.monitor.y + panel.monitor.height, Meta.BarrierDirection.NEGATIVE_Y); - barriers._leftPanelBarrier.push(panel.monitor.y, Meta.BarrierDirection.POSITIVE_Y); - } else { - barriers._rightPanelBarrier.push(panel.monitor.x + panel.monitor.width, Meta.BarrierDirection.NEGATIVE_X); - barriers._leftPanelBarrier.push(panel.monitor.x, Meta.BarrierDirection.POSITIVE_X); - } - - switch (panel.getPosition()) { - //values are initialized as St.Side.TOP - case St.Side.BOTTOM: - fixed1 = panel.monitor.y + panel.monitor.height - barrierSize; - fixed2 = panel.monitor.y + panel.monitor.height; - break; - case St.Side.LEFT: - fixed1 = panel.monitor.x + barrierSize; - fixed2 = panel.monitor.x; - break; - case St.Side.RIGHT: - fixed1 = panel.monitor.x + panel.monitor.width - barrierSize; - fixed2 = panel.monitor.x + panel.monitor.width; - break; - } - - //remove left barrier if it overlaps one of the hotcorners - for (let k in this.hotCorners) { - let hc = this.hotCorners[k]; - - if (hc && hc._monitor == panel.monitor && - ((fixed1 == hc._x || fixed2 == hc._x) || fixed1 == hc._y || fixed2 == hc._y)) { - delete barriers._leftPanelBarrier; - break; - } - } - - Object.keys(barriers).forEach(k => { - let barrierOptions = { - backend: global.backend, - directions: barriers[k][2] - }; - - barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = barriers[k][1]; - barrierOptions[panel.fixedCoord.c1] = fixed1; - barrierOptions[panel.fixedCoord.c2] = fixed2; - - barriers[k][0][k] = new Meta.Barrier(barrierOptions); - }); -} - -function _newLookingGlassResize() { - let primaryMonitorPanel = Utils.find(global.zorinTaskbar.panels, p => p.monitor == Main.layoutManager.primaryMonitor); - let topOffset = primaryMonitorPanel.getPosition() == St.Side.TOP ? primaryMonitorPanel.dtpSize + 8 : 32; - - this._oldResize(); - - this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height; - this._targetY = this._hiddenY + this.height; - this.y = this._hiddenY; - - this._objInspector.set_position(this.x + Math.floor(this.width * 0.1), this._targetY + Math.floor(this.height * 0.1)); -} - -function _newLookingGlassOpen() { - if (this._open) - return; - - this._resize(); - this._oldOpen(); -} diff --git a/panelPositions.js b/panelPositions.js deleted file mode 100644 index 6a4c79a..0000000 --- a/panelPositions.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Panel extension - */ - -export const SHOW_APPS_BTN = 'showAppsButton'; -export const ACTIVITIES_BTN = 'activitiesButton'; -export const TASKBAR = 'taskbar'; -export const DATE_MENU = 'dateMenu'; -export const SYSTEM_MENU = 'systemMenu'; -export const LEFT_BOX = 'leftBox'; -export const CENTER_BOX = 'centerBox'; -export const RIGHT_BOX = 'rightBox'; -export const DESKTOP_BTN = 'desktopButton'; - -export const STACKED_TL = 'stackedTL'; -export const STACKED_BR = 'stackedBR'; -export const CENTERED = 'centered'; -export const CENTERED_MONITOR = 'centerMonitor'; - -export const TOP = 'TOP'; -export const BOTTOM = 'BOTTOM'; -export const LEFT = 'LEFT'; -export const RIGHT = 'RIGHT'; - -export const START = 'START'; -export const MIDDLE = 'MIDDLE'; -export const END = 'END'; - -export const defaults = [ - { element: LEFT_BOX, visible: true, position: STACKED_TL }, - { element: SHOW_APPS_BTN, visible: false, position: STACKED_TL }, - { element: ACTIVITIES_BTN, visible: true, position: STACKED_TL }, - { element: TASKBAR, visible: true, position: STACKED_TL }, - { element: CENTER_BOX, visible: true, position: STACKED_BR }, - { element: RIGHT_BOX, visible: true, position: STACKED_BR }, - { element: SYSTEM_MENU, visible: true, position: STACKED_BR }, - { element: DATE_MENU, visible: true, position: STACKED_BR }, - { element: DESKTOP_BTN, visible: false, position: STACKED_BR }, -]; - -export const optionDialogFunctions = {}; - -optionDialogFunctions[DATE_MENU] = '_showDateMenuOptions'; -optionDialogFunctions[DESKTOP_BTN] = '_showDesktopButtonOptions'; - -export function checkIfCentered(position) { - return position == CENTERED || position == CENTERED_MONITOR; -} diff --git a/panelSettings.js b/panelSettings.js deleted file mode 100644 index d00af83..0000000 --- a/panelSettings.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import * as Pos from './panelPositions.js'; - -/** Return object representing a settings value that is stored as JSON. */ -export function getSettingsJson(settings, setting) { - try { - return JSON.parse(settings.get_string(setting)); - } catch(e) { - log('Error parsing positions: ' + e.message); - } -} -/** Write value object as JSON to setting in settings. */ -export function setSettingsJson(settings, setting, value) { - try { - const json = JSON.stringify(value); - settings.set_string(setting, json); - } catch(e) { - log('Error serializing setting: ' + e.message); - } -} - -/** Returns size of panel on a specific monitor, in pixels. */ -export function getPanelSize(settings, monitorIndex) { - const sizes = getSettingsJson(settings, 'panel-sizes'); - // Pull in deprecated setting if panel-sizes does not have setting for monitor. - const fallbackSize = settings.get_int('panel-size'); - const theDefault = 48; - return sizes[monitorIndex] || fallbackSize || theDefault; -} - -export function setPanelSize(settings, monitorIndex, value) { - if (!(Number.isInteger(value) && value <= 128 && value >= 16)) { - log('Not setting invalid panel size: ' + value); - return; - } - let sizes = getSettingsJson(settings, 'panel-sizes'); - sizes[monitorIndex] = value; - setSettingsJson(settings, 'panel-sizes', sizes); -} - -/** - * Returns length of panel on a specific monitor, as a whole number percent, - * from settings. e.g. 100 - */ -export function getPanelLength(settings, monitorIndex) { - const lengths = getSettingsJson(settings, 'panel-lengths'); - const theDefault = 100; - return lengths[monitorIndex] || theDefault; -} - -export function setPanelLength(settings, monitorIndex, value) { - if (!(Number.isInteger(value) && value <= 100 && value >= 0)) { - log('Not setting invalid panel length: ' + value); - return; - } - let lengths = getSettingsJson(settings, 'panel-lengths'); - lengths[monitorIndex] = value; - setSettingsJson(settings, 'panel-lengths', lengths); -} - -/** Returns position of panel on a specific monitor. */ -export function getPanelPosition(settings, monitorIndex) { - const positions = getSettingsJson(settings, 'panel-positions'); - const fallbackPosition = settings.get_string('panel-position'); - const theDefault = Pos.BOTTOM; - return positions[monitorIndex] || fallbackPosition || theDefault; -} - -export function setPanelPosition(settings, monitorIndex, value) { - if (!(value === Pos.TOP || value === Pos.BOTTOM || value === Pos.LEFT - || value === Pos.RIGHT)) { - log('Not setting invalid panel position: ' + value); - return; - } - const positions = getSettingsJson(settings, 'panel-positions'); - positions[monitorIndex] = value; - setSettingsJson(settings, 'panel-positions', positions); -} - -/** Returns anchor location of panel on a specific monitor. */ -export function getPanelAnchor(settings, monitorIndex) { - const anchors = getSettingsJson(settings, 'panel-anchors'); - const theDefault = Pos.MIDDLE; - return anchors[monitorIndex] || theDefault; -} - -export function setPanelAnchor(settings, monitorIndex, value) { - if (!(value === Pos.START || value === Pos.MIDDLE || value === Pos.END)) { - log('Not setting invalid panel anchor: ' + value); - return; - } - const anchors = getSettingsJson(settings, 'panel-anchors'); - anchors[monitorIndex] = value; - setSettingsJson(settings, 'panel-anchors', anchors); -} diff --git a/panelStyle.js b/panelStyle.js deleted file mode 100644 index 8b1b33f..0000000 --- a/panelStyle.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Panel extension - * Ideas for recursing child actors and assigning inline styles - * are based on code from the StatusAreaHorizontalSpacing extension - * https://bitbucket.org/mathematicalcoffee/status-area-horizontal-spacing-gnome-shell-extension - * mathematical.coffee@gmail.com - */ - -import * as Utils from './utils.js'; - -export const PanelStyle = class { - - enable(panel) { - this.panel = panel; - - this._applyStyles(); - } - - disable() { - this._removeStyles(); - } - - _applyStyles() { - this._rightBoxOperations = []; - - // center box has been moved next to the right box and will be treated the same - this._centerBoxOperations = this._rightBoxOperations; - - this._leftBoxOperations = []; - - this._applyStylesRecursively(); - - /* connect signal */ - this._rightBoxActorAddedID = this.panel._rightBox.connect('child-added', - (container, actor) => { - if(this._rightBoxOperations.length && !this._ignoreAddedChild) - this._recursiveApply(actor, this._rightBoxOperations); - - this._ignoreAddedChild = 0; - } - ); - this._centerBoxActorAddedID = this.panel._centerBox.connect('child-added', - (container, actor) => { - if(this._centerBoxOperations.length && !this._ignoreAddedChild) - this._recursiveApply(actor, this._centerBoxOperations); - - this._ignoreAddedChild = 0; - } - ); - this._leftBoxActorAddedID = this.panel._leftBox.connect('child-added', - (container, actor) => { - if(this._leftBoxOperations.length) - this._recursiveApply(actor, this._leftBoxOperations); - } - ); - } - - _removeStyles() { - /* disconnect signal */ - if (this._rightBoxActorAddedID) - this.panel._rightBox.disconnect(this._rightBoxActorAddedID); - if (this._centerBoxActorAddedID) - this.panel._centerBox.disconnect(this._centerBoxActorAddedID); - if (this._leftBoxActorAddedID) - this.panel._leftBox.disconnect(this._leftBoxActorAddedID); - - this._restoreOriginalStyle(this.panel._rightBox); - this._restoreOriginalStyle(this.panel._centerBox); - this._restoreOriginalStyle(this.panel._leftBox); - - this._applyStylesRecursively(true); - } - - _applyStylesRecursively(restore) { - /*recurse actors */ - if(this._rightBoxOperations.length) { - // add the system menu as we move it from the rightbox to the panel to position it independently - let children = this.panel._rightBox.get_children().concat([this.panel.statusArea[Utils.getSystemMenuInfo().name].container]); - for(let i in children) - this._recursiveApply(children[i], this._rightBoxOperations, restore); - } - - if(this._centerBoxOperations.length) { - // add the date menu as we move it from the centerbox to the panel to position it independently - let children = this.panel._centerBox.get_children().concat([this.panel.statusArea.dateMenu.container]); - for(let i in children) - this._recursiveApply(children[i], this._centerBoxOperations, restore); - } - - if(this._leftBoxOperations.length) { - let children = this.panel._leftBox.get_children(); - for(let i in children) - this._recursiveApply(children[i], this._leftBoxOperations, restore); - } - } - - _recursiveApply(actor, operations, restore) { - for(let i in operations) { - let o = operations[i]; - if(o.compareFn(actor)) - if(restore) - o.restoreFn ? o.restoreFn(actor) : this._restoreOriginalStyle(actor); - else - o.applyFn(actor, i); - } - - if(actor.get_children) { - let children = actor.get_children(); - for(let i in children) { - this._recursiveApply(children[i], operations, restore); - } - } - } - - _restoreOriginalStyle(actor) { - if (actor._dtp_original_inline_style !== undefined) { - actor.set_style(actor._dtp_original_inline_style); - delete actor._dtp_original_inline_style; - delete actor._dtp_style_overrides; - } - - if (actor.has_style_class_name('panel-button')) { - this._refreshPanelButton(actor); - } - } - - _refreshPanelButton(actor) { - if (actor.visible) { - //force gnome 3.34+ to refresh (having problem with the -natural-hpadding) - let parent = actor.get_parent(); - let children = parent.get_children(); - let actorIndex = 0; - - if (children.length > 1) { - actorIndex = children.indexOf(actor); - } - - this._ignoreAddedChild = [this.panel._centerBox, this.panel._rightBox].indexOf(parent) >= 0; - - parent.remove_child(actor); - parent.insert_child_at_index(actor, actorIndex); - } - } - -} diff --git a/po/af.po b/po/af.po index 6353ad4..a13b23b 100644 --- a/po/af.po +++ b/po/af.po @@ -18,6 +18,3 @@ msgstr "Wys Rekenaar-Ikoon" msgid "Show Desktop button padding (px)" msgstr "Wys Rekenaar-Ikoon vulling (px)" - -msgid "Floating rounded theme" -msgstr "Swewende afgeronde tema" diff --git a/po/ar.po b/po/ar.po index 9a6a5a0..bb4805b 100644 --- a/po/ar.po +++ b/po/ar.po @@ -22,8 +22,5 @@ msgstr "أظهر أيقونات سطح المكتب" msgid "Show Desktop button padding (px)" msgstr "أظهر زر سطح المكتب المساحة المتروكة (بيكسل)" -msgid "Floating rounded theme" -msgstr "مظهر دائري عائم" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "عزل مساحات العمل والشاشات في إعدادات تبديل التطبيقات" diff --git a/po/be.po b/po/be.po index c185a70..3cb2c86 100644 --- a/po/be.po +++ b/po/be.po @@ -21,6 +21,3 @@ msgstr "Паказаць значок працоўнага стала" msgid "Show Desktop button padding (px)" msgstr "Пракладка Паказаць кнопку працоўнага стала (px)" - -msgid "Floating rounded theme" -msgstr "Плаваючая круглявая тэма" diff --git a/po/bg.po b/po/bg.po index 179f7a7..79a588b 100644 --- a/po/bg.po +++ b/po/bg.po @@ -21,6 +21,3 @@ msgstr "Показване на икона на работния плот" msgid "Show Desktop button padding (px)" msgstr "Бутон Покажи работния плот - подложка (px)" - -msgid "Floating rounded theme" -msgstr "Плаваща заоблена тема" diff --git a/po/bn.po b/po/bn.po index 3da79f3..f771f8f 100644 --- a/po/bn.po +++ b/po/bn.po @@ -18,6 +18,3 @@ msgstr "ডেস্কটপ আইকন দেখান" msgid "Show Desktop button padding (px)" msgstr "Show Desktop button প্যাডিং (px আকারে)" - -msgid "Floating rounded theme" -msgstr "ভাসমান বৃত্তাকার থিম" diff --git a/po/ca.po b/po/ca.po index b629d33..00aae2d 100644 --- a/po/ca.po +++ b/po/ca.po @@ -21,6 +21,3 @@ msgstr "Mostrar l'icona de l'Escriptori" msgid "Show Desktop button padding (px)" msgstr "Farciment Mostra el botó de l'escriptori (px)" - -msgid "Floating rounded theme" -msgstr "Tema flotant arrodonit" diff --git a/po/cs.po b/po/cs.po index 94e3c78..9b9dc22 100644 --- a/po/cs.po +++ b/po/cs.po @@ -1412,9 +1412,6 @@ msgstr "Zobrazit ikonu plochy" msgid "Show Desktop button padding (px)" msgstr "vybarvení tlačítka Zobraz plochu" -msgid "Floating rounded theme" -msgstr "Plovoucí zaoblený vzhled" - #~ msgid "Top, with plugin icons collapsed to bottom" #~ msgstr "Nahoře, ikony dole" diff --git a/po/da.po b/po/da.po index 11aaffc..bb612c3 100644 --- a/po/da.po +++ b/po/da.po @@ -21,6 +21,3 @@ msgstr "Vis skrivebords ikon" msgid "Show Desktop button padding (px)" msgstr "Vis skrivebords knap afstand (px)" - -msgid "Floating rounded theme" -msgstr "Flydende afrundet tema" diff --git a/po/de.po b/po/de.po index 3fe146c..3b7a541 100644 --- a/po/de.po +++ b/po/de.po @@ -1479,9 +1479,6 @@ msgstr "Desktop-Symbol anzeigen" msgid "Show Desktop button padding (px)" msgstr "Padding (px) für Desktop-Symbol anzeigen" -msgid "Floating rounded theme" -msgstr "Schwebendes abgerundetes Thema" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isolieren Sie Arbeitsbereiche und Monitore in den Anwendungswechseleinstellungen" diff --git a/po/el.po b/po/el.po index ba3ee8f..b02cd6e 100644 --- a/po/el.po +++ b/po/el.po @@ -21,6 +21,3 @@ msgstr "Εμφάνιση επιφάνειας εργασίας" msgid "Show Desktop button padding (px)" msgstr "Εμφάνισε διαχωριστικό" - -msgid "Floating rounded theme" -msgstr "Κυμαινόμενο στρογγυλεμένο θέμα" diff --git a/po/es.po b/po/es.po index 20b7d2b..a2441fa 100644 --- a/po/es.po +++ b/po/es.po @@ -1507,9 +1507,6 @@ msgstr "Mostrar Escritorio - ícono" msgid "Show Desktop button padding (px)" msgstr "Mostrar Escritorio - espaciado del botón (px)" -msgid "Floating rounded theme" -msgstr "Tema flotante redondeado" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Aislar espacios de trabajo y monitores en la configuración de Cambio de aplicaciones" diff --git a/po/et.po b/po/et.po index 339ed56..36b3692 100644 --- a/po/et.po +++ b/po/et.po @@ -21,6 +21,3 @@ msgstr "Kuva töölauaikoon" msgid "Show Desktop button padding (px)" msgstr "Täitmine Kuva töölaua nupp (px)" - -msgid "Floating rounded theme" -msgstr "Ujuv ümardatud teema" diff --git a/po/fa.po b/po/fa.po index a7268ec..9991070 100644 --- a/po/fa.po +++ b/po/fa.po @@ -1466,9 +1466,6 @@ msgstr "نمایش آیکون دسکتاپ" msgid "Show Desktop button padding (px)" msgstr "حاشیه‌ی دکمه‌ی نمایش دسکتاپ (px)" -msgid "Floating rounded theme" -msgstr "تم گرد و شناور" - #~ msgid "Show Details" #~ msgstr "نمایش جزییات" diff --git a/po/fi.po b/po/fi.po index da51cc9..8a87468 100644 --- a/po/fi.po +++ b/po/fi.po @@ -21,6 +21,3 @@ msgstr "Näytä työpöytä-kuvake" msgid "Show Desktop button padding (px)" msgstr "Näytä työpöytä painikkeen asettelu (px)" - -msgid "Floating rounded theme" -msgstr "Kelluva ja pyöristetty teema" diff --git a/po/fr.po b/po/fr.po index 12caeef..3903c2f 100644 --- a/po/fr.po +++ b/po/fr.po @@ -3,22 +3,22 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # Laurent Tréguier , 2019. -# Charles Gagnon , 2019-2023. +# Charles Gagnon , 2019-2025. # msgid "" msgstr "" -"Project-Id-Version: DAsh To PAnel\n" +"Project-Id-Version: Dash To Panel\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-09-29 15:58+0200\n" -"PO-Revision-Date: 2024-09-29 16:02+0200\n" -"Last-Translator: Delphin PETER\n" +"POT-Creation-Date: 2025-03-09 23:44-0400\n" +"PO-Revision-Date: 2025-03-09 23:48-0400\n" +"Last-Translator: Charles Gagnon \n" "Language-Team: French\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.4.2\n" +"X-Generator: Gtranslator 47.1\n" "X-Poedit-Basepath: ..\n" "X-Poedit-SearchPath-0: ui\n" "X-Poedit-SearchPath-1: windowPreview.js\n" @@ -39,498 +39,510 @@ msgstr "" "X-Poedit-SearchPath-16: desktopIconsIntegration.js\n" "X-Poedit-SearchPath-17: appIcons.js\n" -#: appIcons.js:1485 appIcons.js:1495 ui/BoxMiddleClickOptions.ui:32 -#: ui/BoxMiddleClickOptions.ui:60 ui/BoxMiddleClickOptions.ui:88 -msgid "Quit" -msgstr "Quitter" +#: src/extension.js:96 +msgid "Dash to Panel has been updated!" +msgstr "Dash to Panel a été mis à jour!" -#: appIcons.js:1497 -#, javascript-format -msgid "Quit %d Window" -msgid_plural "Quit %d Windows" -msgstr[0] "Fermer %d fenêtre" -msgstr[1] "Fermer %d fenêtres" +#: src/extension.js:97 +msgid "You are now running version" +msgstr "Vous utilisez maintenant la version" -#: appIcons.js:1772 -msgid "Power options" -msgstr "Options d'alimentation" +#: src/extension.js:103 +msgid "See what's new" +msgstr "Voir les nouveautés" -#: appIcons.js:1777 -msgid "Event logs" -msgstr "Journaux d'évènements" - -#: appIcons.js:1782 -msgid "System" -msgstr "Système" - -#: appIcons.js:1787 -msgid "Device Management" -msgstr "Gestionnaire de périphériques" - -#: appIcons.js:1792 -msgid "Disk Management" -msgstr "Gestionnaire de disques" - -#: appIcons.js:1805 -msgid "Terminal" -msgstr "Terminal" - -#: appIcons.js:1810 -msgid "System Monitor" -msgstr "Moniteur système" - -#: appIcons.js:1815 -msgid "Files" -msgstr "Fichiers" - -#: appIcons.js:1820 -msgid "Extensions" -msgstr "Extensions" - -#: appIcons.js:1825 -msgid "Settings" -msgstr "Paramètres" - -#: appIcons.js:1836 -msgid "Unlock taskbar" -msgstr "Déverrouiller la barre des tâches" - -#: appIcons.js:1836 -msgid "Lock taskbar" -msgstr "Verrouiller la barre des tâches" - -#: appIcons.js:1841 -msgid "Taskbar Settings" -msgstr "Paramètres de la barre des tâches" - -#: appIcons.js:1846 -msgid "Restore Windows" -msgstr "Restaurer les fenêtres" - -#: appIcons.js:1846 -msgid "Show Desktop" -msgstr "Afficher le bureau" - -#: panel.js:171 -msgid "Top Bar" -msgstr "Panneau du haut" - -#: prefs.js:246 +#: src/prefs.js:275 msgid "Show Desktop button height (px)" msgstr "Hauteur du bouton Afficher le Bureau (px)" -#: prefs.js:246 +#: src/prefs.js:276 msgid "Show Desktop button width (px)" msgstr "Longueur du bouton Afficher le Bureau (px)" -#: prefs.js:258 -msgid "Unavailable when gnome-shell top panel is present" -msgstr "Indisponible quand le panneau de gnome-shell est présent" - -#: prefs.js:316 ui/SettingsPosition.ui:148 ui/SettingsStyle.ui:149 +#: src/prefs.js:348 ui/SettingsPosition.ui.h:13 ui/SettingsStyle.ui.h:19 msgid "Left" msgstr "Gauche" -#: prefs.js:317 +#: src/prefs.js:349 msgid "Center" msgstr "Centre" -#: prefs.js:318 ui/SettingsPosition.ui:157 ui/SettingsStyle.ui:157 +#: src/prefs.js:350 ui/SettingsPosition.ui.h:14 ui/SettingsStyle.ui.h:20 msgid "Right" msgstr "Droit" -#: prefs.js:320 ui/BoxWindowPreviewOptions.ui:330 ui/SettingsPosition.ui:139 -#: ui/SettingsStyle.ui:141 +#: src/prefs.js:352 ui/BoxWindowPreviewOptions.ui.h:38 +#: ui/SettingsPosition.ui.h:12 ui/SettingsStyle.ui.h:18 msgid "Top" msgstr "Haut" -#: prefs.js:321 prefs.js:326 ui/SettingsPosition.ui:210 +#: src/prefs.js:353 src/prefs.js:358 ui/SettingsPosition.ui.h:22 msgid "Middle" msgstr "Milieu" -#: prefs.js:322 ui/BoxWindowPreviewOptions.ui:321 ui/SettingsPosition.ui:130 -#: ui/SettingsStyle.ui:134 +#: src/prefs.js:354 ui/BoxWindowPreviewOptions.ui.h:37 +#: ui/SettingsPosition.ui.h:11 ui/SettingsStyle.ui.h:17 msgid "Bottom" msgstr "Bas" -#: prefs.js:325 ui/SettingsPosition.ui:209 +#: src/prefs.js:357 ui/SettingsPosition.ui.h:21 msgid "Start" msgstr "Début" -#: prefs.js:327 ui/SettingsPosition.ui:211 +#: src/prefs.js:359 ui/SettingsPosition.ui.h:23 msgid "End" msgstr "Fin" -#: prefs.js:412 +#: src/prefs.js:484 msgid "Show Applications button" msgstr "Bouton Afficher les Applications" -#: prefs.js:413 +#: src/prefs.js:485 msgid "Activities button" msgstr "Bouton Activités" -#: prefs.js:414 +#: src/prefs.js:486 msgid "Taskbar" msgstr "Barre des tâches" -#: prefs.js:415 +#: src/prefs.js:487 msgid "Date menu" msgstr "Horloge" -#: prefs.js:416 +#: src/prefs.js:488 msgid "System menu" msgstr "Menu système" -#: prefs.js:417 +#: src/prefs.js:489 msgid "Left box" msgstr "Zone gauche" -#: prefs.js:418 +#: src/prefs.js:490 msgid "Center box" msgstr "Zone centre" -#: prefs.js:419 +#: src/prefs.js:491 msgid "Right box" msgstr "Zone droite" -#: prefs.js:420 +#: src/prefs.js:492 msgid "Desktop button" msgstr "Bouton Afficher le Bureau" -#: prefs.js:426 +#: src/prefs.js:502 src/prefs.js:2788 msgid "Move up" msgstr "Déplacer vers le haut" -#: prefs.js:428 +#: src/prefs.js:504 src/prefs.js:2796 msgid "Move down" msgstr "Déplacer vers le bas" -#: prefs.js:430 +#: src/prefs.js:510 msgid "Visible" msgstr "Visible" -#: prefs.js:431 +#: src/prefs.js:514 msgid "Select element position" msgstr "Sélectionner la position de l'élément" -#: prefs.js:442 +#: src/prefs.js:528 msgid "Stacked to top" msgstr "Empilé en haut" -#: prefs.js:442 +#: src/prefs.js:528 msgid "Stacked to left" msgstr "Empilé à gauche" -#: prefs.js:443 +#: src/prefs.js:532 msgid "Stacked to bottom" msgstr "Empilé en bas" -#: prefs.js:443 +#: src/prefs.js:532 msgid "Stacked to right" msgstr "Empilé à droite" -#: prefs.js:444 +#: src/prefs.js:534 msgid "Centered" msgstr "Centré" -#: prefs.js:445 +#: src/prefs.js:535 msgid "Monitor Center" msgstr "Centre de l'écran " -#: prefs.js:464 +#: src/prefs.js:562 msgid "More options" msgstr "Plus d'options" -#: prefs.js:496 +#: src/prefs.js:597 msgid "Reset to defaults" msgstr "Restaurer les paramètres par défaut" -#: prefs.js:519 +#: src/prefs.js:620 msgid "Show Applications options" msgstr "Options du bouton Afficher les Applications" -#: prefs.js:529 +#: src/prefs.js:643 msgid "Open icon" msgstr "Choisir l'icône" -#: prefs.js:576 +#: src/prefs.js:708 msgid "Show Desktop options" msgstr "Options du bouton Afficher le Bureau" -#: prefs.js:660 +#: src/prefs.js:800 +msgid "Primary monitor" +msgstr "Écran principal" + +#: src/prefs.js:801 +msgid "Monitor " +msgstr "Écran " + +#: src/prefs.js:928 +msgid "Running Indicator Options" +msgstr "Options de l'indicateur d'activité" + +#: src/prefs.js:1461 +msgid "Dynamic opacity options" +msgstr "Options d'opacité dynamique" + +#: src/prefs.js:1710 +msgid "Intellihide options" +msgstr "Options du masquage intelligent" + +#: src/prefs.js:1916 +msgid "Window preview options" +msgstr "Options de la prévisualisation" + +#: src/prefs.js:2467 +msgid "Ungrouped application options" +msgstr "Options des applications dégroupées" + +#: src/prefs.js:2634 +msgid "Customize middle-click behavior" +msgstr "Modifier l'action du clic de la molette" + +#: src/prefs.js:2763 +msgid "Text" +msgstr "Texte" + +#: src/prefs.js:2772 +msgid "Command" +msgstr "Commande" + +#: src/prefs.js:2804 +msgid "Remove" +msgstr "Retirer" + +#: src/prefs.js:2832 +msgid "Customize panel scroll behavior" +msgstr "Modifier l'action du défilement de la souris sur le panneau" + +#: src/prefs.js:2878 +msgid "Customize icon scroll behavior" +msgstr "Modifier l'action du défilement de la souris sur une application" + +#: src/prefs.js:3002 +msgid "Advanced hotkeys options" +msgstr "Raccourcis avancés" + +#: src/prefs.js:3030 +msgid "Secondary Menu Options" +msgstr "Options du menu secondaire" + +#: src/prefs.js:3230 #, javascript-format msgid "%d ms" msgstr "%d ms" -#: prefs.js:665 +#: src/prefs.js:3236 #, javascript-format msgid "%d °" msgstr "%d °" -#: prefs.js:670 prefs.js:675 +#: src/prefs.js:3242 src/prefs.js:3248 #, javascript-format msgid "%d %%" msgstr "%d %%" -#: prefs.js:680 +#: src/prefs.js:3254 #, javascript-format msgid "%.1f" msgstr "%.1f" -#: prefs.js:685 +#: src/prefs.js:3260 #, javascript-format msgid "%d icon" msgid_plural "%d icons" msgstr[0] "%d icône" msgstr[1] "%d icônes" -#: prefs.js:787 -msgid "Running Indicator Options" -msgstr "Options de l'indicateur d'activité" - -#: prefs.js:933 -msgid "Primary monitor" -msgstr "Écran principal" - -#: prefs.js:933 -msgid "Monitor " -msgstr "Écran " - -#: prefs.js:1127 -msgid "Dynamic opacity options" -msgstr "Options d'opacité dynamique" - -#: prefs.js:1260 -msgid "Intellihide options" -msgstr "Options du masquage intelligent" - -#: prefs.js:1366 -msgid "Window preview options" -msgstr "Options de la prévisualisation" - -#: prefs.js:1642 -msgid "Ungrouped application options" -msgstr "Options des applications dégroupées" - -#: prefs.js:1721 -msgid "Customize middle-click behavior" -msgstr "Modifier l'action du clic de la molette" - -#: prefs.js:1771 -msgid "Customize panel scroll behavior" -msgstr "Modifier l'action du défilement de la souris sur le panneau" - -#: prefs.js:1799 -msgid "Customize icon scroll behavior" -msgstr "Modifier l'action du défilement de la souris sur une application" - -#: prefs.js:1880 -msgid "Advanced hotkeys options" -msgstr "Raccourcis avancés" - -#: prefs.js:1898 -msgid "Secondary Menu Options" -msgstr "Options du menu secondaire" - -#: prefs.js:1925 ui/SettingsFineTune.ui:268 -msgid "Advanced Options" -msgstr "Options avancées" - -#: prefs.js:2041 +#: src/prefs.js:3387 msgid "App icon animation options" msgstr "Options d'animation des icônes d'application" -#: prefs.js:2089 +#: src/prefs.js:3510 +msgid "App icon highlight options" +msgstr "Options d'animation du surlignement des icônes d'application" + +#: src/prefs.js:3599 msgid "Export settings" msgstr "Exporter les paramètres" -#: prefs.js:2105 +#: src/prefs.js:3620 msgid "Import settings" msgstr "Importer des paramètres" -#: ui/BoxAdvancedOptions.ui:19 -msgid "Nothing yet!" -msgstr "Rien pour l'instant !" +#: src/appIcons.js:1911 src/appIcons.js:1928 ui/BoxMiddleClickOptions.ui.h:11 +msgid "Quit" +msgstr "Quitter" -#: ui/BoxAdvancedOptions.ui:20 -msgid "For real..." -msgstr "Pour vrai..." +#: src/appIcons.js:1931 +#, javascript-format +msgid "Quit %d Window" +msgid_plural "Quit %d Windows" +msgstr[0] "Fermer %d fenêtre" +msgstr[1] "Fermer %d fenêtres" -#: ui/BoxAnimateAppIconHoverOptions.ui:62 +#: src/appIcons.js:2253 +msgid "Power options" +msgstr "Options d'alimentation" + +#: src/appIcons.js:2258 +msgid "Event logs" +msgstr "Journaux d'évènements" + +#: src/appIcons.js:2263 +msgid "System" +msgstr "Système" + +#: src/appIcons.js:2268 +msgid "Device Management" +msgstr "Gestionnaire de périphériques" + +#: src/appIcons.js:2273 +msgid "Disk Management" +msgstr "Gestionnaire de disques" + +msgid "System Monitor" +msgstr "Moniteur système" + +msgid "Files" +msgstr "Fichiers" + +msgid "Settings" +msgstr "Paramètres" + +#: src/appIcons.js:2304 +msgid "Unlock taskbar" +msgstr "Déverrouiller la barre des tâches" + +#: src/appIcons.js:2305 +msgid "Lock taskbar" +msgstr "Verrouiller la barre des tâches" + +#: src/appIcons.js:2315 +msgid "Gnome Settings" +msgstr "Paramètres Gnome" + +#: src/appIcons.js:2319 +msgid "Taskbar Settings" +msgstr "Paramètres de la barre des tâches" + +#: src/appIcons.js:2326 +msgid "Restore Windows" +msgstr "Restaurer les fenêtres" + +#: src/appIcons.js:2327 +msgid "Show Desktop" +msgstr "Afficher le bureau" + +#: src/taskbar.js:66 +msgid "Hide and donation options" +msgstr "Cacher et options de don" + +#: src/taskbar.js:79 +msgid "Thank you!" +msgstr "Merci!" + +#: src/taskbar.js:79 +msgid "Please donate :)" +msgstr "Merci de faire un don :)" + +#: ui/BoxAnimateAppIconHoverOptions.ui.h:1 msgid "Animation type" msgstr "Type d'animation" -#: ui/BoxAnimateAppIconHoverOptions.ui:67 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:2 msgid "Simple" msgstr "Simple" -#: ui/BoxAnimateAppIconHoverOptions.ui:68 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:3 msgid "Ripple" msgstr "Ondulation" -#: ui/BoxAnimateAppIconHoverOptions.ui:69 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:4 msgid "Plank" msgstr "Planche" -#: ui/BoxAnimateAppIconHoverOptions.ui:84 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:5 msgid "Duration" msgstr "Durée" -#: ui/BoxAnimateAppIconHoverOptions.ui:101 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:6 msgid "Rotation" msgstr "Rotation" -#: ui/BoxAnimateAppIconHoverOptions.ui:118 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:7 msgid "Travel" msgstr "Déplacement" -#: ui/BoxAnimateAppIconHoverOptions.ui:135 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:8 msgid "Zoom" msgstr "Zoom" -#: ui/BoxAnimateAppIconHoverOptions.ui:152 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:9 msgid "Convexity" msgstr "Convexité" -#: ui/BoxAnimateAppIconHoverOptions.ui:169 +#: ui/BoxAnimateAppIconHoverOptions.ui.h:10 msgid "Extent" msgstr "Étendue" -#: ui/BoxDotOptions.ui:37 +#: ui/BoxDotOptions.ui.h:1 msgid "Highlight focused application" msgstr "Surligner l'application active" -#: ui/BoxDotOptions.ui:54 +#: ui/BoxDotOptions.ui.h:2 msgid "Icon dominant color" msgstr "Couleur d'icône dominante" -#: ui/BoxDotOptions.ui:65 +#: ui/BoxDotOptions.ui.h:3 msgid "Custom color" msgstr "Couleur personnalisée" -#: ui/BoxDotOptions.ui:76 +#: ui/BoxDotOptions.ui.h:4 msgid "Highlight opacity" msgstr "Opacité du surlignement" -#: ui/BoxDotOptions.ui:96 +#: ui/BoxDotOptions.ui.h:5 msgid "Indicator size (px)" msgstr "Taille de l'indicateur (px)" -#: ui/BoxDotOptions.ui:109 +#: ui/BoxDotOptions.ui.h:6 msgid "Indicator color - Icon Dominant" msgstr "Couleur de l'indicateur - Icône dominante" -#: ui/BoxDotOptions.ui:126 +#: ui/BoxDotOptions.ui.h:7 msgid "Indicator color - Override Theme" msgstr "Couleur de l'indicateur - Remplacer le thème" -#: ui/BoxDotOptions.ui:142 ui/BoxDotOptions.ui:215 +#: ui/BoxDotOptions.ui.h:8 msgid "1 window open (or ungrouped)" msgstr "1 fenêtre ouverte (ou dégroupée)" -#: ui/BoxDotOptions.ui:146 ui/BoxDotOptions.ui:219 +#: ui/BoxDotOptions.ui.h:9 msgid "Apply to all" msgstr "Appliquer à tout" -#: ui/BoxDotOptions.ui:159 ui/BoxDotOptions.ui:232 +#: ui/BoxDotOptions.ui.h:10 msgid "2 windows open" msgstr "2 fenêtres ouvertes" -#: ui/BoxDotOptions.ui:170 ui/BoxDotOptions.ui:243 +#: ui/BoxDotOptions.ui.h:11 msgid "3 windows open" msgstr "3 fenêtres ouvertes" -#: ui/BoxDotOptions.ui:181 ui/BoxDotOptions.ui:254 +#: ui/BoxDotOptions.ui.h:12 msgid "4+ windows open" msgstr "4+ fenêtres ouvertes" -#: ui/BoxDotOptions.ui:198 +#: ui/BoxDotOptions.ui.h:13 msgid "Use different for unfocused" msgstr "Style différent pour les applications inactives" -#: ui/BoxDynamicOpacityOptions.ui:37 +#: ui/BoxDynamicOpacityOptions.ui.h:1 msgid "The panel background opacity is affected by" msgstr "L'opacité de l'arrière-plan du panneau est affectée par" -#: ui/BoxDynamicOpacityOptions.ui:42 ui/BoxIntellihideOptions.ui:69 +#: ui/BoxDynamicOpacityOptions.ui.h:2 ui/BoxIntellihideOptions.ui.h:3 msgid "All windows" msgstr "Toutes les fenêtres" -#: ui/BoxDynamicOpacityOptions.ui:43 ui/BoxIntellihideOptions.ui:70 +#: ui/BoxDynamicOpacityOptions.ui.h:3 ui/BoxIntellihideOptions.ui.h:4 msgid "Focused windows" msgstr "Fenêtres au premier plan" -#: ui/BoxDynamicOpacityOptions.ui:44 ui/BoxIntellihideOptions.ui:71 +#: ui/BoxDynamicOpacityOptions.ui.h:4 ui/BoxIntellihideOptions.ui.h:5 msgid "Maximized windows" msgstr "Fenêtres maximisées" -#: ui/BoxDynamicOpacityOptions.ui:53 +#: ui/BoxDynamicOpacityOptions.ui.h:5 msgid "Change opacity when a window gets closer than (px)" msgstr "Changer l'opacité lorsqu'une fenêtre est plus proche que (px)" -#: ui/BoxDynamicOpacityOptions.ui:69 +#: ui/BoxDynamicOpacityOptions.ui.h:7 +#, no-c-format msgid "Change opacity to (%)" msgstr "Changer l'opacité à (%)" -#: ui/BoxDynamicOpacityOptions.ui:72 ui/BoxShowApplicationsOptions.ui:64 -#: ui/BoxWindowPreviewOptions.ui:468 ui/SettingsStyle.ui:266 -#: ui/SettingsStyle.ui:329 ui/SettingsStyle.ui:348 +#: ui/BoxDynamicOpacityOptions.ui.h:8 ui/BoxShowApplicationsOptions.ui.h:3 +#: ui/BoxWindowPreviewOptions.ui.h:57 ui/SettingsStyle.ui.h:40 msgid "0" msgstr "0" -#: ui/BoxDynamicOpacityOptions.ui:82 +#: ui/BoxDynamicOpacityOptions.ui.h:9 msgid "Opacity change animation duration (ms)" msgstr "Durée de l'animation de changement d'opacité (ms)" -#: ui/BoxGroupAppsOptions.ui:32 +#: ui/BoxGroupAppsOptions.ui.h:1 msgid "Font size (px) of the application titles (default is 14)" msgstr "Taille (px) du texte des titres d'application (défaut: 14)" -#: ui/BoxGroupAppsOptions.ui:47 +#: ui/BoxGroupAppsOptions.ui.h:2 msgid "Font weight of application titles" msgstr "Épaisseur de la police du texte des titres d'application" -#: ui/BoxGroupAppsOptions.ui:52 ui/BoxWindowPreviewOptions.ui:402 +#: ui/BoxGroupAppsOptions.ui.h:3 ui/BoxWindowPreviewOptions.ui.h:44 msgid "inherit from theme" msgstr "hériter du thème" -#: ui/BoxGroupAppsOptions.ui:53 ui/BoxWindowPreviewOptions.ui:403 +#: ui/BoxGroupAppsOptions.ui.h:4 ui/BoxWindowPreviewOptions.ui.h:45 msgid "normal" msgstr "normale" -#: ui/BoxGroupAppsOptions.ui:54 ui/BoxWindowPreviewOptions.ui:404 +#: ui/BoxGroupAppsOptions.ui.h:5 ui/BoxWindowPreviewOptions.ui.h:46 msgid "lighter" msgstr "plus légere" -#: ui/BoxGroupAppsOptions.ui:55 ui/BoxWindowPreviewOptions.ui:405 +#: ui/BoxGroupAppsOptions.ui.h:6 ui/BoxWindowPreviewOptions.ui.h:47 msgid "bold" msgstr "gras" -#: ui/BoxGroupAppsOptions.ui:56 ui/BoxWindowPreviewOptions.ui:406 +#: ui/BoxGroupAppsOptions.ui.h:7 ui/BoxWindowPreviewOptions.ui.h:48 msgid "bolder" msgstr "plus gras" -#: ui/BoxGroupAppsOptions.ui:65 +#: ui/BoxGroupAppsOptions.ui.h:8 msgid "Font color of the application titles" msgstr "Couleur du texte des titres d'application" -#: ui/BoxGroupAppsOptions.ui:77 +#: ui/BoxGroupAppsOptions.ui.h:9 msgid "Font color of the minimized application titles" msgstr "Couleur du texte des titres d'application de fenêtres minimisées" -#: ui/BoxGroupAppsOptions.ui:95 +#: ui/BoxGroupAppsOptions.ui.h:10 msgid "Maximum width (px) of the application titles" msgstr "Longueur maximum (px) des titres d'application" -#: ui/BoxGroupAppsOptions.ui:96 +#: ui/BoxGroupAppsOptions.ui.h:11 msgid "(default is 160)" msgstr "(la valeur par défaut est 160)" -#: ui/BoxGroupAppsOptions.ui:111 +#: ui/BoxGroupAppsOptions.ui.h:12 msgid "Use a fixed width for the application titles" msgstr "Utiliser une largeur fixe pour les titres d'application" -#: ui/BoxGroupAppsOptions.ui:112 +#: ui/BoxGroupAppsOptions.ui.h:13 msgid "" "The application titles all have the same width, even if their texts are " "shorter than the maximum width. The maximum width value is used as the fixed " @@ -540,75 +552,107 @@ msgstr "" "plus petit que la taille maximum. La valeur maximale de longueur est " "utilisée comme longueur fixe." -#: ui/BoxGroupAppsOptions.ui:129 +#: ui/BoxGroupAppsOptions.ui.h:14 msgid "Display running indicators on unfocused applications" msgstr "Afficher des indicateurs sur les applications an arrière-plan" -#: ui/BoxGroupAppsOptions.ui:140 +#: ui/BoxGroupAppsOptions.ui.h:15 msgid "Use the favorite icons as application launchers" msgstr "Utiliser les applications favorites comme lanceurs" -#: ui/BoxIntellihideOptions.ui:53 +#: ui/BoxHighlightAppIconHoverOptions.ui.h:1 +msgid "Highlight AppIcon color" +msgstr "Couleur de surlignement" + +#: ui/BoxHighlightAppIconHoverOptions.ui.h:2 +msgid "Pressed AppIcon color" +msgstr "Couleur des icônes d'application enfoncées" + +#: ui/BoxHighlightAppIconHoverOptions.ui.h:3 +msgid "Highlight AppIcon border radius" +msgstr "Coins arrondis du surlignement des icônes" + +#: ui/BoxHighlightAppIconHoverOptions.ui.h:4 +msgid "Overrides global border radius (default is 0)" +msgstr "Remplace l'option globale des coins arrondis" + +#: ui/BoxIntellihideOptions.ui.h:1 msgid "Only hide the panel when it is obstructed by windows" msgstr "Ne cacher le panneau que lorsqu'il est au-dessus d'une fenêtre" -#: ui/BoxIntellihideOptions.ui:64 +#: ui/BoxIntellihideOptions.ui.h:2 msgid "The panel hides from" msgstr "Le panneau se cache de" -#: ui/BoxIntellihideOptions.ui:86 +#: ui/BoxIntellihideOptions.ui.h:6 msgid "Require pressure at the edge of the screen to reveal the panel" msgstr "Requérir une pression sur le bord de l'écran pour afficher le panneau" -#: ui/BoxIntellihideOptions.ui:97 +#: ui/BoxIntellihideOptions.ui.h:7 msgid "Required pressure threshold (px)" msgstr "Seuil d'activation (px)" -#: ui/BoxIntellihideOptions.ui:112 +#: ui/BoxIntellihideOptions.ui.h:8 msgid "Required pressure timeout (ms)" msgstr "Délai d'activation (ms)" -#: ui/BoxIntellihideOptions.ui:133 +#: ui/BoxIntellihideOptions.ui.h:9 msgid "Allow the panel to be revealed while in fullscreen mode" msgstr "Permettre au panneau d'être affiché en mode plein écran" -#: ui/BoxIntellihideOptions.ui:144 -msgid "Only hide secondary panels" -msgstr "Ne cacher que les panneaux secondaires" - -#: ui/BoxIntellihideOptions.ui:145 +#: ui/BoxIntellihideOptions.ui.h:10 msgid "(requires multi-monitors option)" msgstr "(requiert l'option multi-écran)" -#: ui/BoxIntellihideOptions.ui:156 -msgid "Keyboard shortcut to reveal and hold the panel" -msgstr "Raccourci clavier pour révéler et maintenir le panneau" +#: ui/BoxIntellihideOptions.ui.h:11 +msgid "Only hide secondary panels" +msgstr "Ne cacher que les panneaux secondaires" -#: ui/BoxIntellihideOptions.ui:157 ui/BoxOverlayShortcut.ui:71 +#: ui/BoxIntellihideOptions.ui.h:12 ui/BoxOverlayShortcut.ui.h:11 msgid "Syntax: <Shift>, <Ctrl>, <Alt>, <Super>" msgstr "Syntaxe: <Shift>, <Ctrl>, <Alt>, <Super>" -#: ui/BoxIntellihideOptions.ui:162 +#: ui/BoxIntellihideOptions.ui.h:13 +msgid "Keyboard shortcut to reveal and hold the panel" +msgstr "Raccourci clavier pour révéler et maintenir le panneau" + +#: ui/BoxIntellihideOptions.ui.h:14 msgid "e.g. i" msgstr "e.g. i" -#: ui/BoxIntellihideOptions.ui:176 +#: ui/BoxIntellihideOptions.ui.h:15 +msgid "Persist state across restarts" +msgstr "Conserver l'état lors des redémarrages" + +#: ui/BoxIntellihideOptions.ui.h:16 +msgid "" +"(respects \"Do Not Disturb\" and requires show notification counter " +"badge option)" +msgstr "" +"(respecte \"Ne pas déranger\" et requiert afficher le nombre de " +"notifications)" + +#: ui/BoxIntellihideOptions.ui.h:17 +msgid "Reveal and hold the panel on notification" +msgstr "Révéler le panneau à la réception d'une notification" + +#: ui/BoxIntellihideOptions.ui.h:18 msgid "Hide and reveal animation duration (ms)" msgstr "Durée des animations d'affichage (ms)" -#: ui/BoxIntellihideOptions.ui:191 +#: ui/BoxIntellihideOptions.ui.h:19 msgid "Delay before hiding the panel (ms)" msgstr "Délai avant le masquage du panneau (ms)" -#: ui/BoxIntellihideOptions.ui:207 +#: ui/BoxIntellihideOptions.ui.h:20 msgid "Delay before enabling intellihide on start (ms)" msgstr "Délai avant l'activation du masquage intelligent (ms)" -#: ui/BoxMiddleClickOptions.ui:19 +#: ui/BoxMiddleClickOptions.ui.h:1 msgid "Shift+Click action" msgstr "Action Maj+Clic" -#: ui/BoxMiddleClickOptions.ui:20 +#: ui/BoxMiddleClickOptions.ui.h:2 msgid "" "When set to minimize, double clicking minimizes all the windows of the " "application." @@ -616,78 +660,71 @@ msgstr "" "Quand minimiser est sélectionné, un double-clic réduit toutes les fenêtres " "de l'application." -#: ui/BoxMiddleClickOptions.ui:25 ui/BoxMiddleClickOptions.ui:53 -#: ui/BoxMiddleClickOptions.ui:81 ui/SettingsAction.ui:41 +#: ui/BoxMiddleClickOptions.ui.h:3 ui/SettingsAction.ui.h:10 msgid "Raise windows" msgstr "Montrer les fenêtres" -#: ui/BoxMiddleClickOptions.ui:26 ui/BoxMiddleClickOptions.ui:54 -#: ui/BoxMiddleClickOptions.ui:82 +#: ui/BoxMiddleClickOptions.ui.h:4 msgid "Minimize window" msgstr "Réduire la fenêtre" -#: ui/BoxMiddleClickOptions.ui:27 ui/BoxMiddleClickOptions.ui:55 -#: ui/BoxMiddleClickOptions.ui:83 ui/SettingsAction.ui:42 +#: ui/BoxMiddleClickOptions.ui.h:5 ui/SettingsAction.ui.h:11 msgid "Launch new instance" msgstr "Lancer une nouvelle instance" -#: ui/BoxMiddleClickOptions.ui:28 ui/BoxMiddleClickOptions.ui:56 -#: ui/BoxMiddleClickOptions.ui:84 ui/SettingsAction.ui:37 +#: ui/BoxMiddleClickOptions.ui.h:6 ui/SettingsAction.ui.h:5 msgid "Cycle through windows" msgstr "Cycler sur les fenêtres" -#: ui/BoxMiddleClickOptions.ui:29 ui/BoxMiddleClickOptions.ui:57 -#: ui/BoxMiddleClickOptions.ui:85 ui/SettingsAction.ui:36 +#: ui/BoxMiddleClickOptions.ui.h:7 ui/SettingsAction.ui.h:4 msgid "Cycle windows + minimize" msgstr "Cycler sur les fenêtres + réduire" -#: ui/BoxMiddleClickOptions.ui:30 ui/BoxMiddleClickOptions.ui:58 -#: ui/BoxMiddleClickOptions.ui:86 ui/SettingsAction.ui:38 +#: ui/BoxMiddleClickOptions.ui.h:8 ui/SettingsAction.ui.h:6 msgid "Toggle single / Preview multiple" msgstr "Prévisualisation simple / multiple" -#: ui/BoxMiddleClickOptions.ui:31 ui/BoxMiddleClickOptions.ui:59 -#: ui/BoxMiddleClickOptions.ui:87 ui/SettingsAction.ui:39 +#: ui/BoxMiddleClickOptions.ui.h:9 ui/SettingsAction.ui.h:7 msgid "Toggle single / Cycle multiple" msgstr "Prévisualisation simple / cycle multiple" -#: ui/BoxMiddleClickOptions.ui:47 +#: ui/BoxMiddleClickOptions.ui.h:10 ui/SettingsAction.ui.h:8 +msgid "Toggle single / Spread multiple" +msgstr "Prévisualisation simple / diffusion de l'application" + +#: ui/BoxMiddleClickOptions.ui.h:12 msgid "Middle-Click action" msgstr "Action clic molette" -#: ui/BoxMiddleClickOptions.ui:48 +#: ui/BoxMiddleClickOptions.ui.h:13 msgid "Behavior for Middle-Click." msgstr "Comportement du clic molette." -#: ui/BoxMiddleClickOptions.ui:75 +#: ui/BoxMiddleClickOptions.ui.h:14 msgid "Shift+Middle-Click action" msgstr "Action de Maj+Clic molette" -#: ui/BoxMiddleClickOptions.ui:76 +#: ui/BoxMiddleClickOptions.ui.h:15 msgid "Behavior for Shift+Middle-Click." msgstr "Comportement pour Maj+Clic molette." -#: ui/BoxOverlayShortcut.ui:25 -msgid "Hotkeys prefix" -msgstr "Préfixe raccourcis" - -#: ui/BoxOverlayShortcut.ui:26 +#: ui/BoxOverlayShortcut.ui.h:1 msgid "Hotkeys will either be Super+Number or Super+Alt+Num" msgstr "Les raccourcis seront soit Super+Numéro soit Super+Alt+Numéro" -#: ui/BoxOverlayShortcut.ui:31 +#: ui/BoxOverlayShortcut.ui.h:2 +msgid "Hotkeys prefix" +msgstr "Préfixe raccourcis" + +#: ui/BoxOverlayShortcut.ui.h:3 msgid "Super" msgstr "Super" -#: ui/BoxOverlayShortcut.ui:32 +#: ui/BoxOverlayShortcut.ui.h:4 msgid "Super + Alt" msgstr "Super + Alt" -#: ui/BoxOverlayShortcut.ui:41 -msgid "Number overlay" -msgstr "Superposition des nombres" - -#: ui/BoxOverlayShortcut.ui:42 +#: ui/BoxOverlayShortcut.ui.h:5 msgid "" "Temporarily show the application numbers over the icons when using the " "hotkeys." @@ -695,294 +732,314 @@ msgstr "" "Afficher temporairement les numéros des applications par dessus les icônes " "lors de l'utilisation des raccourcis." -#: ui/BoxOverlayShortcut.ui:47 +#: ui/BoxOverlayShortcut.ui.h:6 +msgid "Number overlay" +msgstr "Superposition des nombres" + +#: ui/BoxOverlayShortcut.ui.h:7 msgid "Never" msgstr "Jamais" -#: ui/BoxOverlayShortcut.ui:48 +#: ui/BoxOverlayShortcut.ui.h:8 msgid "Show temporarily" msgstr "Afficher temporairement" -#: ui/BoxOverlayShortcut.ui:49 +#: ui/BoxOverlayShortcut.ui.h:9 msgid "Always visible" msgstr "Toujours visible" -#: ui/BoxOverlayShortcut.ui:58 +#: ui/BoxOverlayShortcut.ui.h:10 msgid "Hide timeout (ms)" msgstr "Délai avant de cacher (ms)" -#: ui/BoxOverlayShortcut.ui:70 +#: ui/BoxOverlayShortcut.ui.h:12 msgid "Shortcut to show the overlay for 2 seconds" msgstr "Raccourci pour afficher la superposition pendant 2 secondes" -#: ui/BoxOverlayShortcut.ui:76 +#: ui/BoxOverlayShortcut.ui.h:13 msgid "e.g. q" msgstr "e.g. q" -#: ui/BoxOverlayShortcut.ui:84 -msgid "Show window previews on hotkey" -msgstr "Afficher les aperçus des fenêtres lors de l'utilisation d'un raccourci" +#: ui/BoxOverlayShortcut.ui.h:14 +msgid "" +"On secondary monitors, show the overlay on icons matching the primary monitor" +msgstr "" +"Sur les panneaux secondaires, afficher la superposition sur les icônes " +"correspondantes au panneau principal" -#: ui/BoxOverlayShortcut.ui:85 +#: ui/BoxOverlayShortcut.ui.h:15 +msgid "Show the overlay on all monitors" +msgstr "Afficher la superposition sur tous les écrans" + +#: ui/BoxOverlayShortcut.ui.h:16 msgid "Show previews when the application have multiple instances" msgstr "" "Afficher les aperçus lorsque les applications possèdent plusieurs fenêtres" -#: ui/BoxOverlayShortcut.ui:96 -msgid "Hotkeys are activated with" -msgstr "Les raccourcis sont activés par" +#: ui/BoxOverlayShortcut.ui.h:17 +msgid "Show window previews on hotkey" +msgstr "Afficher les aperçus des fenêtres lors de l'utilisation d'un raccourci" -#: ui/BoxOverlayShortcut.ui:97 +#: ui/BoxOverlayShortcut.ui.h:18 msgid "Select which keyboard number keys are used to activate the hotkeys" msgstr "" "Sélectionner quelles touches numériques sont utilisées pour activer les " "raccourcis" -#: ui/BoxOverlayShortcut.ui:102 +#: ui/BoxOverlayShortcut.ui.h:19 +msgid "Hotkeys are activated with" +msgstr "Les raccourcis sont activés par" + +#: ui/BoxOverlayShortcut.ui.h:20 msgid "Number row" msgstr "Rangée des chiffres" -#: ui/BoxOverlayShortcut.ui:103 +#: ui/BoxOverlayShortcut.ui.h:21 msgid "Numeric keypad" msgstr "Pavé numérique" -#: ui/BoxOverlayShortcut.ui:104 +#: ui/BoxOverlayShortcut.ui.h:22 msgid "Both" msgstr "Les deux" -#: ui/BoxScrollIconOptions.ui:25 ui/BoxScrollPanelOptions.ui:25 +#: ui/BoxScrollIconOptions.ui.h:1 ui/BoxScrollPanelOptions.ui.h:1 msgid "Delay between mouse scroll events (ms)" msgstr "Délai entre les évènements de défilement de la souris (ms)" -#: ui/BoxScrollIconOptions.ui:26 ui/BoxScrollPanelOptions.ui:26 +#: ui/BoxScrollIconOptions.ui.h:2 ui/BoxScrollPanelOptions.ui.h:2 msgid "Use this value to limit the number of captured mouse scroll events." msgstr "" "Utiliser cette valeur pour limiter le nombre d'évènements de défilement de " "la souris (ms)" -#: ui/BoxScrollPanelOptions.ui:42 +#: ui/BoxScrollPanelOptions.ui.h:3 msgid "Show popup when changing workspace" msgstr "Afficher l'indicateur de changement d'espace de travail" -#: ui/BoxScrollPanelOptions.ui:43 +#: ui/BoxScrollPanelOptions.ui.h:4 msgid "This affects workspace popup when scrolling on the panel only." msgstr "" "Affecte uniquement l'indicateur de changement d'espace de travail au " "défilement de la souris sur le panneau." -#: ui/BoxSecondaryMenuOptions.ui:19 +#: ui/BoxSecondaryMenuOptions.ui.h:1 msgid "Integrate AppMenu items" msgstr "Intégrer les actions du Menu d'applications" -#: ui/BoxSecondaryMenuOptions.ui:30 -msgid "Show Details menu item" -msgstr "Menu Afficher les détails" +#: ui/BoxSecondaryMenuOptions.ui.h:2 +msgid "App Details menu item" +msgstr "Menu Détails de l’application" -#: ui/BoxShowApplicationsOptions.ui:25 +#: ui/BoxSecondaryMenuOptions.ui.h:3 +msgid "App Details is only available when Gnome Software is installed" +msgstr "" +"Détails de l’application est uniquement disponible lorsque Gnome " +"Software est installé" + +#: ui/BoxShowApplicationsOptions.ui.h:1 msgid "Show Applications icon" msgstr "Bouton Afficher les Applications" -#: ui/BoxShowApplicationsOptions.ui:60 +#: ui/BoxShowApplicationsOptions.ui.h:2 msgid "Show Applications icon side padding (px)" -msgstr "Marge interne du bouton \"Afficher les Applications\"" +msgstr "Remplissage du bouton \"Afficher les Applications\"" -#: ui/BoxShowApplicationsOptions.ui:73 +#: ui/BoxShowApplicationsOptions.ui.h:4 msgid "Override escape key and return to desktop" msgstr "" "Remplacer l'action de la touche d'échappement et retourner sur le bureau" -#: ui/BoxShowDesktopOptions.ui:53 +#: ui/BoxShowDesktopOptions.ui.h:1 msgid "Override Show Desktop line color" msgstr "Remplacer la couleur de la ligne du bouton Afficher le Bureau" -#: ui/BoxShowDesktopOptions.ui:77 +#: ui/BoxShowDesktopOptions.ui.h:2 msgid "Reveal the desktop when hovering the Show Desktop button" msgstr "Révéler le bureau lors du survol du bouton Afficher le Bureau" -#: ui/BoxShowDesktopOptions.ui:90 +#: ui/BoxShowDesktopOptions.ui.h:3 msgid "Delay before revealing the desktop (ms)" msgstr "Délai avant affichage du bureau (ms)" -#: ui/BoxShowDesktopOptions.ui:106 +#: ui/BoxShowDesktopOptions.ui.h:4 msgid "Fade duration (ms)" msgstr "Durée du fondu (ms)" -#: ui/BoxWindowPreviewOptions.ui:89 +#: ui/BoxWindowPreviewOptions.ui.h:1 msgid "Time (ms) before showing" msgstr "Temps (ms) avant d'afficher" -#: ui/BoxWindowPreviewOptions.ui:90 +#: ui/BoxWindowPreviewOptions.ui.h:2 msgid "(400 is default)" msgstr "(400 par défaut)" -#: ui/BoxWindowPreviewOptions.ui:105 +#: ui/BoxWindowPreviewOptions.ui.h:3 msgid "Time (ms) before hiding" msgstr "Temps (ms) avant de cacher" -#: ui/BoxWindowPreviewOptions.ui:106 +#: ui/BoxWindowPreviewOptions.ui.h:4 msgid "(100 is default)" msgstr "(100 par défaut)" -#: ui/BoxWindowPreviewOptions.ui:113 +#: ui/BoxWindowPreviewOptions.ui.h:5 msgid "Immediate on application icon click" msgstr "Immédiat au clic d'application" -#: ui/BoxWindowPreviewOptions.ui:134 +#: ui/BoxWindowPreviewOptions.ui.h:6 msgid "Animation time (ms)" msgstr "Durée d'animation (ms)" -#: ui/BoxWindowPreviewOptions.ui:155 +#: ui/BoxWindowPreviewOptions.ui.h:7 msgid "Middle click on the preview to close the window" msgstr "Cliquez avec la molette sur la prévisualisation pour fermer la fenêtre" -#: ui/BoxWindowPreviewOptions.ui:172 +#: ui/BoxWindowPreviewOptions.ui.h:8 msgid "Window previews preferred size (px)" msgstr "Taille des prévisualisations de fenêtres (px)" -#: ui/BoxWindowPreviewOptions.ui:188 +#: ui/BoxWindowPreviewOptions.ui.h:9 msgid "Window previews aspect ratio X (width)" msgstr "Ratio d'aspect des prévisualisations (largeur)" -#: ui/BoxWindowPreviewOptions.ui:193 ui/BoxWindowPreviewOptions.ui:234 +#: ui/BoxWindowPreviewOptions.ui.h:10 msgid "1" msgstr "1" -#: ui/BoxWindowPreviewOptions.ui:194 ui/BoxWindowPreviewOptions.ui:235 +#: ui/BoxWindowPreviewOptions.ui.h:11 msgid "2" msgstr "2" -#: ui/BoxWindowPreviewOptions.ui:195 ui/BoxWindowPreviewOptions.ui:236 +#: ui/BoxWindowPreviewOptions.ui.h:12 msgid "3" msgstr "3" -#: ui/BoxWindowPreviewOptions.ui:196 ui/BoxWindowPreviewOptions.ui:237 +#: ui/BoxWindowPreviewOptions.ui.h:13 msgid "4" msgstr "4" -#: ui/BoxWindowPreviewOptions.ui:197 ui/BoxWindowPreviewOptions.ui:238 -#: ui/BoxWindowPreviewOptions.ui:302 +#: ui/BoxWindowPreviewOptions.ui.h:14 msgid "5" msgstr "5" -#: ui/BoxWindowPreviewOptions.ui:198 ui/BoxWindowPreviewOptions.ui:239 +#: ui/BoxWindowPreviewOptions.ui.h:15 msgid "6" msgstr "6" -#: ui/BoxWindowPreviewOptions.ui:199 ui/BoxWindowPreviewOptions.ui:240 +#: ui/BoxWindowPreviewOptions.ui.h:16 msgid "7" msgstr "7" -#: ui/BoxWindowPreviewOptions.ui:200 ui/BoxWindowPreviewOptions.ui:241 +#: ui/BoxWindowPreviewOptions.ui.h:17 msgid "8" msgstr "8" -#: ui/BoxWindowPreviewOptions.ui:201 ui/BoxWindowPreviewOptions.ui:242 +#: ui/BoxWindowPreviewOptions.ui.h:18 msgid "9" msgstr "9" -#: ui/BoxWindowPreviewOptions.ui:202 ui/BoxWindowPreviewOptions.ui:243 +#: ui/BoxWindowPreviewOptions.ui.h:19 msgid "10" msgstr "10" -#: ui/BoxWindowPreviewOptions.ui:203 ui/BoxWindowPreviewOptions.ui:244 +#: ui/BoxWindowPreviewOptions.ui.h:20 msgid "11" msgstr "11" -#: ui/BoxWindowPreviewOptions.ui:204 ui/BoxWindowPreviewOptions.ui:245 +#: ui/BoxWindowPreviewOptions.ui.h:21 msgid "12" msgstr "12" -#: ui/BoxWindowPreviewOptions.ui:205 ui/BoxWindowPreviewOptions.ui:246 +#: ui/BoxWindowPreviewOptions.ui.h:22 msgid "13" msgstr "13" -#: ui/BoxWindowPreviewOptions.ui:206 ui/BoxWindowPreviewOptions.ui:247 +#: ui/BoxWindowPreviewOptions.ui.h:23 msgid "14" msgstr "14" -#: ui/BoxWindowPreviewOptions.ui:207 ui/BoxWindowPreviewOptions.ui:248 +#: ui/BoxWindowPreviewOptions.ui.h:24 msgid "15" msgstr "15" -#: ui/BoxWindowPreviewOptions.ui:208 ui/BoxWindowPreviewOptions.ui:249 +#: ui/BoxWindowPreviewOptions.ui.h:25 msgid "16" msgstr "16" -#: ui/BoxWindowPreviewOptions.ui:209 ui/BoxWindowPreviewOptions.ui:250 +#: ui/BoxWindowPreviewOptions.ui.h:26 msgid "17" msgstr "17" -#: ui/BoxWindowPreviewOptions.ui:210 ui/BoxWindowPreviewOptions.ui:251 +#: ui/BoxWindowPreviewOptions.ui.h:27 msgid "18" msgstr "18" -#: ui/BoxWindowPreviewOptions.ui:211 ui/BoxWindowPreviewOptions.ui:252 +#: ui/BoxWindowPreviewOptions.ui.h:28 msgid "19" msgstr "19" -#: ui/BoxWindowPreviewOptions.ui:212 ui/BoxWindowPreviewOptions.ui:253 +#: ui/BoxWindowPreviewOptions.ui.h:29 msgid "20" msgstr "20" -#: ui/BoxWindowPreviewOptions.ui:213 ui/BoxWindowPreviewOptions.ui:254 +#: ui/BoxWindowPreviewOptions.ui.h:30 msgid "21" msgstr "21" -#: ui/BoxWindowPreviewOptions.ui:219 ui/BoxWindowPreviewOptions.ui:260 +#: ui/BoxWindowPreviewOptions.ui.h:31 msgid "Fixed" msgstr "Fixé" -#: ui/BoxWindowPreviewOptions.ui:229 +#: ui/BoxWindowPreviewOptions.ui.h:32 msgid "Window previews aspect ratio Y (height)" msgstr "Ratio d'aspect des prévisualisations (hauteur)" -#: ui/BoxWindowPreviewOptions.ui:270 +#: ui/BoxWindowPreviewOptions.ui.h:33 msgid "Window previews padding (px)" -msgstr "Marge intérieure des prévisualisations (px)" +msgstr "Remplissage des prévisualisations (px)" -#: ui/BoxWindowPreviewOptions.ui:292 +#: ui/BoxWindowPreviewOptions.ui.h:34 msgid "Use custom opacity for the previews background" msgstr "Opacité personnalisée pour l'arrière plan des prévisualisations" -#: ui/BoxWindowPreviewOptions.ui:293 +#: ui/BoxWindowPreviewOptions.ui.h:35 msgid "" "If disabled, the previews background have the same opacity as the panel." msgstr "" "Si désactivé, les prévisualisations ont la même opacité que le panneau." -#: ui/BoxWindowPreviewOptions.ui:318 +#: ui/BoxWindowPreviewOptions.ui.h:36 msgid "Close button and header position" msgstr "Bouton de fermeture et position de l'en-tête" -#: ui/BoxWindowPreviewOptions.ui:348 +#: ui/BoxWindowPreviewOptions.ui.h:39 msgid "Display window preview headers" msgstr "Afficher le titre de la fenêtre dans la prévisualisation." -#: ui/BoxWindowPreviewOptions.ui:359 +#: ui/BoxWindowPreviewOptions.ui.h:40 msgid "Icon size (px) of the window preview" msgstr "Taille (px) de l'icône des prévisualisations" -#: ui/BoxWindowPreviewOptions.ui:360 +#: ui/BoxWindowPreviewOptions.ui.h:41 msgid "If disabled, the previews icon size will be based on headerbar size" msgstr "Si désactivé, la taille de l'icône est basée sur celle de l'en-tête" -#: ui/BoxWindowPreviewOptions.ui:381 +#: ui/BoxWindowPreviewOptions.ui.h:42 msgid "Font size (px) of the preview titles" msgstr "Taille (px) du texte des prévisualisations" -#: ui/BoxWindowPreviewOptions.ui:397 +#: ui/BoxWindowPreviewOptions.ui.h:43 msgid "Font weight of the preview titles" msgstr "Épaisseur du texte des titres pendant la prévisualisation" -#: ui/BoxWindowPreviewOptions.ui:415 +#: ui/BoxWindowPreviewOptions.ui.h:49 msgid "Font color of the preview titles" msgstr "Couleur du texte des titres pendant la prévisualisation" -#: ui/BoxWindowPreviewOptions.ui:433 +#: ui/BoxWindowPreviewOptions.ui.h:50 msgid "Enable window peeking" msgstr "Activer la vue de fenêtre" -#: ui/BoxWindowPreviewOptions.ui:434 +#: ui/BoxWindowPreviewOptions.ui.h:51 msgid "" "When hovering over a window preview for some time, the window gets " "distinguished." @@ -990,11 +1047,11 @@ msgstr "" "Le survol sur la prévisualisation de la fenêtre, pendant un certain temps, " "la met en avant." -#: ui/BoxWindowPreviewOptions.ui:445 +#: ui/BoxWindowPreviewOptions.ui.h:52 msgid "Enter window peeking mode timeout (ms)" msgstr "Temps d'attente avant activation de la vue fenêtre" -#: ui/BoxWindowPreviewOptions.ui:446 +#: ui/BoxWindowPreviewOptions.ui.h:53 msgid "" "Time of inactivity while hovering over a window preview needed to enter the " "window peeking mode." @@ -1002,15 +1059,15 @@ msgstr "" "Temps d'inactivité pendant le survol de la prévisualisation d'une fenêtre " "nécessaire pour activer la vue de la fenêtre." -#: ui/BoxWindowPreviewOptions.ui:451 +#: ui/BoxWindowPreviewOptions.ui.h:54 msgid "50" msgstr "50" -#: ui/BoxWindowPreviewOptions.ui:462 +#: ui/BoxWindowPreviewOptions.ui.h:55 msgid "Window peeking mode opacity" msgstr "Opacité de la vue de fenêtre" -#: ui/BoxWindowPreviewOptions.ui:463 +#: ui/BoxWindowPreviewOptions.ui.h:56 msgid "" "All windows except for the peeked one have their opacity set to the same " "value." @@ -1018,35 +1075,31 @@ msgstr "" "Toutes les fenêtres, mis à part celle prévisualisée, ont leur opacité mise à " "la même valeur." -#: ui/SettingsAbout.ui:6 +#: ui/SettingsAbout.ui.h:1 msgid "About" msgstr "À propos" -#: ui/SettingsAbout.ui:12 +#: ui/SettingsAbout.ui.h:2 msgid "Info" msgstr "Information" -#: ui/SettingsAbout.ui:16 +#: ui/SettingsAbout.ui.h:3 msgid "Version" msgstr "Version" -#: ui/SettingsAbout.ui:27 +#: ui/SettingsAbout.ui.h:4 msgid "Source" msgstr "Source" -#: ui/SettingsAbout.ui:30 +#: ui/SettingsAbout.ui.h:5 msgid "GitHub" msgstr "GitHub" -#: ui/SettingsAbout.ui:45 +#: ui/SettingsAbout.ui.h:6 msgid "Export and Import" msgstr "Import et export" -#: ui/SettingsAbout.ui:49 -msgid "Export and import settings" -msgstr "Import et export de paramètres" - -#: ui/SettingsAbout.ui:50 +#: ui/SettingsAbout.ui.h:7 msgid "" "Use the buttons below to create a settings file from your current " "preferences that can be imported on a different machine." @@ -1054,90 +1107,91 @@ msgstr "" "Utiliser les boutons ci-dessous pour créer un fichier de paramètres à partir " "de vos préférences actuelles qui pourra être importé sur une autre machine." -#: ui/SettingsAbout.ui:66 +#: ui/SettingsAbout.ui.h:8 +msgid "Export and import settings" +msgstr "Import et export de paramètres" + +#: ui/SettingsAbout.ui.h:9 msgid "Export to file" msgstr "Exporter vers un fichier" -#: ui/SettingsAbout.ui:72 +#: ui/SettingsAbout.ui.h:10 msgid "Import from file" msgstr "Importer à partir d'un fichier" -#: ui/SettingsAbout.ui:89 +#: ui/SettingsAbout.ui.h:11 msgid "" "This program comes with ABSOLUTELY NO WARRANTY.\n" -"See the GNU General Public License, version 2 or later for details." +"See the GNU General Public License, version 2 or later for " +"details." msgstr "" "Ce programme est fourni SANS AUCUNE GARANTIE.\n" "Pour plus de détails, visitez la Licence publique générale GNU, version 2 ou " "ultérieure" -#: ui/SettingsAction.ui:6 +#: ui/SettingsAction.ui.h:1 msgid "Action" msgstr "Action" -#: ui/SettingsAction.ui:12 ui/SettingsAction.ui:16 +#: ui/SettingsAction.ui.h:2 msgid "Click action" msgstr "Action du clic" -#: ui/SettingsAction.ui:17 +#: ui/SettingsAction.ui.h:3 msgid "Behaviour when clicking on the icon of a running application." msgstr "Comportement lors du clic sur l'icône d'une application lancée." -#: ui/SettingsAction.ui:40 +#: ui/SettingsAction.ui.h:9 msgid "Toggle windows" msgstr "Basculer les fenêtres" -#: ui/SettingsAction.ui:55 +#: ui/SettingsAction.ui.h:12 msgid "Scroll action" msgstr "Action du défilement de la souris" -#: ui/SettingsAction.ui:59 -msgid "Scroll panel action" -msgstr "Action du défilement de la souris sur le panneau" - -#: ui/SettingsAction.ui:60 +#: ui/SettingsAction.ui.h:13 msgid "Behavior when mouse scrolling over the panel." msgstr "Comportement lors du défilement de la souris sur le panneau" -#: ui/SettingsAction.ui:84 ui/SettingsAction.ui:118 +#: ui/SettingsAction.ui.h:14 +msgid "Scroll panel action" +msgstr "Action du défilement de la souris sur le panneau" + +#: ui/SettingsAction.ui.h:15 msgid "Do nothing" msgstr "Ne rien faire" -#: ui/SettingsAction.ui:85 +#: ui/SettingsAction.ui.h:16 msgid "Switch workspace" msgstr "Défiler les espaces de travail" -#: ui/SettingsAction.ui:86 ui/SettingsAction.ui:119 +#: ui/SettingsAction.ui.h:17 msgid "Cycle windows" msgstr "Défiler les fenêtres" -#: ui/SettingsAction.ui:87 +#: ui/SettingsAction.ui.h:18 msgid "Change volume" msgstr "Changer le volume" -#: ui/SettingsAction.ui:96 -msgid "Scroll icon action" -msgstr "Action du défilement de la souris sur une application" - -#: ui/SettingsAction.ui:97 +#: ui/SettingsAction.ui.h:19 msgid "Behavior when mouse scrolling over an application icon." msgstr "Comportement lors du défilement de la souris sur une application" -#: ui/SettingsAction.ui:120 +#: ui/SettingsAction.ui.h:20 +msgid "Scroll icon action" +msgstr "Action du défilement de la souris sur une application" + +#: ui/SettingsAction.ui.h:21 msgid "Same as panel" msgstr "Comme le panneau" -#: ui/SettingsAction.ui:133 +#: ui/SettingsAction.ui.h:22 msgid "Hotkey overlay" msgstr "Superposition des nombres" -#: ui/SettingsAction.ui:137 -msgid "Use hotkeys to activate apps" -msgstr "Utiliser des raccourcis pour lancer les applications" - -#: ui/SettingsAction.ui:138 +#: ui/SettingsAction.ui.h:23 msgid "" "Enable Super+(0-9) as shortcuts to activate apps. It can also be used " "together with Shift and Ctrl." @@ -1145,347 +1199,401 @@ msgstr "" "Activer Super+(0-9) comme raccourcis pour lancer les applications. Maj et " "Ctrl peuvent aussi être utilisés." -#: ui/SettingsBehavior.ui:5 -msgid "Behavior" -msgstr "Comportement" +#: ui/SettingsAction.ui.h:24 +msgid "Use hotkeys to activate apps" +msgstr "Utiliser des raccourcis pour lancer les applications" -#: ui/SettingsBehavior.ui:11 -msgid "Applications" -msgstr "Applications" +#: ui/SettingsAction.ui.h:25 +msgid "Application icons context menu" +msgstr "Menu contextuel de l'application" -#: ui/SettingsBehavior.ui:15 -msgid "Show favorite applications" -msgstr "Afficher les applications favorites" - -#: ui/SettingsBehavior.ui:26 -msgid "Show favorite applications on secondary panels" -msgstr "Afficher les applications favorites sur les panneaux secondaires" - -#: ui/SettingsBehavior.ui:37 -msgid "Show running applications" -msgstr "Afficher les applications ouvertes" - -#: ui/SettingsBehavior.ui:48 -msgid "Ungroup applications" -msgstr "Dégrouper les applications" - -#: ui/SettingsBehavior.ui:73 -msgid "Show notification counter badge" -msgstr "Afficher le nombre de notifications" - -#: ui/SettingsBehavior.ui:90 -msgid "Hover" -msgstr "Survol" - -#: ui/SettingsBehavior.ui:94 -msgid "Show window previews on hover" -msgstr "Afficher les aperçus des fenêtres lors du survol" - -#: ui/SettingsBehavior.ui:119 -msgid "Show tooltip on hover" -msgstr "Afficher les bulles d'info. lors du survol" - -#: ui/SettingsBehavior.ui:134 -msgid "Isolate" -msgstr "Isolation" - -#: ui/SettingsBehavior.ui:138 -msgid "Isolate Workspaces" -msgstr "Isoler les espaces de travail" - -#: ui/SettingsBehavior.ui:149 -msgid "Isolate monitors" -msgstr "Isoler les écrans" - -#: ui/SettingsBehavior.ui:164 -msgid "Overview" -msgstr "Activités" - -#: ui/SettingsBehavior.ui:168 -msgid "Click empty space to close overview" -msgstr "Clic région vide pour fermer Activités" - -#: ui/SettingsBehavior.ui:179 -msgid "Disable show overview on startup" -msgstr "Désactiver le démarrage en mode Activités" - -#: ui/SettingsFineTune.ui:41 -msgid "Fine-Tune" -msgstr "Personnalisation" - -#: ui/SettingsFineTune.ui:47 -msgid "Font size" -msgstr "Taille de la police" - -#: ui/SettingsFineTune.ui:51 -msgid "Tray Font Size" -msgstr "Taille de la police des éléments de la barre d'état" - -#: ui/SettingsFineTune.ui:52 ui/SettingsFineTune.ui:70 -msgid "(0 = theme default)" -msgstr "(0 = par défaut)" - -#: ui/SettingsFineTune.ui:69 -msgid "LeftBox Font Size" -msgstr "Taille de la police de la zone de gauche" - -#: ui/SettingsFineTune.ui:91 -msgid "Padding" -msgstr "Marge" - -#: ui/SettingsFineTune.ui:95 -msgid "Tray Item Padding" -msgstr "Marge des éléments de la barre d'état" - -#: ui/SettingsFineTune.ui:96 ui/SettingsFineTune.ui:114 -#: ui/SettingsFineTune.ui:132 -msgid "(-1 = theme default)" -msgstr "(-1 = par défaut)" - -#: ui/SettingsFineTune.ui:113 -msgid "Status Icon Padding" -msgstr "Marge des icônes de statut" - -#: ui/SettingsFineTune.ui:131 -msgid "LeftBox Padding" -msgstr "Marge de la zone de gauche" - -#: ui/SettingsFineTune.ui:153 -msgid "Animate" -msgstr "Animation" - -#: ui/SettingsFineTune.ui:157 -msgid "Animate switching applications" -msgstr "Animer le changement d'application" - -#: ui/SettingsFineTune.ui:168 -msgid "Animate launching new windows" -msgstr "Animer le lancement de nouvelles fenêtres" - -#: ui/SettingsFineTune.ui:183 -msgid "Gnome functionality" -msgstr "Fonctionnalités Gnome" - -#: ui/SettingsFineTune.ui:187 -msgid "Keep original gnome-shell dash" -msgstr "Garder le lanceur d'origine de gnome-shell (Activités)" - -#: ui/SettingsFineTune.ui:188 -msgid "(overview)" -msgstr "(activités)" - -#: ui/SettingsFineTune.ui:199 -msgid "Keep original gnome-shell top panel" -msgstr "Garder le panneau d'origine de gnome-shell" - -#: ui/SettingsFineTune.ui:210 -msgid "Activate panel menu buttons on click only" -msgstr "Activer les menus du panneau seulement au clic" - -#: ui/SettingsFineTune.ui:211 -msgid "(e.g. date menu)" -msgstr "(exemple: horloge)" - -#: ui/SettingsFineTune.ui:222 -msgid "Force Activities hot corner on primary monitor" -msgstr "Forcer le coin actif des Activités sur l'écran principal" - -#: ui/SettingsFineTune.ui:240 -msgid "App icon secondary menu" -msgstr "Menu secondaire de l'application" - -#: ui/SettingsFineTune.ui:241 +#: ui/SettingsAction.ui.h:26 msgid "(right-click menu)" msgstr "(clic droit)" -#: ui/SettingsPosition.ui:19 +#: ui/SettingsAction.ui.h:27 +msgid "Secondary menu" +msgstr "Options du menu secondaire" + +#: ui/SettingsAction.ui.h:28 +msgid "Panel context menu entries" +msgstr "Entrées menu contextuel du panneau" + +#: ui/SettingsAction.ui.h:29 +msgid "Add entry" +msgstr "Ajouter entrée" + +#: ui/SettingsBehavior.ui.h:1 +msgid "Behavior" +msgstr "Comportement" + +#: ui/SettingsBehavior.ui.h:2 +msgid "Applications" +msgstr "Applications" + +#: ui/SettingsBehavior.ui.h:3 +msgid "Show favorite applications" +msgstr "Afficher les applications favorites" + +#: ui/SettingsBehavior.ui.h:4 +msgid "Show favorite applications on secondary panels" +msgstr "Afficher les applications favorites sur les panneaux secondaires" + +#: ui/SettingsBehavior.ui.h:5 +msgid "Show running applications" +msgstr "Afficher les applications ouvertes" + +#: ui/SettingsBehavior.ui.h:6 +msgid "Ungroup applications" +msgstr "Dégrouper les applications" + +#: ui/SettingsBehavior.ui.h:7 +msgid "Show notification counter badge" +msgstr "Afficher le nombre de notifications" + +#: ui/SettingsBehavior.ui.h:8 +msgid "Hover" +msgstr "Survol" + +#: ui/SettingsBehavior.ui.h:9 +msgid "Show window previews on hover" +msgstr "Afficher les aperçus des fenêtres lors du survol" + +#: ui/SettingsBehavior.ui.h:10 +msgid "Show tooltip on hover" +msgstr "Afficher les bulles d'info. lors du survol" + +#: ui/SettingsBehavior.ui.h:11 +msgid "Isolate" +msgstr "Isolation" + +#: ui/SettingsBehavior.ui.h:12 +msgid "Isolate Workspaces" +msgstr "Isoler les espaces de travail" + +#: ui/SettingsBehavior.ui.h:13 +msgid "Isolate monitors" +msgstr "Isoler les écrans" + +#: ui/SettingsBehavior.ui.h:14 +msgid "Overview" +msgstr "Activités" + +#: ui/SettingsBehavior.ui.h:15 +msgid "Click empty space to close overview" +msgstr "Clic région vide pour fermer Activités" + +#: ui/SettingsBehavior.ui.h:16 +msgid "Disable show overview on startup" +msgstr "Désactiver le démarrage en mode Activités" + +#: ui/SettingsFineTune.ui.h:1 +msgid "Fine-Tune" +msgstr "Personnalisation" + +#: ui/SettingsFineTune.ui.h:2 +msgid "Font size" +msgstr "Taille de la police" + +#: ui/SettingsFineTune.ui.h:3 +msgid "(0 = theme default)" +msgstr "(0 = par défaut)" + +#: ui/SettingsFineTune.ui.h:4 +msgid "Tray Font Size" +msgstr "Taille de la police des éléments de la barre d'état" + +#: ui/SettingsFineTune.ui.h:5 +msgid "LeftBox Font Size" +msgstr "Taille de la police de la zone de gauche" + +#: ui/SettingsFineTune.ui.h:6 +msgid "Padding" +msgstr "Remplissage" + +#: ui/SettingsFineTune.ui.h:7 +msgid "(-1 = theme default)" +msgstr "(-1 = par défaut)" + +#: ui/SettingsFineTune.ui.h:8 +msgid "Tray Item Padding" +msgstr "Remplissage des éléments de la barre d'état" + +#: ui/SettingsFineTune.ui.h:9 +msgid "Status Icon Padding" +msgstr "Remplissage des icônes de statut" + +#: ui/SettingsFineTune.ui.h:10 +msgid "LeftBox Padding" +msgstr "Remplissage de la zone de gauche" + +#: ui/SettingsFineTune.ui.h:11 +msgid "Animate" +msgstr "Animation" + +#: ui/SettingsFineTune.ui.h:12 +msgid "Animate switching applications" +msgstr "Animer le changement d'application" + +#: ui/SettingsFineTune.ui.h:13 +msgid "Animate launching new windows" +msgstr "Animer le lancement de nouvelles fenêtres" + +#: ui/SettingsFineTune.ui.h:14 +msgid "Gnome functionality" +msgstr "Fonctionnalités Gnome" + +#: ui/SettingsFineTune.ui.h:15 +msgid "(overview)" +msgstr "(activités)" + +#: ui/SettingsFineTune.ui.h:16 +msgid "Keep original gnome-shell dash" +msgstr "Garder le lanceur d'origine de gnome-shell (Activités)" + +#: ui/SettingsFineTune.ui.h:17 +msgid "Keep original gnome-shell top panel" +msgstr "Garder le panneau d'origine de gnome-shell" + +#: ui/SettingsFineTune.ui.h:18 +msgid "(e.g. date menu)" +msgstr "(exemple: horloge)" + +#: ui/SettingsFineTune.ui.h:19 +msgid "Activate panel menu buttons on click only" +msgstr "Activer les menus du panneau seulement au clic" + +#: ui/SettingsFineTune.ui.h:20 +msgid "Force Activities hot corner on primary monitor" +msgstr "Forcer le coin actif des Activités sur l'écran principal" + +#: ui/SettingsPosition.ui.h:1 msgid "Position" msgstr "Position" -#: ui/SettingsPosition.ui:25 +#: ui/SettingsPosition.ui.h:2 msgid "Panel" msgstr "Panneau" -#: ui/SettingsPosition.ui:29 +#: ui/SettingsPosition.ui.h:3 msgid "Display the main panel on" msgstr "Afficher le panneau principal sur" -#: ui/SettingsPosition.ui:40 +#: ui/SettingsPosition.ui.h:4 msgid "Display panels on all monitors" msgstr "Afficher le panneau principal sur tous les écrans" -#: ui/SettingsPosition.ui:56 -msgid "Panel Intellihide" -msgstr "Masquage intelligent du panneau" - -#: ui/SettingsPosition.ui:57 +#: ui/SettingsPosition.ui.h:5 msgid "Hide and reveal the panel according to preferences" msgstr "Afficher/Cacher le panneau selon les préférences" -#: ui/SettingsPosition.ui:86 +#: ui/SettingsPosition.ui.h:6 +msgid "Panel Intellihide" +msgstr "Masquage intelligent du panneau" + +#: ui/SettingsPosition.ui.h:7 msgid "Order and Position on monitors" msgstr "Ordre et positions de l'écran" -#: ui/SettingsPosition.ui:91 +#: ui/SettingsPosition.ui.h:8 msgid "Monitor" msgstr "Écran" -#: ui/SettingsPosition.ui:100 +#: ui/SettingsPosition.ui.h:9 msgid "Apply changes to all monitors" msgstr "Appliquer les changements sur tous les écrans" -#: ui/SettingsPosition.ui:127 +#: ui/SettingsPosition.ui.h:10 msgid "Panel screen position" msgstr "Position du panneau" -#: ui/SettingsPosition.ui:169 -msgid "Panel thickness" -msgstr "Taille du panneau" - -#: ui/SettingsPosition.ui:170 +#: ui/SettingsPosition.ui.h:15 msgid "(default is 48)" msgstr "(la valeur par défaut est 48)" -#: ui/SettingsPosition.ui:187 -msgid "Panel length (%)" -msgstr "Longueur du panneau (%)" +#: ui/SettingsPosition.ui.h:16 +msgid "Panel thickness" +msgstr "Taille du panneau" -#: ui/SettingsPosition.ui:188 +#: ui/SettingsPosition.ui.h:17 msgid "(default is 100)" msgstr "(la valeur par défaut est 100)" -#: ui/SettingsPosition.ui:204 +#: ui/SettingsPosition.ui.h:18 +msgid "Panel length" +msgstr "Longueur du panneau" + +#: ui/SettingsPosition.ui.h:19 +msgid "Dynamic" +msgstr "Dynamique" + +#: ui/SettingsPosition.ui.h:20 msgid "Anchor" msgstr "Ancrage" -#: ui/SettingsPosition.ui:227 +#: ui/SettingsPosition.ui.h:24 msgid "Taskbar Display" msgstr "Affichage de la barre des tâches" -#: ui/SettingsStyle.ui:38 +#: ui/SettingsStyle.ui.h:1 msgid "Style" msgstr "Style" -#: ui/SettingsStyle.ui:44 +#: ui/SettingsStyle.ui.h:2 +msgid "Global style" +msgstr "Style global" + +#: ui/SettingsStyle.ui.h:3 +msgid "Border radius" +msgstr "Coins arrondis" + +#: ui/SettingsStyle.ui.h:4 msgid "AppIcon style" msgstr "Style des icônes d'application" -#: ui/SettingsStyle.ui:48 -msgid "App Icon Margin" -msgstr "Marge ext. de l'icône d'application" - -#: ui/SettingsStyle.ui:49 +#: ui/SettingsStyle.ui.h:5 msgid "(default is 8)" msgstr "(la valeur par défaut est 8)" -#: ui/SettingsStyle.ui:66 -msgid "App Icon Padding" -msgstr "Marge int. de l'icône d'application" +#: ui/SettingsStyle.ui.h:6 +msgid "App Icon Margin" +msgstr "Marge de l'icône d'application" -#: ui/SettingsStyle.ui:67 +#: ui/SettingsStyle.ui.h:7 msgid "(default is 4)" msgstr "(la valeur par défaut est 4)" -#: ui/SettingsStyle.ui:84 +#: ui/SettingsStyle.ui.h:8 +msgid "App Icon Padding" +msgstr "Remplissage de l'icône d'application" + +#: ui/SettingsStyle.ui.h:9 msgid "Animate hovering app icons" msgstr "Animer le survol des icônes d'application" -#: ui/SettingsStyle.ui:109 +#: ui/SettingsStyle.ui.h:10 +msgid "Highlight hovering app icons" +msgstr "Animer le survol des icônes d'application" + +#: ui/SettingsStyle.ui.h:11 msgid "Icon style" msgstr "Style d'icône" -#: ui/SettingsStyle.ui:114 +#: ui/SettingsStyle.ui.h:12 msgid "Normal" msgstr "Normal" -#: ui/SettingsStyle.ui:115 +#: ui/SettingsStyle.ui.h:13 msgid "Symbolic" msgstr "Symbolique" -#: ui/SettingsStyle.ui:127 +#: ui/SettingsStyle.ui.h:14 +msgid "Grayscale" +msgstr "Niveaux de gris" + +#: ui/SettingsStyle.ui.h:15 msgid "Running indicator" msgstr "Position de l'indicateur d'activité" -#: ui/SettingsStyle.ui:131 +#: ui/SettingsStyle.ui.h:16 msgid "Running indicator position" msgstr "Position de l'indicateur d'activité" -#: ui/SettingsStyle.ui:168 +#: ui/SettingsStyle.ui.h:21 msgid "Running indicator style (Focused app)" msgstr "Style de l'indicateur d'activité (Application active)" -#: ui/SettingsStyle.ui:186 ui/SettingsStyle.ui:206 +#: ui/SettingsStyle.ui.h:22 msgid "Dots" msgstr "Points" -#: ui/SettingsStyle.ui:187 ui/SettingsStyle.ui:207 +#: ui/SettingsStyle.ui.h:23 msgid "Squares" msgstr "Carrés" -#: ui/SettingsStyle.ui:188 ui/SettingsStyle.ui:208 +#: ui/SettingsStyle.ui.h:24 msgid "Dashes" msgstr "Traits" -#: ui/SettingsStyle.ui:189 ui/SettingsStyle.ui:209 +#: ui/SettingsStyle.ui.h:25 msgid "Segmented" msgstr "Segmenté" -#: ui/SettingsStyle.ui:190 ui/SettingsStyle.ui:210 +#: ui/SettingsStyle.ui.h:26 msgid "Solid" msgstr "Solide" -#: ui/SettingsStyle.ui:191 ui/SettingsStyle.ui:211 +#: ui/SettingsStyle.ui.h:27 msgid "Ciliora" msgstr "Ciliora" -#: ui/SettingsStyle.ui:192 ui/SettingsStyle.ui:212 +#: ui/SettingsStyle.ui.h:28 msgid "Metro" msgstr "Metro" -#: ui/SettingsStyle.ui:201 +#: ui/SettingsStyle.ui.h:29 msgid "Running indicator style (Unfocused apps)" msgstr "Style de l'indicateur d'activité (Applications inactives)" -#: ui/SettingsStyle.ui:225 +#: ui/SettingsStyle.ui.h:30 msgid "Panel style" msgstr "Style du panneau" -#: ui/SettingsStyle.ui:229 +#: ui/SettingsStyle.ui.h:31 +msgid "(default is 0)" +msgstr "(la valeur par défaut est 0)" + +#: ui/SettingsStyle.ui.h:32 +msgid "Side margins" +msgstr "Marges latérales" + +#: ui/SettingsStyle.ui.h:33 +msgid "Top and bottom margins" +msgstr "Marges supérieure et inférieure" + +#: ui/SettingsStyle.ui.h:34 +msgid "Side padding" +msgstr "Remplissage latéral" + +#: ui/SettingsStyle.ui.h:35 +msgid "Top and bottom padding" +msgstr "Remplissage supérieur et inférieur" + +#: ui/SettingsStyle.ui.h:36 msgid "Override panel theme background color" msgstr "Remplacer la couleur de fond du thème du panneau" -#: ui/SettingsStyle.ui:251 +#: ui/SettingsStyle.ui.h:37 msgid "Override panel theme background opacity" msgstr "Remplacer l'opacité du thème du panneau" -#: ui/SettingsStyle.ui:262 +#: ui/SettingsStyle.ui.h:39 +#, no-c-format msgid "Panel background opacity (%)" msgstr "Opacité du fond du panneau" -#: ui/SettingsStyle.ui:275 -msgid "Dynamic background opacity" -msgstr "Opacité de fond dynamique" - -#: ui/SettingsStyle.ui:276 +#: ui/SettingsStyle.ui.h:41 msgid "Change opacity when a window gets close to the panel" msgstr "Changer l'opacité lorsqu'une fenêtre s'approche du panneau" -#: ui/SettingsStyle.ui:308 +#: ui/SettingsStyle.ui.h:42 +msgid "Dynamic background opacity" +msgstr "Opacité de fond dynamique" + +#: ui/SettingsStyle.ui.h:43 msgid "Override panel theme gradient" msgstr "Remplacer le gradient du thème du panneau" -#: ui/SettingsStyle.ui:319 +#: ui/SettingsStyle.ui.h:45 +#, no-c-format msgid "Gradient top color and opacity (%)" msgstr "Couleur et opacité (%) du haut du gradient" -#: ui/SettingsStyle.ui:338 +#: ui/SettingsStyle.ui.h:47 +#, no-c-format msgid "Gradient bottom color and opacity (%)" msgstr "Couleur et opacité (%) du bas du gradient" -#: windowPreview.js:938 -msgid "Move to current Workspace" -msgstr "Déplacer vers l'espace de travail courrant" - msgid "Weekday" msgstr "Jour de la semaine" @@ -1501,12 +1609,36 @@ msgstr "Afficher le Bureau – icône" msgid "Show Desktop button padding (px)" msgstr "Afficher le Bureau - rembourrage du bouton (px)" -msgid "Floating rounded theme" -msgstr "Thème arrondi flottant" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isoler les espaces de travail et les moniteurs dans les paramètres de Bascule entre les applications" +#~ msgid "Unavailable when gnome-shell top panel is present" +#~ msgstr "Indisponible quand le panneau de gnome-shell est présent" + +#~ msgid "Unavailable when the panel on the primary monitor is at the top" +#~ msgstr "Indisponible quand le panneau sur l'écran principal est au haut" + +#~ msgid "Nothing yet!" +#~ msgstr "Rien pour l'instant !" + +#~ msgid "For real..." +#~ msgstr "Pour vrai..." + +#~ msgid "Advanced Options" +#~ msgstr "Options avancées" + +#~ msgid "Terminal" +#~ msgstr "Terminal" + +#~ msgid "Extensions" +#~ msgstr "Extensions" + +#~ msgid "Top Bar" +#~ msgstr "Panneau du haut" + +#~ msgid "Move to current Workspace" +#~ msgstr "Déplacer vers l'espace de travail courrant" + #~ msgid "Windows" #~ msgstr "Fenêtres" @@ -1659,9 +1791,6 @@ msgstr "Isoler les espaces de travail et les moniteurs dans les paramètres de B #~ msgid "Display favorite applications on all monitors" #~ msgstr "Afficher les applications favorites sur tous les écrans" -#~ msgid "Display the clock on all monitors" -#~ msgstr "Afficher l'horloge sur tous les écrans" - #~ msgid "Display the status menu on all monitors" #~ msgstr "Afficher le menu système sur tous les écrans" @@ -1674,9 +1803,6 @@ msgstr "Isoler les espaces de travail et les moniteurs dans les paramètres de B #~ msgid "Show Applications icon" #~ msgstr "Afficher l'icône Applications" -#~ msgid "Highlight color" -#~ msgstr "Couleur de surlignement" - #~ msgid "Preview timeout on icon leave (ms)" #~ msgstr "Délai de l'aperçu lorsqu'on quitte l'icône (ms)" diff --git a/po/ga.po b/po/ga.po index d105904..dac8511 100644 --- a/po/ga.po +++ b/po/ga.po @@ -21,6 +21,3 @@ msgstr "Taispeáin deilbhín Deisce" msgid "Show Desktop button padding (px)" msgstr "Stuáil Taispeáin cnaipe Deisce (px)" - -msgid "Floating rounded theme" -msgstr "Téama chothromú ar snámh" diff --git a/po/gl.po b/po/gl.po index 10485b9..445f6bc 100644 --- a/po/gl.po +++ b/po/gl.po @@ -1505,9 +1505,6 @@ msgstr "Mostrar iconas na escritorio" msgid "Show Desktop button padding (px)" msgstr "Recheo Mostrar o botón do escritorio (px)" -msgid "Floating rounded theme" -msgstr "Tema redondeado flotante" - #~ msgid "Top, with plugin icons collapsed to bottom" #~ msgstr "Arriba, coas iconas contraídas" diff --git a/po/gu.po b/po/gu.po index 560d76e..8070cb5 100644 --- a/po/gu.po +++ b/po/gu.po @@ -21,6 +21,3 @@ msgstr "ડેસ્કટ .પ ચિહ્ન બતાવો" msgid "Show Desktop button padding (px)" msgstr "ડેસ્કટtopપ બટન બતાવો પેડિંગ (પીએક્સ)" - -msgid "Floating rounded theme" -msgstr "ફ્લોટિંગ ગોળાકાર થીમ" diff --git a/po/he.po b/po/he.po index e282b11..5afd434 100644 --- a/po/he.po +++ b/po/he.po @@ -21,6 +21,3 @@ msgstr "הצגת סמל שולחן העבודה" msgid "Show Desktop button padding (px)" msgstr "ריפוד הצגת כפתור שולחן עבודה (פיקסלים)" - -msgid "Floating rounded theme" -msgstr "ערכת נושא מעוגלת עם ריחוף" diff --git a/po/hi.po b/po/hi.po index b1d1674..c33a6ba 100644 --- a/po/hi.po +++ b/po/hi.po @@ -22,8 +22,5 @@ msgstr "डेस्कटॉप आइकन दिखाएं" msgid "Show Desktop button padding (px)" msgstr "डेस्कटॉप बटन दिखाएं पैडिंग (पीएक्स)" -msgid "Floating rounded theme" -msgstr "फ़्लोटिंग गोलाकार थीम" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "एप्लिकेशन स्विचिंग सेटिंग्स में कार्यस्थलों और मॉनिटर को अलग करें" diff --git a/po/hr.po b/po/hr.po index 159fa97..109af40 100644 --- a/po/hr.po +++ b/po/hr.po @@ -21,6 +21,3 @@ msgstr "Prikaži ikonu radne površine" msgid "Show Desktop button padding (px)" msgstr "Podmetač Prikaži gumb radne površine (px)" - -msgid "Floating rounded theme" -msgstr "Plutajuća zaobljena tema" diff --git a/po/hu.po b/po/hu.po index f041c08..8accb64 100644 --- a/po/hu.po +++ b/po/hu.po @@ -1503,9 +1503,6 @@ msgstr "Asztali ikon megjelenítése" msgid "Show Desktop button padding (px)" msgstr "Asztal megjelenítése gomb margója (px)" -msgid "Floating rounded theme" -msgstr "Lebegő lekerekített téma" - #~ msgid "Current Show Applications icon" #~ msgstr "Jelenlegi „Alkalmazások megjelenítése” ikon" diff --git a/po/id.po b/po/id.po index 3f35243..015faff 100644 --- a/po/id.po +++ b/po/id.po @@ -22,8 +22,5 @@ msgstr "Tampilkan ikon desktop" msgid "Show Desktop button padding (px)" msgstr "Tampilkan tombol desktop padding (px)" -msgid "Floating rounded theme" -msgstr "Tema bulat mengambang" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isolasi Ruang Kerja dan Monitor dalam pengaturan Pengalihan Aplikasi" diff --git a/po/is.po b/po/is.po index af7e151..a376950 100644 --- a/po/is.po +++ b/po/is.po @@ -21,6 +21,3 @@ msgstr "Sýna skjáborðstáknið" msgid "Show Desktop button padding (px)" msgstr "„Sýna skjáborðshnapp“ padding (px)" - -msgid "Floating rounded theme" -msgstr "Fljótandi ávalið þema" diff --git a/po/it.po b/po/it.po index 5dae0d7..380a642 100644 --- a/po/it.po +++ b/po/it.po @@ -1428,9 +1428,6 @@ msgstr "Mostra le icone del Desktop" msgid "Show Desktop button padding (px)" msgstr "Spaziatura del pulsante Mostra Desktop (px)" -msgid "Floating rounded theme" -msgstr "Tema arrotondato fluttuante" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isola aree di lavoro e monitor nelle impostazioni di Cambio applicazione" diff --git a/po/ja.po b/po/ja.po index c489804..b61434b 100644 --- a/po/ja.po +++ b/po/ja.po @@ -1492,9 +1492,6 @@ msgstr "デスクトップアイコンを表示" msgid "Show Desktop button padding (px)" msgstr "デスクトップボタンを表示 余白 (px)" -msgid "Floating rounded theme" -msgstr "フローティングラウンドテーマ" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "アプリケーションの切り替え設定でワークスペースとモニターを分離する" diff --git a/po/ka.po b/po/ka.po index 12930b4..5df5faa 100644 --- a/po/ka.po +++ b/po/ka.po @@ -15,6 +15,3 @@ msgstr "აჩვენე დესკტოპის იკონები" msgid "Show Desktop button padding (px)" msgstr "ღილაკი აჩვენე დესკტოპი" - -msgid "Floating rounded theme" -msgstr "მოფარფატე მომრგვალო თემა" diff --git a/po/kn.po b/po/kn.po index 5bd07f0..a8d0dc9 100644 --- a/po/kn.po +++ b/po/kn.po @@ -21,6 +21,3 @@ msgstr "ಡೆಸ್ಕ್ಟಾಪ್ ಐಕಾನ್ ತೋರಿಸಿ" msgid "Show Desktop button padding (px)" msgstr "ಡೆಸ್ಕ್ಟಾಪ್ ಬಟನ್ ತೋರಿಸು ಪ್ಯಾಡಿಂಗ್ (ಪಿಎಕ್ಸ್)" - -msgid "Floating rounded theme" -msgstr "ತೇಲುವ ದುಂಡಾದ ಥೀಮ್" diff --git a/po/ko.po b/po/ko.po index 6188bf3..2213a75 100644 --- a/po/ko.po +++ b/po/ko.po @@ -1469,9 +1469,6 @@ msgstr "바탕 화면 아이콘 표시" msgid "Show Desktop button padding (px)" msgstr "바탕 화면 버튼 표시 패딩(px)" -msgid "Floating rounded theme" -msgstr "떠 다니는 둥근 테마" - #~ msgid "Current Show Applications icon" #~ msgstr "현재 프로그램 표시 아이콘" diff --git a/po/lt.po b/po/lt.po index 20956d4..f169353 100644 --- a/po/lt.po +++ b/po/lt.po @@ -21,6 +21,3 @@ msgstr "Rodyti Darbalaukio ikoną" msgid "Show Desktop button padding (px)" msgstr "Rodyti Darbalaukio mygtuką" - -msgid "Floating rounded theme" -msgstr "Plūduriuojanti apskrita tema" diff --git a/po/lv.po b/po/lv.po index fef070b..847b547 100644 --- a/po/lv.po +++ b/po/lv.po @@ -21,6 +21,3 @@ msgstr "Rādīt darbvirsmas ikonu" msgid "Show Desktop button padding (px)" msgstr "Rādīt darbvirsmas pogu polsterējums (px)" - -msgid "Floating rounded theme" -msgstr "Peldoša noapaļota tēma" diff --git a/po/mk.po b/po/mk.po index ca8323d..96e8510 100644 --- a/po/mk.po +++ b/po/mk.po @@ -18,6 +18,3 @@ msgstr "Покажете ја иконата за работната површ msgid "Show Desktop button padding (px)" msgstr "Пополнување „Покажи копче за работна површина“ (px)" - -msgid "Floating rounded theme" -msgstr "Лебдечка заоблена тема" diff --git a/po/ms.po b/po/ms.po index c1ef357..b2a1c36 100644 --- a/po/ms.po +++ b/po/ms.po @@ -21,6 +21,3 @@ msgstr "Tunjukkan ikon Atas Meja" msgid "Show Desktop button padding (px)" msgstr "Pemadatan Tunjuk butang Atas Meja (px)" - -msgid "Floating rounded theme" -msgstr "Tema bundar terapung" diff --git a/po/ne.po b/po/ne.po index 69f286f..d1f5f27 100644 --- a/po/ne.po +++ b/po/ne.po @@ -21,6 +21,3 @@ msgstr "डेस्कटप प्रतिमा देखाउनुहो msgid "Show Desktop button padding (px)" msgstr "डेस्कटप बटन देखाउनुहोस् प्याडि ((px)" - -msgid "Floating rounded theme" -msgstr "फ्लोटिंग गोल थिम" diff --git a/po/nl.po b/po/nl.po index 229e879..94424e3 100644 --- a/po/nl.po +++ b/po/nl.po @@ -1502,9 +1502,6 @@ msgstr "Pictogram Bureaublad weergeven" msgid "Show Desktop button padding (px)" msgstr "Opvulling Pictogram bureaublad weergeven (px)" -msgid "Floating rounded theme" -msgstr "Zwevend afgerond thema" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isoleer werkruimten en monitoren in de instellingen voor het Wisselen tussen toepassingen" diff --git a/po/pa.po b/po/pa.po index 41cf3a7..9b72054 100644 --- a/po/pa.po +++ b/po/pa.po @@ -21,6 +21,3 @@ msgstr "ਡੈਸਕਟਾਪ ਆਈਕਾਨ ਵੇਖਾਓ" msgid "Show Desktop button padding (px)" msgstr "ਡੈਸਕਟਾਪ ਬਟਨ ਦਿਖਾਓ ਭਰਾਵ (px)" - -msgid "Floating rounded theme" -msgstr "ਫਲੋਟਿੰਗ ਗੋਲ ਥੀਮ" diff --git a/po/pl.po b/po/pl.po index 145f608..c1f2838 100644 --- a/po/pl.po +++ b/po/pl.po @@ -4,183 +4,2127 @@ # msgid "" msgstr "" -"Project-Id-Version: \n" +"Project-Id-Version: Dash to Panel\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-02-08 08:31-0500\n" -"PO-Revision-Date: 2019-10-11 12:34+0200\n" -"Last-Translator: Alex \n" +"POT-Creation-Date: 2025-03-10 16:25+0100\n" +"PO-Revision-Date: 2025-03-10 16:31+0100\n" +"Last-Translator: Adam Lewicki \n" "Language-Team: \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.2.1\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 3.4.4\n" -#: prefs.js:247 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:75 +msgid "Panel position (Deprecated)" +msgstr "Położenie panelu (przestarzałe)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:76 +msgid "Panel is shown on the Bottom or Top of the screen." +msgstr "Panel jest wyświetlany u dołu lub u góry ekranu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:80 +msgid "Sync element positions" +msgstr "Synchronizuj położenie elementu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:81 +msgid "Sync panel element positions on all monitors." +msgstr "Synchronizuj położenie elementów panelu na wszystkich monitorach." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:85 +msgid "Panel positions" +msgstr "Położenie panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:86 +msgid "Panel positions (JSON)." +msgstr "Położenie panelu (JSON)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:90 +msgid "Panel element positions" +msgstr "Położenie elementów panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:91 +msgid "Panel element positions (JSON)." +msgstr "Położenie elementów panelu (JSON)." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:95 +msgid "" +"Percentages of screen edge for panel to span, -1 for dynamic length (dock " +"mode)" +msgstr "" +"Procent krawędzi ekranu, na której rozciąga się panel, -1 dla dynamicznej " +"długości (tryb doku)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:96 +msgid "Length of the panels, in percent (JSON)." +msgstr "Długość paneli w procentach (JSON)." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:100 +msgid "Positions along screen edge" +msgstr "Położenia wzdłuż krawędzi ekranu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:101 +msgid "" +"Where to show the panels if it is not the full length of the screen edge " +"(JSON)." +msgstr "" +"Gdzie wyświetlać panele, jeśli nie zajmują całej długości krawędzi ekranu " +"(JSON)." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:105 +msgid "Panel sizes" +msgstr "Rozmiary paneli" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:106 +msgid "Sizes of panels, in pixels." +msgstr "Rozmiary paneli w pikselach." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:110 +msgid "Panel size (Deprecated)" +msgstr "Rozmiar panelu (przestarzałe)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:111 +msgid "Set the size of the panel." +msgstr "Ustaw rozmiar panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:115 +#: ui/BoxShowDesktopOptions.ui:53 +msgid "Override Show Desktop line color" +msgstr "Zastąp kolor linii Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:116 +msgid "Replace current Show Desktop button line color" +msgstr "Zastąp obecny kolor linii przycisku Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:120 +msgid "Custom Show Desktop line color" +msgstr "Niestandardowy kolor linii przycisku Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:121 +msgid "Custom Show Desktop button line color" +msgstr "Niestandardowy kolor linii przycisku Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:125 +msgid "Dot position" +msgstr "Położenie kropki" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:126 +msgid "Running indicators are shown on the Bottom or Top of the screen." +msgstr "" +"Wskaźniki uruchomionych programów są wyświetlane u dołu lub u góry ekranu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:130 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:131 +msgid "Style of Appicons" +msgstr "Styl ikon programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:135 +msgid "Style of the running indicator (focused)" +msgstr "Styl wskaźnika uruchomionych programów (pierwszoplanowych)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:136 +msgid "" +"Style of the running indicator for the icon for the currently focused " +"application" +msgstr "" +"Styl wskaźnika uruchomionych programów dla ich ikony programów " +"pierwszoplanowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:140 +msgid "Style of the running indicator (unfocused)" +msgstr "Styl wskaźnika uruchomionych programów (drugoplanowych)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:141 +msgid "" +"Style of the running indicator for the icon for applications which are not " +"currently focused" +msgstr "" +"Styl wskaźnika uruchomionych programów dla ich ikon, które nie są aktualnie " +"na pierwszym planie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:145 +msgid "Running indicator dominant color" +msgstr "Dominujący kolor wskaźnika uruchomienia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:146 +msgid "Whether to use the app icon's dominant color for .app-well-running-dot" +msgstr "Używanie dominującego koloru ikony aplikacji dla .app-well-running-dot" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:150 +msgid "Running indicator color override" +msgstr "Zastąpienie koloru wskaźnika uruchomionych programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:151 +msgid "" +"Whether to override the theme background color for .app-well-running-dot" +msgstr "Zastąpienie koloru tła motywu dla .app-well-running-dot" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:155 +msgid "Color of running indicator (1 window)" +msgstr "Kolor wskaźnika uruchomienia (1 okno)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:156 +msgid "" +"Customize the color of the running indicator when one window is running for " +"that application" +msgstr "" +"Dostosuj kolor wskaźnika uruchomionych programów, gdy dla danego programu " +"jest uruchomione jedno okno" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:160 +msgid "Color of running indicator (2 windows)" +msgstr "Kolor wskaźnika uruchomienia (2 okna)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:161 +msgid "" +"Customize the color of the running indicator when two windows are running " +"for that application" +msgstr "" +"Dostosuj kolor wskaźnika uruchomionych programów, gdy dla danego programu są " +"uruchomione dwa okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:165 +msgid "Color of running indicator (3 windows)" +msgstr "Kolor wskaźnika uruchomienia (3 okna)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:166 +msgid "" +"Customize the color of the running indicator when three windows are running " +"for that application" +msgstr "" +"Dostosuj kolor wskaźnika uruchomionych programów, gdy dla danego programu są " +"uruchomione trzy okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:170 +msgid "Color of running indicator (4+ windows)" +msgstr "Kolor wskaźnika uruchomienia (4+ okien)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:171 +msgid "" +"Customize the color of the running indicator when four or more windows are " +"running for that application" +msgstr "" +"Dostosuj kolor wskaźnika uruchomionych programów, gdy dla danego programu są " +"uruchomione cztery lub więcej okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:175 +msgid "Unfocused color is different than focused" +msgstr "Kolor programów drugoplanowych jest inny niż pierwszoplanowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:176 +msgid "" +"Whether to apply a 2nd color scheme to the indicator when the app is not " +"focused" +msgstr "" +"Drugi schemat kolorów do wskaźnika, gdy aplikacja nie jest na pierwszym " +"planie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:180 +msgid "Color of unfocused running indicator (1 window)" +msgstr "Kolor wskaźnika drugoplanowych uruchomionych programów (1 okno)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:181 +msgid "" +"Customize the color of the unfocused running indicator when one window is " +"running for that application" +msgstr "" +"Dostosuj kolor wskaźnika drugoplanowych uruchomionych programów, gdy dla " +"danej aplikacji jest uruchomione jedno okno" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:185 +msgid "Color of unfocused running indicator (2 windows)" +msgstr "Kolor wskaźnika drugoplanowych uruchomionych programów (2 okna)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:186 +msgid "" +"Customize the color of the unfocused running indicator when two windows are " +"running for that application" +msgstr "" +"Dostosuj kolor wskaźnika drugoplanowych programów, gdy dla danej aplikacji " +"są uruchomione dwa okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:190 +msgid "Color of unfocused running indicator (3 windows)" +msgstr "Kolor wskaźnika drugoplanowych uruchomionych programów (3 okna)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:191 +msgid "" +"Customize the color of the unfocused running indicator when three windows " +"are running for that application" +msgstr "" +"Dostosuj kolor wskaźnika drugoplanowych uruchomionych programów, gdy dla " +"danej aplikacji są uruchomione trzy okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:195 +msgid "Color of unfocused running indicator (4+ windows)" +msgstr "Kolor wskaźnika drugoplanowych uruchomionych programów (4+ okien)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:196 +msgid "" +"Customize the color of the unfocused running indicator when four or more " +"windows are running for that application" +msgstr "" +"Dostosuj kolor wskaźnika drugoplanowych uruchomionych programów, gdy dla " +"danej aplikacji są uruchomione cztery lub więcej okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:200 +msgid "Running indicator height" +msgstr "Wysokość wskaźnika uruchomionych programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:201 +msgid "Height of the running indicator line/diameter of window indicator dots" +msgstr "" +"Wysokość linii wskaźnika uruchomionych programów/średnica kropek wskaźnika " +"okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:205 +msgid "Highlight icon of focused application" +msgstr "Podświetl ikonę pierwszoplanowego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:206 +msgid "" +"Whether to highlight the background of the currently focused application's " +"icon" +msgstr "Podświetlenie tła ikony aktualnego pierwszoplanowego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:210 +msgid "Highlight icon dominant color" +msgstr "Podświetl dominujący kolor ikony" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:211 +msgid "Base the active window highlight color on that application's icon" +msgstr "Oparcie koloru podświetlenia aktywnego okna na ikonie tego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:215 +msgid "Color of highlight of focused application" +msgstr "Kolor podświetlenia programu pierwszoplanowego" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:216 +msgid "Customize the color of the highlight of the focused application" +msgstr "Dostosuj kolor podświetlenia pierwszoplanowego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:220 +msgid "Opacity of highlight of focused application" +msgstr "Przezroczystość podświetlenia programu pierwszoplanowego" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:221 +msgid "Customize the opacity of the highlight of the focused application" +msgstr "Dostosuj przezroczystość podświetlenia pierwszoplanowej aplikacji" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:225 +msgid "Keep dash" +msgstr "Zachowaj panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:226 +msgid "Whether to keep the stock gnome-shell dash while in overview" +msgstr "Zachowuj domyślny panel powłoki GNOME podczas przeglądu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:230 +msgid "Keep top panel" +msgstr "Zachowaj górny panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:231 +msgid "Whether to keep the stock gnome-shell top panel" +msgstr "Zachowaj domyślny górny panel powłoki GNOME" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:235 +msgid "Panel menu buttons require click" +msgstr "Przyciski menu panelu wymagają kliknięcia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:236 +msgid "Whether to activate the panel menu buttons on hover or on click" +msgstr "" +"Aktywacja przycisków menu panelu po najechaniu kursorem, lub po kliknięciu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:240 +msgid "Force hot corner" +msgstr "Wymuś gorący róg" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:241 +msgid "Wheter to force having an Activities hot corner on the primary monitor" +msgstr "Wymuś posiadanie aktywnego rogu Aktywności na monitorze głównym" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:245 +msgid "Lock the taskbar" +msgstr "Zablokuj pasek zadań" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:246 +msgid "Specifies if the user can modify the taskbar" +msgstr "Określa, czy użytkownik może modyfikować pasek zadań" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:250 +msgid "Panel top and bottom margins" +msgstr "Marginesy górny i dolny panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:254 +msgid "Panel side margins" +msgstr "Marginesy boczne panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:258 +msgid "Panel top and bottom padding" +msgstr "Wypełnienie górne i dolne panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:262 +msgid "Panel sides padding" +msgstr "Wypełnienie boków panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:266 +msgid "Override theme background color" +msgstr "Zastąp kolor tła motywu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:267 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:277 +msgid "Replace current theme background color for the panel" +msgstr "Zastąp aktualny kolor tła motywu dla panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:271 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:276 +msgid "Custom background color" +msgstr "Niestandardowy kolor tła" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:272 +msgid "Custom background color for the panel" +msgstr "Niestandardowy kolor tła panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:281 +msgid "Dynamic opacity" +msgstr "Dynamiczna przezroczystość" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:282 +msgid "Enable dynamic opacity" +msgstr "Włącz dynamiczną przezroczystość" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:286 +msgid "Panel opacity" +msgstr "Przezroczystość panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:287 +msgid "Custom opacity for the panel" +msgstr "Niestandardowa przezroczystość panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:291 +msgid "Dynamic opacity behavior" +msgstr "Zachowanie dynamicznej przezroczystości" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:292 +msgid "Dictates which window type affects the panel opacity" +msgstr "Wpływ typu okna wpływa na przezroczystość panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:296 +msgid "Distance to change opacity" +msgstr "Odległość zmiany przezroczystości" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:297 +msgid "The distance a window needs to be from the panel to change opacity" +msgstr "" +"Odległość, jaką okno musi osiągnąć od panelu, aby zmienić przezroczystość" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:301 +msgid "Modified panel opacity" +msgstr "Zmodyfikowana przezroczystość panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:302 +msgid "Modified opacity for the panel when a window is near" +msgstr "Zmodyfikowana przezroczystość panelu, gdy okno znajduje się w pobliżu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:306 +msgid "Opacity change duration" +msgstr "Czas trwania zmiany przezroczystości" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:307 +msgid "The duration of the animation when the opacity changes" +msgstr "Czas trwania animacji zmiany przezroczystości" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:311 +msgid "Custom gradient" +msgstr "Niestandardowy gradient" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:312 +msgid "Replace current theme gradient for the panel" +msgstr "Zastąp aktualny gradient motywu dla panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:316 +msgid "Custom gradient top color" +msgstr "Niestandardowy górny kolor gradientu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:317 +msgid "Custom gradient top color for the panel" +msgstr "Niestandardowy górny kolor gradientu panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:321 +msgid "Custom gradient top opacity" +msgstr "Niestandardowa górna przezroczystość gradientu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:322 +msgid "Custom gradient top opacity for the panel" +msgstr "Niestandardowa górna przezroczystość gradientu panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:326 +msgid "Custom gradient bottom color" +msgstr "Niestandardowy dolny kolor gradientu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:327 +msgid "Custom gradient bottom color for the panel" +msgstr "Niestandardowy dolny kolor gradientu panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:331 +msgid "Custom gradient bottom opacity" +msgstr "Niestandardowa dolna przezroczystość gradientu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:332 +msgid "Custom gradient bottom opacity for the panel" +msgstr "Niestandardowa dolna przezroczystość gradientu panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:336 +msgid "Intellihide" +msgstr "Inteligentne ukrywanie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:337 +msgid "Whether to intelligently hide the panel" +msgstr "Inteligentne ukrywanie panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:341 +msgid "Only hide from windows" +msgstr "Ukrywaj tylko przed oknami" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:342 +msgid "Dictates if the dash should only hide when in conflict with windows" +msgstr "" +"Określa, czy panel powinien być ukrywany tylko w przypadku kolizji z oknami" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:346 +msgid "Intellihide behaviour" +msgstr "Zachowanie inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:347 +msgid "Dictates how to intelligently hide the panel" +msgstr "Określa, jak inteligentnie ukrywać panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:351 +msgid "Intellihide pressure" +msgstr "Nacisk inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:352 +msgid "" +"To reveal the panel, pressure needs to be applied to the edege of the screen" +msgstr "Aby odsłonić panel, należy wywrzeć nacisk na krawędź ekranu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:356 +msgid "Intellihide pressure threshold" +msgstr "Próg nacisku inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:357 +msgid "The pressure needed to reveal the panel" +msgstr "Nacisk potrzebny do odsłonięcia panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:361 +msgid "Intellihide pressure time" +msgstr "Czas nacisku inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:362 +msgid "" +"The numer of milliseconds that the pressure needs to be applied to reveal " +"the panel" +msgstr "" +"Liczba milisekund, przez które należy wywierać nacisk, aby odsłonić panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:366 +msgid "Allow revealing the panel while in fullscreen" +msgstr "Zezwalaj na ujawnienie panelu w trybie pełnoekranowym" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:367 +msgid "" +"Allow the panel to be revealed while an application is in fullscreen mode" +msgstr "" +"Zezwalaj na ujawnienie panelu, gdy program jest w trybie pełnoekranowym" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:371 +msgid "Reveal the panel on notification" +msgstr "Odsłoń panel przy powiadomieniu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:375 +msgid "Intellihide only secondary" +msgstr "Inteligentne ukrywanie tylko na dodatkowych monitorach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:376 +msgid "Whether to only hide secondary panels" +msgstr "Ukryj tylko panele dodatkowe" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:380 +msgid "Intellihide animation time" +msgstr "Czas trwania animacji inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:381 +msgid "The animation time (ms) to hide and reveal the panel" +msgstr "Czas animacji (ms) ukrywania i odkrywania panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:385 +msgid "Intellihide close delay" +msgstr "Opóźnienie zamykania inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:386 +msgid "The delay (ms) before hiding the panel" +msgstr "Opóźnienie (ms) przed ukryciem panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:390 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:395 +msgid "Keybinding toggle intellihide" +msgstr "Skrót klawiszowy do przełączania inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:391 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:396 +msgid "Keybinding to reveal the panel while in intellihide mode" +msgstr "" +"Skrót klawiszowy do odsłonięcia panelu w trybie inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:400 +msgid "Persisted intellihide hold status. -1 means the option is disabled" +msgstr "" +"Utrwalony stan przytrzymania inteligentnego ukrywania. -1 oznacza, że opcja " +"jest wyłączona" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:404 +msgid "Intellihide enable start delay" +msgstr "Opóźnienie włączenia inteligentnego ukrywania" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:405 +msgid "The delay before enabling intellihide on start" +msgstr "Opóźnienie przed włączeniem inteligentnego ukrywania przy starcie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:409 +msgid "Custom Show Applications icon" +msgstr "Niestandardowa ikona Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:410 +msgid "Customize the Show Applications icon" +msgstr "Dostosuj ikonę Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:414 +msgid "Show Applications icon side padding" +msgstr "Wypełnienie boczne ikony Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:415 +msgid "Customize the Show Applications icon side padding" +msgstr "Dostosuj odstęp ikony Pokaż programy od krawędzi" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:419 +msgid "Override escape key" +msgstr "Zastąp klawisz ESC" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:420 +msgid "" +"Override the escape key to return to the desktop when entering the overview " +"using the Show Applications button" +msgstr "" +"Zastąp klawisz ESC, aby powrócić do pulpitu podczas wchodzenia do podglądu " +"za pomocą przycisku Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:424 +msgid "Show Apps button context menu commands" +msgstr "Polecenia menu kontekstowego przycisku Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:425 +msgid "Commands to add to the Show Apps button right click menu" +msgstr "Polecenia do dodania do menu prawego przycisku myszy Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:429 +msgid "Show Apps button context menu titles" +msgstr "Tytuły menu kontekstowego przycisku Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:430 +msgid "Titles for commands added to Show Apps button right click menu" +msgstr "Tytuły poleceń dodanych do menu prawego przycisku myszy Pokaż programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:434 +msgid "Panel context menu commands" +msgstr "Polecenia menu kontekstowego panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:435 +msgid "Commands to add to the panel right click menu" +msgstr "Polecenia do dodania do menu prawego przycisku myszy panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:439 +msgid "Panel context menu titles" +msgstr "Tytuły menu kontekstowego panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:440 +msgid "Titles for commands added to panel right click menu" +msgstr "Tytuły poleceń dodanych do menu prawego przycisku myszy panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:444 +msgid "Show activities button" +msgstr "Pokaż przycisk Aktywności" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:445 +msgid "Show activities button on the left hand side of the taskbar" +msgstr "Pokaż przycisk Aktywności po lewej stronie paska zadań" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:449 +msgid "Width of show Desktop button" +msgstr "Szerokość przycisku Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:450 +msgid "Customize the width of the show Desktop button" +msgstr "Dostosuj szerokość przycisku Pokaż pulpit" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:454 +msgid "Show desktop on hover" +msgstr "Pokaż pulpit po najechaniu kursorem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:455 +msgid "Show the desktop on mouse hover" +msgstr "Pokaż pulpit po najechaniu myszą" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:459 +msgid "Delay show desktop" +msgstr "Opóźnienie pokazywania pulpitu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:460 +msgid "Delay before showing the desktop" +msgstr "Opóźnienie przed pokazaniem pulpitu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:464 +msgid "Show desktop animation time" +msgstr "Czas animacji pokazywania pulpitu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:465 +msgid "Window fade animation time when showing the destop" +msgstr "Czas animacji zanikania okna podczas pokazywania pulpitu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:469 +msgid "Show window preview" +msgstr "Pokaż podgląd okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:470 +msgid "Show preview of running window on hover of app icon" +msgstr "" +"Pokaż podgląd uruchomionego okna po najechaniu kursorem na ikonę programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:474 +msgid "Show tooltip" +msgstr "Pokaż etykietkę" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:475 +msgid "Show tooltip on hover of app icon" +msgstr "Pokaż etykietkę po najechaniu kursorem na ikonę programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:479 +msgid "Show running apps" +msgstr "Pokaż uruchomione programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:480 +msgid "Show or hide running application icons in the dash" +msgstr "Pokaż lub ukryj ikony uruchomionych programów w panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:484 +msgid "Show favorites apps" +msgstr "Pokaż ulubione programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:485 +msgid "Show or hide favorite application icons in the dash" +msgstr "Pokaż lub ukryj ikony ulubionych programów w panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:489 +msgid "Icon enter display time" +msgstr "Czas wyświetlania po najechaniu na ikonę" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:490 +msgid "" +"Amount of time after entering icon to wait before displaying window preview " +"if icon is not clicked or mouse has not left." +msgstr "" +"Czas oczekiwania po najechaniu na ikonę, zanim zostanie wyświetlony podgląd " +"okna, jeśli ikona nie zostanie kliknięta lub kursor myszy nie opuścił " +"obszaru ikony." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:494 +msgid "Enable peek mode" +msgstr "Włącz tryb podglądu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:495 +msgid "Peek a window upon hover for some time" +msgstr "Podgląd okna po najechaniu kursorem na pewien czas" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:499 +msgid "Display title in preview" +msgstr "Wyświetl tytuł w podglądzie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:500 +msgid "Display window title in preview" +msgstr "Wyświetl tytuł okna w podglądzie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:504 +msgid "Window previews manual styling" +msgstr "Ręczne style podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:505 +msgid "This defines if the default window previews styling should be applied" +msgstr "Określa, czy powinien być stosowany domyślny styl podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:509 +msgid "Title position" +msgstr "Pozycja tytułu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:510 +msgid "Position of the window title, close button and icon in preview." +msgstr "Pozycja tytułu okna, przycisku zamknięcia i ikony w podglądzie." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:514 +msgid "Window previews title font color" +msgstr "Kolor czcionki tytułu podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:515 +msgid "This defines the window preview titles font color." +msgstr "Określa kolor czcionki tytułów podglądu okien." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:519 +msgid "Window previews title font size" +msgstr "Rozmiar czcionki tytułu podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:520 +msgid "This defines the window preview titles font size." +msgstr "Określa rozmiar czcionki tytułów podglądu okien." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:524 +msgid "Window previews use custom icon size" +msgstr "Podgląd okien używa niestandardowego rozmiaru ikony" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:525 +msgid "Window previews use custom application icon size." +msgstr "Podglądy okien używają niestandardowego rozmiaru ikony programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:529 +msgid "Window previews custom icon size" +msgstr "Niestandardowy rozmiar ikony podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:530 +msgid "Window previews custom application icon size." +msgstr "Niestandardowy rozmiar ikony programu podglądu okien." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:534 +msgid "Window previews animation time" +msgstr "Czas animacji podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:535 +msgid "This defines the window previews animation time." +msgstr "Określa czas animacji podglądu okien." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:539 +msgid "Font weight of window preview titles" +msgstr "Grubość czcionki tytułów podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:540 +msgid "" +"This defines the font weight of window preview titles. Supported values: " +"inherit (from theme), normal, lighter, bold and bolder." +msgstr "" +"Określa grubość czcionki tytułów podglądu okien. Obsługiwane wartości: " +"dziedzicz (z motywu), normalna, cieńsza, gruba i grubsza." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:544 +msgid "Window previews size" +msgstr "Rozmiar podglądów okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:545 +msgid "Preferred window previews size" +msgstr "Preferowany rozmiar podglądu okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:549 +msgid "Fixed aspect ratio X" +msgstr "Stały współczynnik proporcji X" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:550 +msgid "This defines if the window previews use a fixed aspect ratio X." +msgstr "Określa, czy podgląd okien używa stałego współczynnika proporcji X." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:554 +msgid "Fixed aspect ratio Y" +msgstr "Stały współczynnik proporcji Y" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:555 +msgid "This defines if the window previews use a fixed aspect ratio Y." +msgstr "Określa, czy podgląd okien używa stałego współczynnika proporcji Y." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:559 +msgid "Window previews padding" +msgstr "Wypełnienie podglądów okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:560 +msgid "The padding of the window previews" +msgstr "Wypełnienie podglądów okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:564 +msgid "Aspect ratio X" +msgstr "Współczynnik proporcji X" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:565 +msgid "The window previews respected aspect ratio X." +msgstr "Podglądy okien zachowują proporcje X okna." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:569 +msgid "Aspect ratio Y" +msgstr "Proporcje okna Y" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:570 +msgid "The window previews respected aspect ratio Y." +msgstr "Podglądy okien zachowują proporcje Y okna." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:574 +msgid "Immediate hide on icon click" +msgstr "Ukryj natychmiast po kliknięciu na ikonę" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:575 +msgid "" +"The window previews immediately hide when an application icon is clicked." +msgstr "Podgląd okna jest natychmiast ukrywany po kliknięciu ikony programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:579 +msgid "Provide workspace isolation" +msgstr "Zapewnij izolację obszaru roboczego" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:580 +msgid "Dash shows only windows from the current workspace" +msgstr "Panel wyświetla tylko okna z bieżącego obszaru roboczego" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:584 +msgid "Close overview by clicking in empty space" +msgstr "Zamknij przegląd, klikając w puste miejsce" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:585 +msgid "Close overview or app grid by clicking in empty space" +msgstr "Zamknij przegląd lub siatkę programów, klikając w puste miejsce" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:589 +msgid "Hide overview on startup" +msgstr "Ukryj przegląd przy uruchomieniu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:590 +msgid "" +"Hide overview on startup, which would be shown by default at gnome startup." +msgstr "" +"Ukryj przegląd podczas uruchamiania, który będzie domyślnie wyświetlany " +"podczas uruchamiania środowiska GNOME." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:594 +msgid "Group applications" +msgstr "Grupa programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:595 +msgid "Dash groups the application instances under the same icon" +msgstr "Panel grupuje instancje programów pod tą samą ikoną" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:599 +msgid "Application title font size" +msgstr "Rozmiar czcionki tytułu programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:600 +msgid "" +"When the applications are ungrouped, this defines the application titles " +"font size." +msgstr "" +"Gdy programy są rozgrupowane, definiuje rozmiar czcionki tytułów programów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:604 +#: ui/BoxGroupAppsOptions.ui:47 +msgid "Font weight of application titles" +msgstr "Grubość czcionki tytułów programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:605 +msgid "" +"When the applications are ungrouped, this defines font weight of application " +"titles. Supported values: inherit (from theme), normal, lighter, bold and " +"bolder." +msgstr "" +"Gdy programy są rozgrupowane, definiuje grubość czcionki tytułów aplikacji. " +"Obsługiwane wartości: dziedzicz (z motywu), normalna, cieńsza, gruba i " +"grubsza." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:609 +msgid "Application title font color" +msgstr "Kolor czcionki tytułu programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:610 +msgid "" +"When the applications are ungrouped, this defines the application titles " +"font color." +msgstr "" +"Gdy programy są rozgrupowane, ta opcja definiuje kolor czcionki ich tytułów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:614 +msgid "Minimized application title font color" +msgstr "Kolor czcionki tytułu zminimalizowanego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:615 +msgid "" +"When the applications are ungrouped, this defines the titles font color for " +"minimized applications." +msgstr "" +"Gdy programy są rozgrupowane, ta opcja definiuje kolor czcionki tytułów " +"zminimalizowanych programów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:619 +msgid "Application title max width" +msgstr "Maksymalna szerokość tytułu programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:620 +msgid "" +"When the applications are ungrouped, this defines the application titles " +"maximum width." +msgstr "" +"Gdy programy są rozgrupowane, definiuje maksymalną szerokość ich tytułów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:624 +#: ui/BoxGroupAppsOptions.ui:111 +msgid "Use a fixed width for the application titles" +msgstr "Użyj stałej szerokości tytułów programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:625 +#: ui/BoxGroupAppsOptions.ui:112 +msgid "" +"The application titles all have the same width, even if their texts are " +"shorter than the maximum width. The maximum width value is used as the fixed " +"width." +msgstr "" +"Wszystkie tytuły programów będą miały tę samą szerokość, nawet jeśli ich " +"teksty są krótsze niż maksymalna szerokość. Wartość stałej szerokości jest " +"taka, jaka została podana w polu Maksymalna szerokość." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:629 +#: ui/BoxGroupAppsOptions.ui:129 +msgid "Display running indicators on unfocused applications" +msgstr "Wyświetl wskaźnik dla otwartych okien drugoplanowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:630 +msgid "" +"When the applications are ungrouped, this defines if running applications " +"should display an indicator." +msgstr "" +"Określa, czy po rozgrupowaniu programów, uruchomione programy mają " +"wyświetlać wskaźnik." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:634 +msgid "Use favorite icons as application launchers" +msgstr "Użyj ulubionych ikon jako uruchamiających programy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:635 +msgid "" +"When the applications are ungrouped, this defines if running applications " +"stay separate from the favorite icons." +msgstr "" +"Po rozgrupowaniu programów opcja ta określa, czy uruchomione programy mają " +"być oddzielone od ikon ulubionych." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:639 +msgid "Primary monitor index" +msgstr "Indeks Głównego monitora" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:640 +msgid "Specifies the id of the primary monitor." +msgstr "Określa identyfikator monitora głównego." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:644 +#: ui/SettingsPosition.ui:35 +msgid "Display panels on all monitors" +msgstr "Wyświetl panel na wszystkich monitorach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:645 +msgid "Specifies if a panel is shown on every monitors" +msgstr "Określa, czy panel jest wyświetlany na każdym monitorze" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:649 +msgid "Provide monitor isolation" +msgstr "Zapewnij izolację monitora" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:650 +msgid "Dash shows only windows from the current monitor" +msgstr "Panel pokazuje tylko okna z bieżącego monitora" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:654 +msgid "Display the favorites on all monitors" +msgstr "Wyświetl ulubione na wszystkich monitorach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:655 +msgid "" +"Specifies if every panel should display the favorite applications. If false, " +"the favorite appplications are only displayed on the primary monitor." +msgstr "" +"Określa, czy każdy panel powinien wyświetlać ulubione programy. Jeśli nie, " +"ulubione programy są wyświetlane tylko na głównym monitorze." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:659 +msgid "Customize click behaviour" +msgstr "Dostosuj zachowanie kliknięcia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:660 +msgid "Customize action on various mouse events" +msgstr "Dostosuj działanie w przypadku różnych zdarzeń myszy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:664 +msgid "Minimize on shift+click" +msgstr "Minimalizuj po naciśnięciu Shift+kliknięcie myszą" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:668 +msgid "Activate only one window" +msgstr "Aktywuj tylko jedno okno" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:672 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:682 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:687 +msgid "Action when clicking on a running app" +msgstr "Akcja po kliknięciu uruchomionego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:673 +msgid "" +"Set the action that is executed when clicking on the icon of a running " +"application" +msgstr "" +"Ustaw akcję, która zostanie wykonana po kliknięciu ikony uruchomionego " +"programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:677 +msgid "Action when shift+clicking on a running app" +msgstr "" +"Akcja po kliknięciu z wciśniętym klawiszem Shift na uruchomionym programie" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:678 +msgid "" +"Set the action that is executed when shift+clicking on the icon of a running " +"application" +msgstr "" +"Ustaw akcję wykonywaną po kliknięciu ikony uruchomionego programu z " +"wciśniętym klawiszem Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:683 +msgid "" +"Set the action that is executed when middle-clicking on the icon of a " +"running application" +msgstr "" +"Ustaw akcję, która zostanie wykonana po kliknięciu środkowym przyciskiem " +"myszy ikony uruchomionego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:688 +msgid "" +"Set the action that is executed when shift+middle-clicking on the icon of a " +"running application" +msgstr "" +"Ustaw akcję wykonywaną po naciśnięciu Shift+środkowy przycisk myszy na " +"ikonie uruchomionego programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:692 +msgid "Action when scrolling over the panel" +msgstr "Akcja podczas przewijania nad panelem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:693 +msgid "Set the action that is executed when scrolling over the panel" +msgstr "Ustaw akcję, która zostanie wykonana po najechaniu kursorem na panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:716 +msgid "User defined context menu entries" +msgstr "Wpisy menu kontekstowego zdefiniowane przez użytkownika" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:720 +msgid "Delay between panel mouse scroll events" +msgstr "Opóźnienie między zdarzeniami przewijania myszką na panelem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:721 +msgid "Set the minimum delay between panel mouse scroll events" +msgstr "" +"Ustaw minimalne opóźnienie między zdarzeniami przewijania nad panelem myszą" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:725 +msgid "" +"Show the workspace switch head-up when workspace is changed by scrolling on " +"the panel" +msgstr "" +"Wyświetlaj powiadomienie o zmianie obszaru roboczego, gdy obszar jest " +"zmieniany przez przewijanie nad panelem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:729 +msgid "Action when scrolling over a running app" +msgstr "Akcja podczas przewijania kursorem nad uruchomionym programem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:730 +msgid "" +"Set the action that is executed when scrolling over a running application" +msgstr "" +"Ustaw akcję, która jest wykonywana podczas przewijania kursorem nad " +"uruchomionym programem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:734 +msgid "Delay between icon mouse scroll events" +msgstr "Opóźnienie między zdarzeniami przewijania myszą nad ikoną" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:735 +msgid "Set the minimum delay between icon mouse scroll events" +msgstr "" +"Ustaw minimalne opóźnienie między zdarzeniami przewijania myszą nad ikoną" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:739 +msgid "Icon leave preview timeout" +msgstr "Limit czasu podglądu po opuszczeniu ikony" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:740 +msgid "" +"Amount of time to leave preview windows open when the mouse has left the " +"application's icon." +msgstr "" +"Czas, przez jaki okna podglądu pozostaną otwarte po opuszczeniu ikony " +"aplikacji przez kursor myszy." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:744 +msgid "Enter window peeking mode timeout" +msgstr "Limit czasu wejścia w tryb podglądu okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:745 +msgid "" +"Amount of time of inactivity to enter the window peeking mode when the mouse " +"has entered a window preview." +msgstr "" +"Czas bezczynności potrzebny do przejścia w tryb podglądu okna po najechaniu " +"kursorem myszy na podgląd okna." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:749 +#: ui/BoxWindowPreviewOptions.ui:466 +msgid "Window peeking mode opacity" +msgstr "Przezroczystość trybu podglądu okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:750 +#: ui/BoxWindowPreviewOptions.ui:467 +msgid "" +"All windows except for the peeked one have their opacity set to the same " +"value." +msgstr "" +"Wszystkie okna, za wyjątkiem okna głównego, mają tą samą przeźroczystość." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:754 +msgid "Middle click preview to close window" +msgstr "Zamknięcie podglądu poprzez kliknięcie środkowego przycisku" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:755 +msgid "Middle click on the window preview to close that window" +msgstr "" +"Kliknięcie środkowym przyciskiem myszy na podglądzie okna powoduje " +"zamknięcie tego okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:759 +msgid "Window previews use custom opacity" +msgstr "Podglądy okien używają niestandardowej przezroczystości" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:760 +msgid "Window previews background use a different opacity from the panel" +msgstr "Tło podglądów okien używa innej przezroczystości niż panel" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:764 +msgid "Window previews background opacity" +msgstr "Przezroczystość tła podglądów okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:765 +msgid "Window previews use this custom background opacity." +msgstr "Podglądy okien używają tej niestandardowej przezroczystości tła." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:769 +msgid "Tray font size" +msgstr "Rozmiar czcionki zasobnika" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:770 +msgid "Set the size of the tray font. (0 for default)" +msgstr "Ustaw rozmiar czcionki zasobnika. (0 dla domyślnego)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:774 +msgid "Leftbox font size" +msgstr "Rozmiar czcionki lewego pola" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:775 +msgid "Set the size of the leftBox font. (0 for default)" +msgstr "Ustaw rozmiar czcionki lewego pola. (0 dla domyślnego)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:779 +msgid "Border radius of panel elements" +msgstr "Promień zaokrąglenia krawędzi elementów panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:783 +msgid "App icon margin" +msgstr "Margines ikony programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:784 +msgid "Set the margin for application icons in the embedded dash." +msgstr "Ustaw margines ikon programów w osadzonym panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:788 +msgid "App icon padding" +msgstr "Wypełnienie ikon programów" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:789 +msgid "Set the padding for application icons in the embedded dash." +msgstr "Ustaw odstęp wewnętrzny ikon programów w osadzonym panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:793 +msgid "Tray item padding" +msgstr "Wypełnienie elementów zasobnika" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:794 +msgid "Set the size of the tray padding. (-1 for default)" +msgstr "Ustaw odstęp wewnętrzny zasobnika. (-1 dla domyślnego)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:798 +msgid "Leftbox item padding" +msgstr "Wypełnienie elementów lewego pola" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:799 +msgid "Set the size of the leftBox padding. (-1 for default)" +msgstr "Ustaw odstęp wewnętrzny lewego pola. (-1 dla domyślnego)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:803 +msgid "Status icon padding" +msgstr "Wypełnienie ikony stanu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:804 +msgid "" +"Set the size of the aggregate (status) menu icon padding. (-1 for default)" +msgstr "" +"Ustaw odstęp wewnętrzny ikony menu zbiorczego (stanu). (-1 dla domyślnego)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:808 +msgid "Animate running indicator when open/closing/switching applications" +msgstr "" +"Animuj wskaźnik uruchomienia podczas otwierania/zamykania/przełączania " +"programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:812 +msgid "Animate when new window launched" +msgstr "Animuj przy uruchamianiu nowego okna" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:816 +msgid "Animate app icon on hover" +msgstr "Animuj ikonę aplikacji po najechaniu kursorem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:820 +msgid "App icon hover animation type" +msgstr "Typ animacji ikony programu po najechaniu kursorem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:824 +msgid "" +"App icon hover animation curve convexity (1 is linear, more is convex, less " +"is concave)" +msgstr "" +"Wypukłość krzywej animacji najechania kursorem na ikonę programu (1 to " +"liniowa, więcej to wypukła, mniej to wklęsła)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:828 +msgid "App icon hover animation duration in milliseconds" +msgstr "" +"Czas trwania animacji ikony programu po najechaniu kursorem w milisekundach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:832 +msgid "App icon hover animation extent (maximum number of animated icons)" +msgstr "" +"Zasięg animacji ikony programu po najechaniu kursorem (maksymalna liczba " +"animowanych ikon)" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:836 +msgid "App icon hover animation rotation in degrees" +msgstr "Obrót ikony programu po najechaniu kursorem w stopniach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:840 +msgid "" +"App icon hover animation travel translation in relation to the app icon size" +msgstr "" +"Przesunięcie animacji najechania kursorem na ikonę programu w odniesieniu do " +"rozmiaru ikony programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:844 +msgid "App icon hover animation zoom scale in relation to the app icon size" +msgstr "" +"Skala powiększenia animacji najechania kursorem na ikonę programu w " +"odniesieniu do rozmiaru ikony programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:848 +msgid "Highlight app icon on hover" +msgstr "Podświetl ikonę programu po najechaniu kursorem" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:852 +msgid "Highlight color" +msgstr "Kolor podświetlenia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:856 +msgid "Mouse down highlight color" +msgstr "Kolor podświetlenia przy naciśnięciu myszy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:860 +msgid "Highlight border radius" +msgstr "Promień zaokrąglenia obramowania podświetlenia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:864 +msgid "Integrate items from the gnome appmenu into the right click menu" +msgstr "" +"Zintegruj elementy z menu programu GNOME z menu prawego przycisku myszy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:868 +msgid "Display App Details to open Gnome Software from right click menu" +msgstr "" +"Wyświetlaj szczegóły programu, aby otworzyć Oprogramowanie GNOME z menu " +"prawego przycisku myszy" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:872 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:877 +msgid "Keybinding to show the dock and the number overlay." +msgstr "Skrót klawiszowy do wyświetlenia doku i nakładki z numerami." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:873 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:878 +msgid "Behavior depends on hotkeys-show-dock and hotkeys-overlay." +msgstr "" +"Zachowanie zależy od ustawień skrótów klawiszowych \"hotkeys-show-dock\" i " +"\"hotkeys-overlay\"." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:882 +msgid "" +"Timeout to hide the panel after showing the overlay using the shortcut, in " +"seconds" +msgstr "" +"Czas oczekiwania na ukrycie panelu po wyświetleniu nakładki za pomocą skrótu " +"klawiszowego, w sekundach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:886 +msgid "Timeout to hide the dock, in seconds" +msgstr "Czas oczekiwania na ukrycie doku, w sekundach" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:887 +msgid "Sets the time duration before the dock is hidden again." +msgstr "Ustawia czas, po którym dok zostanie ponownie ukryty." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:891 +msgid "Transitivity of the number overlay" +msgstr "Transaktywność nakładki z numerami" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:892 +msgid "You can choose between NEVER, TEMPORARILY and ALWAYS." +msgstr "Możesz wybrać pomiędzy NIGDY, TYMCZASOWO i ZAWSZE." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:896 +msgid "Super Hot-Keys" +msgstr "Skróty klawiszowe z Super" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:897 +msgid "Launch and switch between dash items using Super+(0-9)" +msgstr "" +"Uruchamiaj i przełączaj się między elementami panelu za pomocą Super+(0-9)." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:901 +msgid "Prefix to use for hotkeys" +msgstr "Prefiks do użycia dla skrótów klawiszowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:902 +msgid "You can choose between Super or SuperAlt as the prefix for hotkeys." +msgstr "" +"Możesz wybrać pomiędzy Super lub Super+Alt jako prefiks dla skrótów " +"klawiszowych." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:906 +msgid "Show overlay on secondary monitors" +msgstr "Pokaż nakładkę na dodatkowym monitorze" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:910 +msgid "Show window previews" +msgstr "Pokaż podglądy okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:911 +msgid "" +"When multiple instances of the application are available, show their window " +"previews" +msgstr "" +"Gdy dostępnych jest wiele instancji programu, wyświetl ich podglądy okien" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:915 +msgid "Hotkeys number keys" +msgstr "Klawisze numeryczne skrótów klawiszowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:916 +msgid "Which number keys are used for the hotkeys" +msgstr "Klawisze numeryczne są używane do skrótów klawiszowych" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:920 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1130 +msgid "Keybinding to launch 1st dash app" +msgstr "Skrót klawiszowy do uruchomienia 1. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:921 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1131 +msgid "Keybinding to launch 1st app." +msgstr "Skrót klawiszowy do uruchomienia 1. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:927 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1137 +msgid "Keybinding to launch 2nd dash app" +msgstr "Skrót klawiszowy do uruchomienia 2. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:928 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1138 +msgid "Keybinding to launch 2nd app." +msgstr "Skrót klawiszowy do uruchomienia 2. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:934 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1144 +msgid "Keybinding to launch 3rd dash app" +msgstr "Skrót klawiszowy do uruchomienia 3 programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:935 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1145 +msgid "Keybinding to launch 3rd app." +msgstr "Skrót klawiszowy do uruchomienia 3. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:941 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1151 +msgid "Keybinding to launch 4th dash app" +msgstr "Skrót klawiszowy do uruchomienia 4. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:942 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1152 +msgid "Keybinding to launch 4th app." +msgstr "Skrót klawiszowy do uruchomienia 4. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:948 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1158 +msgid "Keybinding to launch 5th dash app" +msgstr "Skrót klawiszowy do uruchomienia 5. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:949 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1159 +msgid "Keybinding to launch 5th app." +msgstr "Skrót klawiszowy do uruchomienia 5. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:955 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1165 +msgid "Keybinding to launch 6th dash app" +msgstr "Skrót klawiszowy do uruchomienia 5. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:956 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1166 +msgid "Keybinding to launch 6th app." +msgstr "Skrót klawiszowy do uruchomienia 6. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:962 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1172 +msgid "Keybinding to launch 7th dash app" +msgstr "Skrót klawiszowy do uruchomienia 7. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:963 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1173 +msgid "Keybinding to launch 7th app." +msgstr "Skrót klawiszowy do uruchomienia 7. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:969 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1179 +msgid "Keybinding to launch 8th dash app" +msgstr "Skrót klawiszowy do uruchomienia 8. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:970 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1180 +msgid "Keybinding to launch 8th app." +msgstr "Skrót klawiszowy do uruchomienia 8. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:976 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1186 +msgid "Keybinding to launch 9th dash app" +msgstr "Skrót klawiszowy do uruchomienia 9. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:977 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1187 +msgid "Keybinding to launch 9th app." +msgstr "Skrót klawiszowy do uruchomienia 9. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:983 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1193 +msgid "Keybinding to launch 10th dash app" +msgstr "Skrót klawiszowy do uruchomienia 10. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:984 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1194 +msgid "Keybinding to launch 10th app." +msgstr "Skrót klawiszowy do uruchomienia 10. programu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:990 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1200 +msgid "Keybinding to trigger 1st dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 1. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:991 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1201 +msgid "Keybinding to trigger 1st app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 1. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:997 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1207 +msgid "Keybinding to trigger 2nd dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 2. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:998 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1208 +msgid "Keybinding to trigger 2nd app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 2. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1004 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1214 +msgid "Keybinding to trigger 3rd dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 3. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1005 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1215 +msgid "Keybinding to trigger 3rd app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 3. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1011 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1221 +msgid "Keybinding to trigger 4th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 4. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1012 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1222 +msgid "Keybinding to trigger 4th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 4. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1018 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1228 +msgid "Keybinding to trigger 5th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 5. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1019 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1229 +msgid "Keybinding to trigger 5th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 5. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1025 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1235 +msgid "Keybinding to trigger 6th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 6. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1026 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1236 +msgid "Keybinding to trigger 6th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 6. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1032 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1242 +msgid "Keybinding to trigger 7th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 7. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1033 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1243 +msgid "Keybinding to trigger 7th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 7. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1039 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1249 +msgid "Keybinding to trigger 8th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 8. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1040 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1250 +msgid "Keybinding to trigger 8th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 8. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1046 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1256 +msgid "Keybinding to trigger 9th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 9. programu z panelu z zachowaniem klawisza " +"Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1047 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1257 +msgid "Keybinding to trigger 9th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 9. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1053 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1263 +msgid "Keybinding to trigger 10th dash app with shift behavior" +msgstr "" +"Skrót klawiszowy do uruchomienia 10. programu z panelu z zachowaniem " +"klawisza Shift" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1054 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1264 +msgid "Keybinding to trigger 10th app with shift behavior." +msgstr "" +"Skrót klawiszowy do uruchomienia 10. programu z zachowaniem klawisza Shift." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1060 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1270 +msgid "Keybinding to trigger 1st dash app" +msgstr "Skrót klawiszowy do uruchomienia 1. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1061 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1271 +msgid "Keybinding to either show or launch the 1st application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 1. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1067 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1277 +msgid "Keybinding to trigger 2nd dash app" +msgstr "Skrót klawiszowy do uruchomienia 2. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1068 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1278 +msgid "Keybinding to either show or launch the 2nd application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 2. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1074 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1284 +msgid "Keybinding to trigger 3rd dash app" +msgstr "Skrót klawiszowy do uruchomienia 3. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1075 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1285 +msgid "Keybinding to either show or launch the 3rd application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 3. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1081 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1291 +msgid "Keybinding to trigger 4th dash app" +msgstr "Skrót klawiszowy do uruchomienia 4. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1082 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1292 +msgid "Keybinding to either show or launch the 4th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 4. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1088 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1298 +msgid "Keybinding to trigger 5th dash app" +msgstr "Skrót klawiszowy do uruchomienia 5. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1089 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1299 +msgid "Keybinding to either show or launch the 5th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 5. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1095 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1305 +msgid "Keybinding to trigger 6th dash app" +msgstr "Skrót klawiszowy do uruchomienia 6. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1096 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1306 +msgid "Keybinding to either show or launch the 6th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 6. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1102 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1312 +msgid "Keybinding to trigger 7th dash app" +msgstr "Skrót klawiszowy do uruchomienia 7. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1103 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1313 +msgid "Keybinding to either show or launch the 7th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 7. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1109 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1319 +msgid "Keybinding to trigger 8th dash app" +msgstr "Skrót klawiszowy do uruchomienia 8. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1110 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1320 +msgid "Keybinding to either show or launch the 8th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 8. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1116 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1326 +msgid "Keybinding to trigger 9th dash app" +msgstr "Skrót klawiszowy do uruchomienia 9. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1117 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1327 +msgid "Keybinding to either show or launch the 9th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 9. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1123 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1333 +msgid "Keybinding to trigger 10th dash app" +msgstr "Skrót klawiszowy do uruchomienia 10. programu z panelu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1124 +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1334 +msgid "Keybinding to either show or launch the 10th application in the dash." +msgstr "" +"Skrót klawiszowy do wyświetlenia lub uruchomienia 10. programu w panelu." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1340 +msgid "Show progress bar on app icon" +msgstr "Wyświetlaj pasek postępu na ikonie programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1341 +msgid "" +"Whether to show progress bar overlay on app icon, for supported applications." +msgstr "" +"Wyświetlanie nakładki paska postępu na ikonie programu dla obsługiwanych " +"programów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1345 +msgid "Show badge count on app icon" +msgstr "Pokaż licznik na ikonie programu" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1346 +msgid "" +"Whether to show badge count overlay on app icon, for supported applications." +msgstr "" +"Wyświetlanie nakładki z liczbą plakietki na ikonie programu dla " +"obsługiwanych programów." + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1350 +msgid "The preferences page name to display" +msgstr "Nazwa strony preferencji do wyświetlenia" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1354 +msgid "Track if the preferences window is opened" +msgstr "Śledź, czy okno preferencji jest otwarte" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1358 +msgid "Unix time when the donate icon was hidden" +msgstr "Czas uniksowy, kiedy ikona darowizny została ukryta" + +#: schemas/org.gnome.shell.extensions.dash-to-panel.gschema.xml:1362 +msgid "Installed extension version" +msgstr "Zainstalowana wersja rozszerzenia" + +#: src/appIcons.js:1911 src/appIcons.js:1928 ui/BoxMiddleClickOptions.ui:33 +#: ui/BoxMiddleClickOptions.ui:62 ui/BoxMiddleClickOptions.ui:91 +msgid "Quit" +msgstr "Zakończ" + +#: src/appIcons.js:1931 +#, javascript-format +msgid "Quit %d Window" +msgid_plural "Quit %d Windows" +msgstr[0] "Zamknij %d okno" +msgstr[1] "Zamknij %d okna" +msgstr[2] "Zamknij %d okien" + +#: src/appIcons.js:2253 +msgid "Power options" +msgstr "Opcje zasilania" + +#: src/appIcons.js:2258 +msgid "Event logs" +msgstr "Dzinnik zdarzeń" + +#: src/appIcons.js:2263 +msgid "System" +msgstr "System" + +#: src/appIcons.js:2268 +msgid "Device Management" +msgstr "Zarządzanie urządzeniami" + +#: src/appIcons.js:2273 +msgid "Disk Management" +msgstr "Zarządzanie dyskami" + +msgid "System Monitor" +msgstr "Monitor procesów" + +msgid "Files" +msgstr "Pliki" + +msgid "Settings" +msgstr "Ustawienia" + +#: src/appIcons.js:2304 +msgid "Unlock taskbar" +msgstr "Odblokuj pasek zadań" + +#: src/appIcons.js:2305 +msgid "Lock taskbar" +msgstr "Zablokuj pasek zadań" + +#: src/appIcons.js:2315 +msgid "Gnome Settings" +msgstr "Ustawienia GNOME" + +#: src/appIcons.js:2319 +msgid "Taskbar Settings" +msgstr "Ustawienia pasek zadań" + +#: src/appIcons.js:2326 +msgid "Restore Windows" +msgstr "Przywróć okna" + +#: src/appIcons.js:2327 +msgid "Show Desktop" +msgstr "Pokaż pulpit" + +#: src/extension.js:96 +msgid "Dash to Panel has been updated!" +msgstr "Panel został zaktualizowany!" + +#: src/extension.js:97 +msgid "You are now running version" +msgstr "Aktualnie używasz wersji" + +#: src/extension.js:103 +msgid "See what's new" +msgstr "Zobacz, co nowego" + +#: src/panel.js:213 +msgid "Top Bar" +msgstr "Górny pasek" + +#: src/prefs.js:275 msgid "Show Desktop button height (px)" -msgstr "Wysokość przycisku , , , " -msgstr "Składnia: , , , " - -#: ui/BoxIntellihideOptions.ui.h:14 +#: ui/BoxIntellihideOptions.ui:143 msgid "e.g. i" msgstr "np. i" -#: ui/BoxIntellihideOptions.ui.h:15 +#: ui/BoxIntellihideOptions.ui:152 +msgid "Persist state across restarts" +msgstr "Zachowaj stan po ponownym uruchomieniu" + +#: ui/BoxIntellihideOptions.ui:162 +msgid "" +"(respects \"Do Not Disturb\" and requires show notification counter " +"badge option)" +msgstr "" +"(uwględnia opcję „Nie przeszkadzać” i wymaga włączenia opcji " +"wyświetlania licznika powiadomień)" + +#: ui/BoxIntellihideOptions.ui:163 +msgid "Reveal and hold the panel on notification" +msgstr "Odsłoń i przytrzymaj panel przy powiadomieniu" + +#: ui/BoxIntellihideOptions.ui:177 msgid "Hide and reveal animation duration (ms)" -msgstr "Czas trwania animacji ukrywania i odsłaniania (ms)" +msgstr "Czas trwania animacji ukrywania i odkrywania (ms)" -#: ui/BoxIntellihideOptions.ui.h:16 +#: ui/BoxIntellihideOptions.ui:191 msgid "Delay before hiding the panel (ms)" -msgstr "Opóźnienie ukrycia panelu (ms)" +msgstr "Opóźnienie ukrycia panela (ms)" -#: ui/BoxIntellihideOptions.ui.h:17 +#: ui/BoxIntellihideOptions.ui:206 msgid "Delay before enabling intellihide on start (ms)" msgstr "Opóźnienie przed aktywacją inteligentnego ukrywania (ms)" -#: ui/BoxMiddleClickOptions.ui.h:1 +#: ui/BoxMiddleClickOptions.ui:19 msgid "Shift+Click action" -msgstr "Kliknięcie lewym przyciskiem + Shift" +msgstr "Kliknięcie myszą + Shift" -#: ui/BoxMiddleClickOptions.ui.h:2 +#: ui/BoxMiddleClickOptions.ui:20 msgid "" "When set to minimize, double clicking minimizes all the windows of the " "application." @@ -589,359 +2434,388 @@ msgstr "" "Wybranie zminimalizowania okna umożliwia minimalizowanie wszystkich okien " "programu dwukrotnym kliknięciem." -#: ui/BoxMiddleClickOptions.ui.h:3 ui/SettingsAction.ui.h:8 +#: ui/BoxMiddleClickOptions.ui:25 ui/BoxMiddleClickOptions.ui:54 +#: ui/BoxMiddleClickOptions.ui:83 ui/SettingsAction.ui:40 msgid "Raise windows" -msgstr "Przywróć okna" +msgstr "Podnieś okna" -#: ui/BoxMiddleClickOptions.ui.h:4 +#: ui/BoxMiddleClickOptions.ui:26 ui/BoxMiddleClickOptions.ui:55 +#: ui/BoxMiddleClickOptions.ui:84 msgid "Minimize window" -msgstr "Zminimalizowanie okna" +msgstr "Minimalizacja okna" -#: ui/BoxMiddleClickOptions.ui.h:5 ui/SettingsAction.ui.h:9 +#: ui/BoxMiddleClickOptions.ui:27 ui/BoxMiddleClickOptions.ui:56 +#: ui/BoxMiddleClickOptions.ui:85 ui/SettingsAction.ui:41 msgid "Launch new instance" -msgstr "Uruchomienie nowego okna" +msgstr "Uruchomienie nowej instancji" -#: ui/BoxMiddleClickOptions.ui.h:6 ui/SettingsAction.ui.h:4 +#: ui/BoxMiddleClickOptions.ui:28 ui/BoxMiddleClickOptions.ui:57 +#: ui/BoxMiddleClickOptions.ui:86 ui/SettingsAction.ui:35 msgid "Cycle through windows" msgstr "Przełączanie między oknami" -#: ui/BoxMiddleClickOptions.ui.h:7 ui/SettingsAction.ui.h:3 +#: ui/BoxMiddleClickOptions.ui:29 ui/BoxMiddleClickOptions.ui:58 +#: ui/BoxMiddleClickOptions.ui:87 ui/SettingsAction.ui:34 msgid "Cycle windows + minimize" -msgstr "Przełączanie + zminimalizowanie okien" +msgstr "Przełączanie + minimalizowanie okien" -#: ui/BoxMiddleClickOptions.ui.h:8 ui/SettingsAction.ui.h:5 +#: ui/BoxMiddleClickOptions.ui:30 ui/BoxMiddleClickOptions.ui:59 +#: ui/BoxMiddleClickOptions.ui:88 ui/SettingsAction.ui:36 msgid "Toggle single / Preview multiple" msgstr "Przełącz pojedyncze / Podejrzyj wiele" -#: ui/BoxMiddleClickOptions.ui.h:9 ui/SettingsAction.ui.h:6 +#: ui/BoxMiddleClickOptions.ui:31 ui/BoxMiddleClickOptions.ui:60 +#: ui/BoxMiddleClickOptions.ui:89 ui/SettingsAction.ui:37 msgid "Toggle single / Cycle multiple" -msgstr "Przełącz pojedyncze / Podejrzyj wiele" +msgstr "Przełącz pojedyncze / Przełącz wiele" -#: ui/BoxMiddleClickOptions.ui.h:11 +#: ui/BoxMiddleClickOptions.ui:32 ui/BoxMiddleClickOptions.ui:61 +#: ui/BoxMiddleClickOptions.ui:90 ui/SettingsAction.ui:38 +msgid "Toggle single / Spread multiple" +msgstr "Przełącz pojedyncze / Rozłóż wiele" + +#: ui/BoxMiddleClickOptions.ui:48 msgid "Middle-Click action" msgstr "Akcja środkowego przycisku" -#: ui/BoxMiddleClickOptions.ui.h:12 +#: ui/BoxMiddleClickOptions.ui:49 msgid "Behavior for Middle-Click." -msgstr "Konfiguruje działanie kliknięcia środkowym przyciskiem." +msgstr "Zachowanie środkowego przycisku myszy." -#: ui/BoxMiddleClickOptions.ui.h:13 +#: ui/BoxMiddleClickOptions.ui:77 msgid "Shift+Middle-Click action" -msgstr "Kliknięcie środkowym przyciskiem + Shift" +msgstr "Kliknięcie środkowym przyciskiem myszy + Shift" -#: ui/BoxMiddleClickOptions.ui.h:14 +#: ui/BoxMiddleClickOptions.ui:78 msgid "Behavior for Shift+Middle-Click." -msgstr "Zachowanie dla Shift+środkowy przycisk" +msgstr "Zachowanie dla Shift+środkowy przycisk myszy." -#: ui/BoxOverlayShortcut.ui.h:1 -msgid "Hotkeys prefix" -msgstr "Prefiks dla skrótów klawiszowych" - -#: ui/BoxOverlayShortcut.ui.h:2 +#: ui/BoxOverlayShortcut.ui:23 msgid "Hotkeys will either be Super+Number or Super+Alt+Num" msgstr "Skrót może być aktywowany jako Super+Cyfra lub Super+Alt+Cyfra" -#: ui/BoxOverlayShortcut.ui.h:3 +#: ui/BoxOverlayShortcut.ui:24 +msgid "Hotkeys prefix" +msgstr "Prefiks dla skrótów klawiszowych" + +#: ui/BoxOverlayShortcut.ui:29 msgid "Super" msgstr "Super" -#: ui/BoxOverlayShortcut.ui.h:4 +#: ui/BoxOverlayShortcut.ui:30 msgid "Super + Alt" msgstr "Super + Alt" -#: ui/BoxOverlayShortcut.ui.h:5 -msgid "Number overlay" -msgstr "Pokazywanie cyfr" - -#: ui/BoxOverlayShortcut.ui.h:6 +#: ui/BoxOverlayShortcut.ui:38 msgid "" "Temporarily show the application numbers over the icons when using the " "hotkeys." -msgstr "Podczas używania skrótów klawiszowych wyświetlaj cyfry nad ikonami ." +msgstr "" +"Tymczasowo wyświetlaj numery aplikacji nad ikonami podczas korzystania ze " +"skrótów klawiszowych." -#: ui/BoxOverlayShortcut.ui.h:7 +#: ui/BoxOverlayShortcut.ui:39 +msgid "Number overlay" +msgstr "Nakładka liczowa" + +#: ui/BoxOverlayShortcut.ui:44 msgid "Never" msgstr "Nigdy" -#: ui/BoxOverlayShortcut.ui.h:8 +#: ui/BoxOverlayShortcut.ui:45 msgid "Show temporarily" -msgstr "Przez chwilę" +msgstr "Pokaż tymczasowo" -#: ui/BoxOverlayShortcut.ui.h:9 +#: ui/BoxOverlayShortcut.ui:46 msgid "Always visible" -msgstr "Zawsze" +msgstr "Zawsze pokazuj" -#: ui/BoxOverlayShortcut.ui.h:10 +#: ui/BoxOverlayShortcut.ui:54 msgid "Hide timeout (ms)" -msgstr "Opóźnienie ukrywania (ms)" +msgstr "Limit czasu ukrywania (ms)" -#: ui/BoxOverlayShortcut.ui.h:11 +#: ui/BoxOverlayShortcut.ui:66 msgid "Shortcut to show the overlay for 2 seconds" -msgstr "Skrót wyświetlający cyfry przez 2 sekundy" +msgstr "Skrót do wyświetlenia nakładki na 2 sekundy" -#: ui/BoxOverlayShortcut.ui.h:13 +#: ui/BoxOverlayShortcut.ui:69 msgid "e.g. q" msgstr "np. q" -#: ui/BoxOverlayShortcut.ui.h:14 +#: ui/BoxOverlayShortcut.ui:78 +msgid "" +"On secondary monitors, show the overlay on icons matching the primary monitor" +msgstr "" +"Na monitorach dodatkowych wyświetlaj nakładkę na ikonach pasujących do " +"monitora głównego" + +#: ui/BoxOverlayShortcut.ui:79 +msgid "Show the overlay on all monitors" +msgstr "Wyświetlaj nakładkę na wszystkich monitorach" + +#: ui/BoxOverlayShortcut.ui:89 +msgid "Show previews when the application have multiple instances" +msgstr "Pokaż podgląd, gdy program ma wiele instancji" + +#: ui/BoxOverlayShortcut.ui:90 msgid "Show window previews on hotkey" msgstr "Pokaż podgląd okna po wciśnięciu klawisza skrótu" -#: ui/BoxOverlayShortcut.ui.h:15 -msgid "Show previews when the application have multiple instances" -msgstr "Pokaż podgląd, gdy aplikacja ma wiele instancji" +#: ui/BoxOverlayShortcut.ui:100 +msgid "Select which keyboard number keys are used to activate the hotkeys" +msgstr "" +"Wybierz, które klawisze numeryczne klawiatury są używane do aktywacji " +"skrótów klawiszowych" -#: ui/BoxOverlayShortcut.ui.h:16 +#: ui/BoxOverlayShortcut.ui:101 msgid "Hotkeys are activated with" msgstr "Aktywacja skrótów klawiszowych wymaga" -#: ui/BoxOverlayShortcut.ui.h:17 -msgid "Select which keyboard number keys are used to activate the hotkeys" -msgstr "" -"Zaznacz które klawisze cyfr mają być używane do aktywacji skrótów " -"klawiszowych" - -#: ui/BoxOverlayShortcut.ui.h:18 +#: ui/BoxOverlayShortcut.ui:106 msgid "Number row" msgstr "Rząd cyfr" -#: ui/BoxOverlayShortcut.ui.h:19 +#: ui/BoxOverlayShortcut.ui:107 msgid "Numeric keypad" msgstr "Klawiatura numeryczna" -#: ui/BoxOverlayShortcut.ui.h:20 +#: ui/BoxOverlayShortcut.ui:108 msgid "Both" -msgstr "Obie" +msgstr "Oba" -#: ui/BoxScrollIconOptions.ui.h:1 ui/BoxScrollPanelOptions.ui.h:1 +#: ui/BoxScrollIconOptions.ui:25 ui/BoxScrollPanelOptions.ui:25 msgid "Delay between mouse scroll events (ms)" -msgstr "Opóźnienie między przewijaniem myszą (ms)" +msgstr "Opóźnienie między zdarzeniami przewijaniem myszą (ms)" -#: ui/BoxScrollIconOptions.ui.h:2 ui/BoxScrollPanelOptions.ui.h:2 +#: ui/BoxScrollIconOptions.ui:26 ui/BoxScrollPanelOptions.ui:26 msgid "Use this value to limit the number of captured mouse scroll events." msgstr "Użyj tą wartość by ograniczyć liczbę przechwyceń przewijania myszą." -#: ui/BoxScrollPanelOptions.ui.h:3 +#: ui/BoxScrollPanelOptions.ui:42 msgid "Show popup when changing workspace" -msgstr "Pokaż wyskakujące okno podczas zmiany przestrzeni roboczej" +msgstr "Pokaż wyskakujące okno podczas zmiany obszaru roboczego" -#: ui/BoxScrollPanelOptions.ui.h:4 +#: ui/BoxScrollPanelOptions.ui:43 msgid "This affects workspace popup when scrolling on the panel only." -msgstr "Ma wpływ na wyskakujące okno w przestrzeni roboczej tylko na panelu" +msgstr "Wpływa na wyskakujące okno w przestrzeni roboczej tylko na panelu." -#: ui/BoxSecondaryMenuOptions.ui.h:1 +#: ui/BoxSecondaryMenuOptions.ui:19 msgid "Integrate AppMenu items" msgstr "Zintegruj elementy Menu programów" -#: ui/BoxSecondaryMenuOptions.ui.h:2 -msgid "Show Details menu item" -msgstr "Wyświetl szczegóły" +#: ui/BoxSecondaryMenuOptions.ui:30 +msgid "App Details menu item" +msgstr "Element menu Wyświetl szczegóły" -#: ui/BoxShowApplicationsOptions.ui.h:1 +#: ui/BoxSecondaryMenuOptions.ui:31 +msgid "App Details is only available when Gnome Software is installed" +msgstr "" +"Opcja Szczegóły aplikacji jest dostępna tylko wtedy, gdy " +"zainstalowane jest Oprogramowanie GNOME" + +#: ui/BoxShowApplicationsOptions.ui:25 msgid "Show Applications icon" -msgstr "Opcje wyświetlania programów" +msgstr "Ikona Pokaż programy" -#: ui/BoxShowApplicationsOptions.ui.h:2 +#: ui/BoxShowApplicationsOptions.ui:60 msgid "Show Applications icon side padding (px)" msgstr "Wewnętrzny margines przycisku Pokaż programy (px)" -#: ui/BoxShowApplicationsOptions.ui.h:4 +#: ui/BoxShowApplicationsOptions.ui:73 msgid "Override escape key and return to desktop" -msgstr "Nadpisz przycisk wyjścia i wróć do pulpitu" +msgstr "Zastąp przycisk ESC i wróć do pulpitu" -#: ui/BoxShowDesktopOptions.ui.h:1 -msgid "Override Show Desktop line color" -msgstr "Nadpisz kolor przycisku Pokaż pulpit" - -#: ui/BoxShowDesktopOptions.ui.h:2 +#: ui/BoxShowDesktopOptions.ui:77 msgid "Reveal the desktop when hovering the Show Desktop button" -msgstr "Pokaż pulpit po najechaniu na przycisk Pokaż pulpit" +msgstr "Odsłoń pulpit po najechaniu kursorem na przycisk Pokaż pulpit" -#: ui/BoxShowDesktopOptions.ui.h:3 +#: ui/BoxShowDesktopOptions.ui:90 msgid "Delay before revealing the desktop (ms)" msgstr "Opóźnienie przed ujawnieniem pulpitu (ms)" -#: ui/BoxShowDesktopOptions.ui.h:4 +#: ui/BoxShowDesktopOptions.ui:106 msgid "Fade duration (ms)" -msgstr "Opóźnienie ukrywania (ms)" +msgstr "Czas trwania zanikania (ms)" -#: ui/BoxWindowPreviewOptions.ui.h:1 +#: ui/BoxWindowPreviewOptions.ui:89 msgid "Time (ms) before showing" -msgstr "Opóźnienie (ms) przed pokazaniem (domyślnie 100)" +msgstr "Opóźnienie (ms) przed pokazaniem" -#: ui/BoxWindowPreviewOptions.ui.h:2 +#: ui/BoxWindowPreviewOptions.ui:90 msgid "(400 is default)" msgstr "(Domyślnie 400)" -#: ui/BoxWindowPreviewOptions.ui.h:3 +#: ui/BoxWindowPreviewOptions.ui:105 msgid "Time (ms) before hiding" -msgstr "Opóźnienie ukrywania miniatur (domyślnie 100 ms)" +msgstr "Czas (ms) przed ukryciem" -#: ui/BoxWindowPreviewOptions.ui.h:4 +#: ui/BoxWindowPreviewOptions.ui:106 msgid "(100 is default)" msgstr "(Domyślnie 100)" -#: ui/BoxWindowPreviewOptions.ui.h:5 +#: ui/BoxWindowPreviewOptions.ui:117 msgid "Immediate on application icon click" -msgstr "Natychmiastowo po kliknięciu na ikonę aplikacji" +msgstr "Natychmiast po kliknięciu na ikonę programu" -#: ui/BoxWindowPreviewOptions.ui.h:6 +#: ui/BoxWindowPreviewOptions.ui:138 msgid "Animation time (ms)" -msgstr "Długość animacji (ms)" +msgstr "Czas animacji (ms)" -#: ui/BoxWindowPreviewOptions.ui.h:7 +#: ui/BoxWindowPreviewOptions.ui:159 msgid "Middle click on the preview to close the window" -msgstr "" -"Kliknięcie środkowego przycisku myszy na podglądzie zamyka okno programu" +msgstr "Zamknij okno poprzez kliknięcie przyciskiem myszy na podglądzie" -#: ui/BoxWindowPreviewOptions.ui.h:8 +#: ui/BoxWindowPreviewOptions.ui:176 msgid "Window previews preferred size (px)" msgstr "Preferowany rozmiar podglądu okna (px)" -#: ui/BoxWindowPreviewOptions.ui.h:9 +#: ui/BoxWindowPreviewOptions.ui:192 msgid "Window previews aspect ratio X (width)" -msgstr "Proporcja szerokości podglądu okna" +msgstr "Proporcje podglądu okna X (szerokość)" -#: ui/BoxWindowPreviewOptions.ui.h:10 +#: ui/BoxWindowPreviewOptions.ui:197 ui/BoxWindowPreviewOptions.ui:238 msgid "1" msgstr "1" -#: ui/BoxWindowPreviewOptions.ui.h:11 +#: ui/BoxWindowPreviewOptions.ui:198 ui/BoxWindowPreviewOptions.ui:239 msgid "2" msgstr "2" -#: ui/BoxWindowPreviewOptions.ui.h:12 +#: ui/BoxWindowPreviewOptions.ui:199 ui/BoxWindowPreviewOptions.ui:240 msgid "3" msgstr "3" -#: ui/BoxWindowPreviewOptions.ui.h:13 +#: ui/BoxWindowPreviewOptions.ui:200 ui/BoxWindowPreviewOptions.ui:241 msgid "4" msgstr "4" -#: ui/BoxWindowPreviewOptions.ui.h:14 +#: ui/BoxWindowPreviewOptions.ui:201 ui/BoxWindowPreviewOptions.ui:242 +#: ui/BoxWindowPreviewOptions.ui:306 msgid "5" msgstr "5" -#: ui/BoxWindowPreviewOptions.ui.h:15 +#: ui/BoxWindowPreviewOptions.ui:202 ui/BoxWindowPreviewOptions.ui:243 msgid "6" msgstr "6" -#: ui/BoxWindowPreviewOptions.ui.h:16 +#: ui/BoxWindowPreviewOptions.ui:203 ui/BoxWindowPreviewOptions.ui:244 msgid "7" msgstr "7" -#: ui/BoxWindowPreviewOptions.ui.h:17 +#: ui/BoxWindowPreviewOptions.ui:204 ui/BoxWindowPreviewOptions.ui:245 msgid "8" msgstr "8" -#: ui/BoxWindowPreviewOptions.ui.h:18 +#: ui/BoxWindowPreviewOptions.ui:205 ui/BoxWindowPreviewOptions.ui:246 msgid "9" msgstr "9" -#: ui/BoxWindowPreviewOptions.ui.h:19 +#: ui/BoxWindowPreviewOptions.ui:206 ui/BoxWindowPreviewOptions.ui:247 msgid "10" msgstr "10" -#: ui/BoxWindowPreviewOptions.ui.h:20 +#: ui/BoxWindowPreviewOptions.ui:207 ui/BoxWindowPreviewOptions.ui:248 msgid "11" msgstr "11" -#: ui/BoxWindowPreviewOptions.ui.h:21 +#: ui/BoxWindowPreviewOptions.ui:208 ui/BoxWindowPreviewOptions.ui:249 msgid "12" msgstr "12" -#: ui/BoxWindowPreviewOptions.ui.h:22 +#: ui/BoxWindowPreviewOptions.ui:209 ui/BoxWindowPreviewOptions.ui:250 msgid "13" msgstr "13" -#: ui/BoxWindowPreviewOptions.ui.h:23 +#: ui/BoxWindowPreviewOptions.ui:210 ui/BoxWindowPreviewOptions.ui:251 msgid "14" msgstr "14" -#: ui/BoxWindowPreviewOptions.ui.h:24 +#: ui/BoxWindowPreviewOptions.ui:211 ui/BoxWindowPreviewOptions.ui:252 msgid "15" msgstr "15" -#: ui/BoxWindowPreviewOptions.ui.h:25 +#: ui/BoxWindowPreviewOptions.ui:212 ui/BoxWindowPreviewOptions.ui:253 msgid "16" msgstr "16" -#: ui/BoxWindowPreviewOptions.ui.h:26 +#: ui/BoxWindowPreviewOptions.ui:213 ui/BoxWindowPreviewOptions.ui:254 msgid "17" msgstr "17" -#: ui/BoxWindowPreviewOptions.ui.h:27 +#: ui/BoxWindowPreviewOptions.ui:214 ui/BoxWindowPreviewOptions.ui:255 msgid "18" msgstr "18" -#: ui/BoxWindowPreviewOptions.ui.h:28 +#: ui/BoxWindowPreviewOptions.ui:215 ui/BoxWindowPreviewOptions.ui:256 msgid "19" msgstr "19" -#: ui/BoxWindowPreviewOptions.ui.h:29 +#: ui/BoxWindowPreviewOptions.ui:216 ui/BoxWindowPreviewOptions.ui:257 msgid "20" msgstr "20" -#: ui/BoxWindowPreviewOptions.ui.h:30 +#: ui/BoxWindowPreviewOptions.ui:217 ui/BoxWindowPreviewOptions.ui:258 msgid "21" msgstr "21" -#: ui/BoxWindowPreviewOptions.ui.h:31 +#: ui/BoxWindowPreviewOptions.ui:223 ui/BoxWindowPreviewOptions.ui:264 msgid "Fixed" msgstr "Stałe" -#: ui/BoxWindowPreviewOptions.ui.h:32 +#: ui/BoxWindowPreviewOptions.ui:233 msgid "Window previews aspect ratio Y (height)" -msgstr "Proporcja wysokości podglądu okna" +msgstr "Proporcje podglądu okna Y (wysokość)" -#: ui/BoxWindowPreviewOptions.ui.h:33 +#: ui/BoxWindowPreviewOptions.ui:274 msgid "Window previews padding (px)" -msgstr "Wewnętrzny odstęp w podglądzie okien (px)" +msgstr "Wypełnienie podglądów okien (px)" -#: ui/BoxWindowPreviewOptions.ui.h:34 +#: ui/BoxWindowPreviewOptions.ui:296 msgid "Use custom opacity for the previews background" -msgstr "Własna przeźroczystość dla tła podglądu okna" +msgstr "Własna przezroczystość dla tła podglądu" -#: ui/BoxWindowPreviewOptions.ui.h:35 +#: ui/BoxWindowPreviewOptions.ui:297 msgid "" "If disabled, the previews background have the same opacity as the panel." msgstr "" -"Jeśli wyłączone, podgląd okna będzie mieć taką samą przeźroczystość jak panel" +"Jeśli wyłączone, tło podglądów będzie miało taką samą przezroczystość jak " +"panel." -#: ui/BoxWindowPreviewOptions.ui.h:36 +#: ui/BoxWindowPreviewOptions.ui:322 msgid "Close button and header position" msgstr "Położenie przycisku zamykania i nagłówka" -#: ui/BoxWindowPreviewOptions.ui.h:39 +#: ui/BoxWindowPreviewOptions.ui:352 msgid "Display window preview headers" msgstr "Wyświetlaj nagłówek w podglądzie okna" -#: ui/BoxWindowPreviewOptions.ui.h:40 +#: ui/BoxWindowPreviewOptions.ui:363 msgid "Icon size (px) of the window preview" -msgstr "Wielkość czcionki etykiet (px) etykiet podglądu" +msgstr "Rozmiar ikony (px) podglądu okna" -#: ui/BoxWindowPreviewOptions.ui.h:41 +#: ui/BoxWindowPreviewOptions.ui:364 msgid "If disabled, the previews icon size will be based on headerbar size" msgstr "" -"Jeśli wyłączone, podgląd okna będzie mieć taką samą przeźroczystość jak panel" +"Jeśli wyłączone, rozmiar ikony podglądu będzie oparty na rozmiarze paska " +"nagłówka" -#: ui/BoxWindowPreviewOptions.ui.h:42 +#: ui/BoxWindowPreviewOptions.ui:385 msgid "Font size (px) of the preview titles" -msgstr "Wielkość czcionki etykiet (px) etykiet podglądu" +msgstr "Wielkość czcionki (px) tytułów podglądu" -#: ui/BoxWindowPreviewOptions.ui.h:43 +#: ui/BoxWindowPreviewOptions.ui:401 msgid "Font weight of the preview titles" -msgstr "Grubość czcionki etykiet podglądu" +msgstr "Grubość czcionki tytułów podglądu" -#: ui/BoxWindowPreviewOptions.ui.h:49 +#: ui/BoxWindowPreviewOptions.ui:419 msgid "Font color of the preview titles" -msgstr "Kolor czcionki etykiet podglądu" +msgstr "Kolor czcionki tytułów podglądu" -#: ui/BoxWindowPreviewOptions.ui.h:50 +#: ui/BoxWindowPreviewOptions.ui:437 msgid "Enable window peeking" -msgstr "Podgląd okien otwartych programów" +msgstr "Włącz podgląd okien" -#: ui/BoxWindowPreviewOptions.ui.h:51 +#: ui/BoxWindowPreviewOptions.ui:438 msgid "" "When hovering over a window preview for some time, the window gets " "distinguished." @@ -949,71 +2823,67 @@ msgstr "" "Po najechaniu kursorem na podgląd okna zostanie ono pokazane na tle " "wszystkich otwartych okien programów." -#: ui/BoxWindowPreviewOptions.ui.h:52 +#: ui/BoxWindowPreviewOptions.ui:449 msgid "Enter window peeking mode timeout (ms)" -msgstr "Opóźnienie wyświetlania (ms)" +msgstr "Limit czasu wejścia w tryb podglądu okna (ms)" -#: ui/BoxWindowPreviewOptions.ui.h:53 +#: ui/BoxWindowPreviewOptions.ui:450 msgid "" "Time of inactivity while hovering over a window preview needed to enter the " "window peeking mode." msgstr "" -"Czas opóźnienia, po którym wyświetlony zostanie podgląd okien otwartych " -"programów." +"Czas bezczynności podczas najeżdżania kursorem na podgląd okna, potrzebny do " +"wejścia w tryb podglądu okna." -#: ui/BoxWindowPreviewOptions.ui.h:54 +#: ui/BoxWindowPreviewOptions.ui:455 msgid "50" msgstr "50" -#: ui/BoxWindowPreviewOptions.ui.h:55 -msgid "Window peeking mode opacity" -msgstr "Przeźroczystość podglądanych okien" +#: ui/SettingsAbout.ui:8 +msgid "About" +msgstr "Informacje" -#: ui/BoxWindowPreviewOptions.ui.h:56 -msgid "All windows except for the peeked one have their opacity set to the same value." -msgstr "Wszystkie okna, za wyjątkiem okna głównego, mają tą samą przeźroczystość." - -#: ui/SettingsAbout.ui.h:1 +#: ui/SettingsAbout.ui:11 msgid "Info" -msgstr "Info" +msgstr "O programie" -#: ui/SettingsAbout.ui.h:2 +#: ui/SettingsAbout.ui:14 msgid "Version" msgstr "Wersja" -#: ui/SettingsAbout.ui.h:3 +#: ui/SettingsAbout.ui:22 msgid "Source" msgstr "Źródło" -#: ui/SettingsAbout.ui.h:4 +#: ui/SettingsAbout.ui:26 msgid "GitHub" msgstr "GitHub" -#: ui/SettingsAbout.ui.h:5 +#: ui/SettingsAbout.ui:37 msgid "Export and Import" -msgstr "Ustawienia eksportu i importu" +msgstr "Eksport i import" -#: ui/SettingsAbout.ui.h:6 -msgid "Export and import settings" -msgstr "Ustawienia eksportu i importu" - -#: ui/SettingsAbout.ui.h:7 +#: ui/SettingsAbout.ui:40 msgid "" "Use the buttons below to create a settings file from your current " "preferences that can be imported on a different machine." msgstr "" -"Użyj poniższych przycisków, aby utworzyć plik ustawień bieżącychpreferencji, " -"które można zaimportować na innym komputerze." +"Użyj poniższych przycisków, aby utworzyć plik ustawień bieżących " +"preferencji, które można zaimportować na innym komputerze." -#: ui/SettingsAbout.ui.h:8 +#: ui/SettingsAbout.ui:41 +msgid "Export and import settings" +msgstr "Ustawienia eksportu i importu" + +#: ui/SettingsAbout.ui:56 msgid "Export to file" msgstr "Eksportuj do pliku" -#: ui/SettingsAbout.ui.h:9 +#: ui/SettingsAbout.ui:62 msgid "Import from file" msgstr "Importuj z pliku" -#: ui/SettingsAbout.ui.h:10 +#: ui/SettingsAbout.ui:77 msgid "" "This program comes with ABSOLUTELY NO WARRANTY.\n" "See the GNU General Public License, wersja 2 " "lub nowsza." -#: ui/SettingsAction.ui.h:1 +#: ui/SettingsAction.ui:8 +msgid "Action" +msgstr "Działanie" + +#: ui/SettingsAction.ui:11 ui/SettingsAction.ui:15 msgid "Click action" msgstr "Działanie kliknięcia" -#: ui/SettingsAction.ui.h:2 +#: ui/SettingsAction.ui:14 msgid "Behaviour when clicking on the icon of a running application." -msgstr "Reakcja na kliknięcie ikony aktywnego programu." +msgstr "Zachowanie po kliknięciu ikony uruchomionego programu." -#: ui/SettingsAction.ui.h:7 +#: ui/SettingsAction.ui:39 msgid "Toggle windows" msgstr "Przełącz okna" -#: ui/SettingsAction.ui.h:10 +#: ui/SettingsAction.ui:51 msgid "Scroll action" -msgstr "Działanie przewijania na ikonie" +msgstr "Akcja przewijania" -#: ui/SettingsAction.ui.h:11 -msgid "Scroll panel action" -msgstr "Działanie przewijania na panelu" - -#: ui/SettingsAction.ui.h:12 +#: ui/SettingsAction.ui:54 msgid "Behavior when mouse scrolling over the panel." -msgstr "Reakcja na przewijanie myszą nad panelem." +msgstr "Zachowanie przewijania myszą nad panelem." -#: ui/SettingsAction.ui.h:13 +#: ui/SettingsAction.ui:55 +msgid "Scroll panel action" +msgstr "Akcja przewijania na panelu" + +#: ui/SettingsAction.ui:79 ui/SettingsAction.ui:112 msgid "Do nothing" msgstr "Nic nie rób" -#: ui/SettingsAction.ui.h:14 +#: ui/SettingsAction.ui:80 msgid "Switch workspace" -msgstr "Przełącz ekran roboczy" +msgstr "Przełącz obszar roboczy" -#: ui/SettingsAction.ui.h:15 +#: ui/SettingsAction.ui:81 ui/SettingsAction.ui:113 msgid "Cycle windows" msgstr "Przełącz między oknami" -#: ui/SettingsAction.ui.h:16 +#: ui/SettingsAction.ui:82 msgid "Change volume" msgstr "Zmień głośność" -#: ui/SettingsAction.ui.h:17 -msgid "Scroll icon action" -msgstr "Działanie przewijania na ikonie" - -#: ui/SettingsAction.ui.h:18 +#: ui/SettingsAction.ui:90 msgid "Behavior when mouse scrolling over an application icon." -msgstr "Reakcja na przewijanie myszą nad ikoną programu." +msgstr "Zachowanie przewijania myszą nad ikoną programu." -#: ui/SettingsAction.ui.h:19 +#: ui/SettingsAction.ui:91 +msgid "Scroll icon action" +msgstr "Akcja przewijania na ikonie" + +#: ui/SettingsAction.ui:114 msgid "Same as panel" -msgstr "Tak jak panel" +msgstr "Tak jak dla panela" -#: ui/SettingsAction.ui.h:20 +#: ui/SettingsAction.ui:124 msgid "Hotkey overlay" -msgstr "Pokazywanie cyfr" +msgstr "Nakładka skrótów klawiszowych" -#: ui/SettingsAction.ui.h:21 -msgid "Use hotkeys to activate apps" -msgstr "Skróty klawiszowe uruchamiania programów" - -#: ui/SettingsAction.ui.h:22 +#: ui/SettingsAction.ui:127 msgid "" "Enable Super+(0-9) as shortcuts to activate apps. It can also be used " "together with Shift and Ctrl." msgstr "" -"Konfigurowanie skrótów uruchamiania programów\n" -"Super+(0-9) - możliwe użycie razem z Shift i Ctrl." +"Włącz Super+(0-9) jako skróty do aktywacji programu. Można go również używać " +"w połączeniu z Shift i Ctrl." -#: ui/SettingsBehavior.ui.h:1 +#: ui/SettingsAction.ui:128 +msgid "Use hotkeys to activate apps" +msgstr "Skróty klawiszowe uruchamiania programów" + +#: ui/SettingsAction.ui:154 +msgid "Application icons context menu" +msgstr "Menu kontekstowe ikon programów" + +#: ui/SettingsAction.ui:157 +msgid "(right-click menu)" +msgstr "(menu prawego przycisku myszy)" + +#: ui/SettingsAction.ui:158 +msgid "Secondary menu" +msgstr "Menu kontekstowe" + +#: ui/SettingsAction.ui:179 +msgid "Panel context menu entries" +msgstr "Wpisy menu kontekstowego panelu" + +#: ui/SettingsAction.ui:190 +msgid "Add entry" +msgstr "Dodaj wpis" + +#: ui/SettingsBehavior.ui:5 +msgid "Behavior" +msgstr "Zachowanie" + +#: ui/SettingsBehavior.ui:11 msgid "Applications" -msgstr "Tryb listy (nie scalaj ikon)" +msgstr "Aplikacje" -#: ui/SettingsBehavior.ui.h:2 +#: ui/SettingsBehavior.ui:15 msgid "Show favorite applications" msgstr "Pokaż ulubione programy" -#: ui/SettingsBehavior.ui.h:3 +#: ui/SettingsBehavior.ui:26 msgid "Show favorite applications on secondary panels" -msgstr "Pokaż ulubione programy" +msgstr "Pokaż ulubione programy na dodatkowym panelu" -#: ui/SettingsBehavior.ui.h:4 +#: ui/SettingsBehavior.ui:37 msgid "Show running applications" msgstr "Pokaż uruchomione programy" -#: ui/SettingsBehavior.ui.h:5 -msgid "Show AppMenu button" -msgstr "Pokaż przycisk Menu programu" - -#: ui/SettingsBehavior.ui.h:6 +#: ui/SettingsBehavior.ui:48 msgid "Ungroup applications" -msgstr "Tryb listy (nie scalaj ikon)" +msgstr "Rozgrupuj programy" -#: ui/SettingsBehavior.ui.h:7 +#: ui/SettingsBehavior.ui:73 msgid "Show notification counter badge" msgstr "Pokaż licznik powiadomień" -#: ui/SettingsBehavior.ui.h:8 +#: ui/SettingsBehavior.ui:90 +msgid "Hover" +msgstr "Najechanie kursorem" + +#: ui/SettingsBehavior.ui:94 msgid "Show window previews on hover" msgstr "Pokaż podgląd okna po najechaniu myszą" -#: ui/SettingsBehavior.ui.h:9 +#: ui/SettingsBehavior.ui:119 msgid "Show tooltip on hover" -msgstr "Pokaż szczegóły po najechaniu myszą" +msgstr "Pokaż etykietkę po najechaniu kursorem" -#: ui/SettingsBehavior.ui.h:10 +#: ui/SettingsBehavior.ui:134 msgid "Isolate" -msgstr "Niezależne obszary robocze" +msgstr "Niezależne" -#: ui/SettingsBehavior.ui.h:11 +#: ui/SettingsBehavior.ui:138 msgid "Isolate Workspaces" msgstr "Niezależne obszary robocze" -#: ui/SettingsBehavior.ui.h:12 +#: ui/SettingsBehavior.ui:149 msgid "Isolate monitors" -msgstr "Niezależne obszary robocze" +msgstr "Niezależne ekrany" -#: ui/SettingsBehavior.ui.h:13 +#: ui/SettingsBehavior.ui:164 msgid "Overview" msgstr "Przegląd" -#: ui/SettingsBehavior.ui.h:14 +#: ui/SettingsBehavior.ui:168 msgid "Click empty space to close overview" -msgstr "Kliknij na pustą przestrzeń, aby zamknąć podgląd" +msgstr "Kliknij na pustą przestrzeń, aby zamknąć przegląd" -#: ui/SettingsBehavior.ui.h:15 +#: ui/SettingsBehavior.ui:179 msgid "Disable show overview on startup" -msgstr "Wyłącz pokazywanie podglądu przy uruchomieniu" +msgstr "Wyłącz pokazywanie przeglądu przy uruchomieniu" -#: ui/SettingsFineTune.ui.h:1 +#: ui/SettingsFineTune.ui:38 +msgid "Fine-Tune" +msgstr "Dostrajanie" + +#: ui/SettingsFineTune.ui:41 msgid "Font size" msgstr "Rozmiar czcionki" -#: ui/SettingsFineTune.ui.h:2 -msgid "Tray Font Size" -msgstr "Rozmiar czcionki zasobnika" - -#: ui/SettingsFineTune.ui.h:3 +#: ui/SettingsFineTune.ui:44 ui/SettingsFineTune.ui:60 msgid "(0 = theme default)" msgstr "(0 - domyślne motywu)" -#: ui/SettingsFineTune.ui.h:4 +#: ui/SettingsFineTune.ui:45 +msgid "Tray Font Size" +msgstr "Rozmiar czcionki zasobnika" + +#: ui/SettingsFineTune.ui:61 msgid "LeftBox Font Size" -msgstr "" -"Rozmiar czcionki lewej strony panelu\n" -"(0 - domyślne motywu)" +msgstr "Rozmiar czcionki lewego pola" -#: ui/SettingsFineTune.ui.h:5 +#: ui/SettingsFineTune.ui:78 msgid "Padding" -msgstr "Odstęp" +msgstr "Wypełnienie" -#: ui/SettingsFineTune.ui.h:6 -msgid "Tray Item Padding" -msgstr "Odstęp elementów zasobnika" - -#: ui/SettingsFineTune.ui.h:7 +#: ui/SettingsFineTune.ui:81 ui/SettingsFineTune.ui:97 +#: ui/SettingsFineTune.ui:113 msgid "(-1 = theme default)" msgstr "(-1 - domyślne motywu)" -#: ui/SettingsFineTune.ui.h:8 +#: ui/SettingsFineTune.ui:82 +msgid "Tray Item Padding" +msgstr "Wypełnienie elementów zasobnika" + +#: ui/SettingsFineTune.ui:98 msgid "Status Icon Padding" -msgstr "Odstęp ikon stanu" +msgstr "Wypełnienie ikony stanu" -#: ui/SettingsFineTune.ui.h:9 +#: ui/SettingsFineTune.ui:114 msgid "LeftBox Padding" -msgstr "Do lewej" +msgstr "Wypełnienie lewego pola" -#: ui/SettingsFineTune.ui.h:10 +#: ui/SettingsFineTune.ui:131 msgid "Animate" -msgstr "Animacja" +msgstr "Animuj" -#: ui/SettingsFineTune.ui.h:11 +#: ui/SettingsFineTune.ui:134 msgid "Animate switching applications" msgstr "Animuj przełączenie programów" -#: ui/SettingsFineTune.ui.h:12 +#: ui/SettingsFineTune.ui:144 msgid "Animate launching new windows" msgstr "Animuj uruchamianie nowych programów" -#: ui/SettingsFineTune.ui.h:13 +#: ui/SettingsFineTune.ui:156 msgid "Gnome functionality" -msgstr "Funkcja Gnome" +msgstr "Funkcjonalność GNOME" -#: ui/SettingsFineTune.ui.h:14 -msgid "Keep original gnome-shell dash" -msgstr "Zachowaj oryginalny panel (podgląd)" - -#: ui/SettingsFineTune.ui.h:15 +#: ui/SettingsFineTune.ui:159 msgid "(overview)" msgstr "(przegląd)" -#: ui/SettingsFineTune.ui.h:16 +#: ui/SettingsFineTune.ui:160 +msgid "Keep original gnome-shell dash" +msgstr "Zachowaj oryginalny panel powłoki GNOME" + +#: ui/SettingsFineTune.ui:170 msgid "Keep original gnome-shell top panel" -msgstr "Zachowaj oryginalny panel (podgląd)" +msgstr "Zachowaj oryginalny górny panel powłoki GNOME" -#: ui/SettingsFineTune.ui.h:17 -msgid "Activate panel menu buttons on click only" -msgstr "" -"Aktywuj przyciski menu panelu (np. menu kalendarza) tylko po kliknięciu" - -#: ui/SettingsFineTune.ui.h:18 +#: ui/SettingsFineTune.ui:180 msgid "(e.g. date menu)" msgstr "(np. menu daty)" -#: ui/SettingsFineTune.ui.h:19 +#: ui/SettingsFineTune.ui:181 +msgid "Activate panel menu buttons on click only" +msgstr "Aktywacja przycisków menu panela tylko po kliknięciu" + +#: ui/SettingsFineTune.ui:191 msgid "Force Activities hot corner on primary monitor" -msgstr "Wymuś Aktywności gorącego rogu na głównym monitorze" +msgstr "Wymuszaj aktywność gorącego rogu na głównym monitorze" -#: ui/SettingsFineTune.ui.h:20 -msgid "App icon secondary menu" -msgstr "Menu kontekstowe programu (prawy przycisk myszy)" +#: ui/SettingsPosition.ui:19 +msgid "Position" +msgstr "Pozycja" -#: ui/SettingsFineTune.ui.h:21 -msgid "(right-click menu)" -msgstr "(prawy przycisk myszy)" - -#: ui/SettingsPosition.ui.h:1 +#: ui/SettingsPosition.ui:22 msgid "Panel" msgstr "Panel" -#: ui/SettingsPosition.ui.h:2 +#: ui/SettingsPosition.ui:25 msgid "Display the main panel on" -msgstr "Wyświetl główny panel na" +msgstr "Wyświetlaj główny panel na" -#: ui/SettingsPosition.ui.h:3 -msgid "Display panels on all monitors" -msgstr "Wyświetl panel na wszystkich monitorach" - -#: ui/SettingsPosition.ui.h:4 -msgid "Panel Intellihide" -msgstr "Inteligentne ukrywanie panelu" - -#: ui/SettingsPosition.ui.h:5 +#: ui/SettingsPosition.ui:49 msgid "Hide and reveal the panel according to preferences" msgstr "Ukryj i odsłoń panel według preferencji" -#: ui/SettingsPosition.ui.h:6 +#: ui/SettingsPosition.ui:50 +msgid "Panel Intellihide" +msgstr "Inteligentne ukrywanie panelu" + +#: ui/SettingsPosition.ui:76 msgid "Order and Position on monitors" msgstr "Kolejność i pozycja na monitorach" -#: ui/SettingsPosition.ui.h:7 +#: ui/SettingsPosition.ui:79 msgid "Monitor" msgstr "Monitor" -#: ui/SettingsPosition.ui.h:8 +#: ui/SettingsPosition.ui:90 msgid "Apply changes to all monitors" msgstr "Zastosuj zmiany na wszyskich monitorach" -#: ui/SettingsPosition.ui.h:9 +#: ui/SettingsPosition.ui:111 msgid "Panel screen position" msgstr "Położenie panelu na ekranie" -#: ui/SettingsPosition.ui.h:14 -msgid "Panel thickness" -msgstr "Inteligentne ukrywanie panelu" - -#: ui/SettingsPosition.ui.h:15 +#: ui/SettingsPosition.ui:152 msgid "(default is 48)" -msgstr "(domyślnie 48)" +msgstr "(Domyślnie 48)" -#: ui/SettingsPosition.ui.h:17 -#, no-c-format -msgid "Panel length (%)" -msgstr "Długość panela (%)" +#: ui/SettingsPosition.ui:153 +msgid "Panel thickness" +msgstr "Grubość panela" -#: ui/SettingsPosition.ui.h:18 +#: ui/SettingsPosition.ui:168 msgid "(default is 100)" -msgstr "(domyślnie 100)" +msgstr "(Domyślnie 100)" -#: ui/SettingsPosition.ui.h:19 +#: ui/SettingsPosition.ui:169 +msgid "Panel length" +msgstr "Długość panelu" + +#: ui/SettingsPosition.ui:174 +msgid "Dynamic" +msgstr "Dynamiczna" + +#: ui/SettingsPosition.ui:192 msgid "Anchor" msgstr "Zakotwiczenie" -#: ui/SettingsPosition.ui.h:23 +#: ui/SettingsPosition.ui:211 msgid "Taskbar Display" msgstr "Wyświetlanie paska zadań" -#: ui/SettingsStyle.ui.h:1 +#: ui/SettingsStyle.ui:59 +msgid "Style" +msgstr "Wygląd" + +#: ui/SettingsStyle.ui:62 +msgid "Global style" +msgstr "Styl globalny" + +#: ui/SettingsStyle.ui:65 +msgid "Border radius" +msgstr "Promień zaokrąglenia krawędzi" + +#: ui/SettingsStyle.ui:82 msgid "AppIcon style" -msgstr "Styl ikon aplikacji" +msgstr "Styl ikon programu" -#: ui/SettingsStyle.ui.h:2 -msgid "App Icon Margin" -msgstr "Odstęp między ikonami" - -#: ui/SettingsStyle.ui.h:3 +#: ui/SettingsStyle.ui:85 msgid "(default is 8)" -msgstr "(domyślnie 8)" +msgstr "(Domyślnie 8)" -#: ui/SettingsStyle.ui.h:4 -msgid "App Icon Padding" -msgstr "Wypełnienie ikony aplikacji" +#: ui/SettingsStyle.ui:86 +msgid "App Icon Margin" +msgstr "Margines ikony programu" -#: ui/SettingsStyle.ui.h:5 +#: ui/SettingsStyle.ui:101 msgid "(default is 4)" -msgstr "(domyślnie 4)" +msgstr "(Domyślnie 4)" -#: ui/SettingsStyle.ui.h:6 +#: ui/SettingsStyle.ui:102 +msgid "App Icon Padding" +msgstr "Wypełnienie ikony programów" + +#: ui/SettingsStyle.ui:117 msgid "Animate hovering app icons" -msgstr "Animuj przełączenie programów" +msgstr "Animuj ikony aplikacji po najechaniu kursorem" -#: ui/SettingsStyle.ui.h:7 +#: ui/SettingsStyle.ui:141 +msgid "Highlight hovering app icons" +msgstr "Podświetl ikony aplikacji po najechaniu kursorem" + +#: ui/SettingsStyle.ui:165 +msgid "Icon style" +msgstr "Styl ikony" + +#: ui/SettingsStyle.ui:170 +msgid "Normal" +msgstr "Normalna" + +#: ui/SettingsStyle.ui:171 +msgid "Symbolic" +msgstr "Symboliczna" + +#: ui/SettingsStyle.ui:172 +msgid "Grayscale" +msgstr "Skala szarości" + +#: ui/SettingsStyle.ui:182 msgid "Running indicator" -msgstr "Pozycja wskaźnika aktywnych okien" +msgstr "Wskaźnik uruchomienia" -#: ui/SettingsStyle.ui.h:8 +#: ui/SettingsStyle.ui:185 msgid "Running indicator position" -msgstr "Pozycja wskaźnika aktywnych okien" +msgstr "Pozycja wskaźnika uruchomienia" -#: ui/SettingsStyle.ui.h:13 +#: ui/SettingsStyle.ui:221 msgid "Running indicator style (Focused app)" -msgstr "Wygląd wskaźnika (okno na pierwszym planie)" +msgstr "Wygląd wskaźnika uruchomienia (okno na pierwszym planie)" -#: ui/SettingsStyle.ui.h:14 +#: ui/SettingsStyle.ui:239 ui/SettingsStyle.ui:258 msgid "Dots" msgstr "Kropki" -#: ui/SettingsStyle.ui.h:15 +#: ui/SettingsStyle.ui:240 ui/SettingsStyle.ui:259 msgid "Squares" msgstr "Kwadraty" -#: ui/SettingsStyle.ui.h:16 +#: ui/SettingsStyle.ui:241 ui/SettingsStyle.ui:260 msgid "Dashes" msgstr "Kreski" -#: ui/SettingsStyle.ui.h:17 +#: ui/SettingsStyle.ui:242 ui/SettingsStyle.ui:261 msgid "Segmented" msgstr "Segmenty" -#: ui/SettingsStyle.ui.h:18 +#: ui/SettingsStyle.ui:243 ui/SettingsStyle.ui:262 msgid "Solid" msgstr "Ciągły" -#: ui/SettingsStyle.ui.h:19 +#: ui/SettingsStyle.ui:244 ui/SettingsStyle.ui:263 msgid "Ciliora" msgstr "Rzęski" -#: ui/SettingsStyle.ui.h:20 +#: ui/SettingsStyle.ui:245 ui/SettingsStyle.ui:264 msgid "Metro" msgstr "Metro" -#: ui/SettingsStyle.ui.h:21 +#: ui/SettingsStyle.ui:253 msgid "Running indicator style (Unfocused apps)" -msgstr "Wygląd wskaźnika (okno na drugim planie)" +msgstr "Wygląd wskaźnika uruchomienia (okno na drugim planie)" -#: ui/SettingsStyle.ui.h:22 +#: ui/SettingsStyle.ui:274 msgid "Panel style" -msgstr "Inteligentne ukrywanie panelu" +msgstr "Styl panelu" -#: ui/SettingsStyle.ui.h:23 +#: ui/SettingsStyle.ui:277 ui/SettingsStyle.ui:293 ui/SettingsStyle.ui:309 +#: ui/SettingsStyle.ui:325 +msgid "(default is 0)" +msgstr "(Domyślnie 0)" + +#: ui/SettingsStyle.ui:278 +msgid "Side margins" +msgstr "Marginesy boczne" + +#: ui/SettingsStyle.ui:294 +msgid "Top and bottom margins" +msgstr "Marginesy górne i dolne" + +#: ui/SettingsStyle.ui:310 +msgid "Side padding" +msgstr "Wypełnienie boczne" + +#: ui/SettingsStyle.ui:326 +msgid "Top and bottom padding" +msgstr "Wypełnienie górne i dolne" + +#: ui/SettingsStyle.ui:345 msgid "Override panel theme background color" -msgstr "Zastąp kolor panelu " +msgstr "Zastąp kolor tła panelu" -#: ui/SettingsStyle.ui.h:24 +#: ui/SettingsStyle.ui:360 msgid "Override panel theme background opacity" -msgstr "Zastąp przeźroczystość panelu" +msgstr "Zastąp przeźroczystość tła panelu" -#: ui/SettingsStyle.ui.h:26 -#, no-c-format +#: ui/SettingsStyle.ui:370 msgid "Panel background opacity (%)" -msgstr "Przeźroczystość panelu (%)" +msgstr "Przeźroczystość tła panelu (%)" -#: ui/SettingsStyle.ui.h:28 -msgid "Dynamic background opacity" -msgstr "Dynamiczna przeźroczystość" - -#: ui/SettingsStyle.ui.h:29 +#: ui/SettingsStyle.ui:382 msgid "Change opacity when a window gets close to the panel" -msgstr "Zmiana przeźroczystości w kontakcie z oknem" +msgstr "Zmień przezroczystość, gdy okno zbliży się do panelu" -#: ui/SettingsStyle.ui.h:30 +#: ui/SettingsStyle.ui:383 +msgid "Dynamic background opacity" +msgstr "Dynamiczna przezroczystość tła" + +#: ui/SettingsStyle.ui:411 msgid "Override panel theme gradient" -msgstr "Zastąp kolor panelu gradientem " +msgstr "Zastąp kolor panelu gradientem" -#: ui/SettingsStyle.ui.h:32 -#, no-c-format +#: ui/SettingsStyle.ui:421 msgid "Gradient top color and opacity (%)" msgstr "Gradient górnego koloru i przeźroczystość (%)" -#: ui/SettingsStyle.ui.h:34 -#, no-c-format +#: ui/SettingsStyle.ui:439 msgid "Gradient bottom color and opacity (%)" msgstr "Gradient dolnego koloru i przeźroczystość (%)" @@ -1422,218 +3367,5 @@ msgstr "Pokaż ikony na pulpicie" msgid "Show Desktop button padding (px)" msgstr "Pokaż margines wewnętrzny przycisków pulpitu (px)" -msgid "Floating rounded theme" -msgstr "Pływający, zaokrąglony motyw" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Izoluj obszary robocze i monitory w ustawieniach Przełączanie programów" - -#~ msgid "Top, with plugin icons collapsed to bottom" -#~ msgstr "Góra, ikony wtyczek wyrównane do prawej" - -#~ msgid "Left, with plugin icons collapsed to right" -#~ msgstr "Lewa, ikony wtyczek wyrównane do prawej" - -#~ msgid "Top, with fixed center plugin icons" -#~ msgstr "Góra, ze stałym wyśrodkowaniem ikon wtyczek" - -#~ msgid "Left, with fixed center plugin icons" -#~ msgstr "Lewa, ze stałym wyśrodkowaniem ikon wtyczek" - -#~ msgid "Top, with floating center plugin icons" -#~ msgstr "Góra, ze swobodnym wyśrodkowaniem ikon wtyczek" - -#~ msgid "Left, with floating center plugin icons" -#~ msgstr "Lewa, ze swobodnym wyśrodkowaniem ikon wtyczek" - -#~ msgid "Center, fixed in middle of monitor" -#~ msgstr "Środek, ze stałym wyśrodkowaniem na monitorze" - -#~ msgid "Center, floating between top and bottom elements" -#~ msgstr "Środek, z elementami swobodnymi między górą a dołem" - -#~ msgid "Center, floating between left and right elements" -#~ msgstr "Środek, z elementami swobodnymi między lewą a prawą stroną" - -#~ msgid "Top of plugin icons" -#~ msgstr "Górna strona ikon wtyczek" - -#~ msgid "Left of plugin icons" -#~ msgstr "Lewa strona ikon wtyczek" - -#~ msgid "Bottom of plugin icons" -#~ msgstr "Dolna strona ikon wtyczek" - -#~ msgid "Right of plugin icons" -#~ msgstr "Prawa strona ikon wtyczek" - -#~ msgid "Top of system indicators" -#~ msgstr "Górna strona menu systemowego" - -#~ msgid "Left of system indicators" -#~ msgstr "Lewa strona menu systemowego" - -#~ msgid "Bottom of system indicators" -#~ msgstr "Dolna strona menu systemowego" - -#~ msgid "Right of system indicators" -#~ msgstr "Prawa strona menu systemowego" - -#~ msgid "Left of taskbar" -#~ msgstr "Lewa strona paska zadań" - -#~ msgid "Bottom of taskbar" -#~ msgstr "Dolna strona paska zadań" - -#~ msgid "Right of taskbar" -#~ msgstr "Prawa strona paska zadań" - -#~ msgid "Show Details" -#~ msgstr "Pokaż szczegóły" - -#~ msgid "New Window" -#~ msgstr "Nowe okno" - -#, javascript-format -#~ msgid "Version %s (%s) is available" -#~ msgstr "Wersja %s (%s) jest dostępna" - -#~ msgid "Details" -#~ msgstr "Szczegóły" - -#~ msgid "Update" -#~ msgstr "Aktualizacja" - -#~ msgid "Already up to date" -#~ msgstr "Brak nowszej wersji" - -#~ msgid "Update successful, please log out/in" -#~ msgstr "Aktualizacja zakończona pomyślnie, zaloguj się ponownie" - -#~ msgid "Log out" -#~ msgstr "Wyloguj" - -#~ msgid "Update successful, please restart GNOME Shell" -#~ msgstr "Aktualizacja zakończona pomyślnie, prosimy zrestartować powłokę GNOME" - -#~ msgid "Restart GNOME Shell" -#~ msgstr "Zrestartuj powłokę GNOME" - -#~ msgid "Restarting GNOME Shell..." -#~ msgstr "Restartowanie powłoki GNOME..." - -#~ msgid "Error: " -#~ msgstr "Błąd: " - -#~ msgid "Display favorite applications on all monitors" -#~ msgstr "Ulubione programy na wszystkich monitorach" - -#~ msgid "Display the clock on all monitors" -#~ msgstr "Zegar na wszystkich monitorach" - -#~ msgid "Display the status menu on all monitors" -#~ msgstr "Menu systemowe na wszystkich monitorach" - -#~ msgid "Current Show Applications icon" -#~ msgstr "Aktualna ikona przycisku Pokaż programy" - -#~ msgid "Select a Show Applications image icon" -#~ msgstr "Wybierz ikonę przycisku Pokaż programy" - -#~ msgid "Custom Show Applications image icon" -#~ msgstr "Wybierz własną ikonę przycisku Pokaż programy" - -#~ msgid "Taskbar position" -#~ msgstr "Położenie paska zadań" - -#~ msgid "Clock location" -#~ msgstr "Położenie zegara" - -#~ msgid "Style" -#~ msgstr "Wygląd" - -#~ msgid "Show Applications icon" -#~ msgstr "Pokaż ikonę Programy" - -#~ msgid "Animate Show Applications." -#~ msgstr "Animuj pokazanie programów." - -#~ msgid "Top Bar > Show App Menu must be enabled in Tweak Tool" -#~ msgstr "Górny pasek > Menu programu - musi być włączone w Dostrajanie" - -#~ msgid "Behavior" -#~ msgstr "Reakcja" - -#~ msgid "Action" -#~ msgstr "Działanie" - -#~ msgid "Fine-Tune" -#~ msgstr "Dostrajanie" - -#~ msgid "Position" -#~ msgstr "Pozycja" - -#~ msgid "Icon style" -#~ msgstr "Styl ikony" - -#~ msgid "Normal" -#~ msgstr "Normalna" - -#~ msgid "Symbolic" -#~ msgstr "Symboliczna" - -#~ msgid "" -#~ "This allows you to update the extension directly from the GitHub " -#~ "repository." -#~ msgstr "" -#~ "Ta opcja pozwala ci na zaktualizowanie wtyczki bezpośrednio z " -#~ "repozytorium GitHub." - -#~ msgid "Updates" -#~ msgstr "Aktualizacje" - -#~ msgid "Periodically check for updates" -#~ msgstr "Sprawdzaj regularnie dostępność aktualizacji" - -#~ msgid "Check now" -#~ msgstr "Sprawdź teraz" - -#~ msgid "" -#~ "Be aware, these official Dash to " -#~ "Panel releases might not be reviewed yet on extensions.gnome.org! " -#~ "Read more" -#~ msgstr "" -#~ "Bądź świadom, że te oficjalne " -#~ "wydania Dash to Panel mogły jeszcze nie przejść weryfikacji na extensions." -#~ "gnome.org! Więcej " -#~ "informacji" - -#~ msgid "About" -#~ msgstr "Informacje" - -#~ msgid "Highlight color" -#~ msgstr "Kolor podświetlenia" - -#~ msgid "Preview timeout on icon leave (ms)" -#~ msgstr "Opóźnienie wygaszenia podglądu po opuszczeniu ikony (ms)" - -#~ msgid "" -#~ "If set too low, the window preview of running applications may seem to " -#~ "close too quickly when trying to enter the popup. If set too high, the " -#~ "preview may linger too long when moving to an adjacent icon." -#~ msgstr "" -#~ "Ustawienie zbyt małej wartości może spowodować, że pogląd okien może " -#~ "zamykać się zbyt szybko, natomiast za duża wartość spowoduje długi czas " -#~ "wyświetlania podglądu po przejściu do sąsiedniej ikony." - -#~ msgid "Middle click to close window" -#~ msgstr "Zamknięcie okna poprzez kliknięcie środkowego przycisku" - -#~ msgid "Width of the window previews (px)" -#~ msgstr "Szerokość miniatury (px)" - -#~ msgid "Height of the window previews (px)" -#~ msgstr "Wysokość miniatury (px)" - -#~ msgid "Padding of the window previews (px)" -#~ msgstr "Odstęp między miniaturami (px)" diff --git a/po/pt.po b/po/pt.po index 750324e..22aa7b7 100644 --- a/po/pt.po +++ b/po/pt.po @@ -22,8 +22,5 @@ msgstr "Mostrar ícone da Área de Trabalho" msgid "Show Desktop button padding (px)" msgstr "Preenchimento do botão Mostrar Área de Trabalho (px)" -msgid "Floating rounded theme" -msgstr "Tema arredondado flutuante" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isolar espaços de trabalho e monitores nas configurações de Comutação de aplicações" diff --git a/po/pt_BR.po b/po/pt_BR.po index 5fe9e31..dde81b0 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -1513,9 +1513,6 @@ msgstr "Mostrar ícone da Área de Trabalho" msgid "Show Desktop button padding (px)" msgstr "Preenchimento do botão Mostrar Área de Trabalho (px)" -msgid "Floating rounded theme" -msgstr "Tema arredondado flutuante" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Isolar espaços de trabalho e monitores nas configurações de Comutação de aplicações" diff --git a/po/ru.po b/po/ru.po index a34a936..0659e0b 100644 --- a/po/ru.po +++ b/po/ru.po @@ -1461,9 +1461,6 @@ msgstr "Показать значок рабочего стола" msgid "Show Desktop button padding (px)" msgstr "Показать рабочий стол - отступ внутри кнопки (px)" -msgid "Floating rounded theme" -msgstr "Плавающая закругленная тема" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Изолируйте рабочие столы и мониторы в настройках Переключения между приложениями" diff --git a/po/sk.po b/po/sk.po index 951c0f5..9ffcf08 100644 --- a/po/sk.po +++ b/po/sk.po @@ -1404,9 +1404,6 @@ msgstr "Zobrať ikonu plochy" msgid "Show Desktop button padding (px)" msgstr "Zobraziť ikonu plochy výplň (px)" -msgid "Floating rounded theme" -msgstr "Zaoblený plávajúci motív" - #, javascript-format #~ msgid "%d ." #~ msgstr "%d ." diff --git a/po/sl.po b/po/sl.po index 5aaac62..9ce07e1 100644 --- a/po/sl.po +++ b/po/sl.po @@ -21,6 +21,3 @@ msgstr "Pokaži ikono za namizje" msgid "Show Desktop button padding (px)" msgstr "Pokaži ikono za namizje razmik (px)" - -msgid "Floating rounded theme" -msgstr "Plavajoča zaokrožena tema" diff --git a/po/sq.po b/po/sq.po index 09fd8c7..f46e0fa 100644 --- a/po/sq.po +++ b/po/sq.po @@ -15,6 +15,3 @@ msgstr "Shfaq ikonën e desktopit" msgid "Show Desktop button padding (px)" msgstr "Mbushja Shfaq butonin e desktopit (px)" - -msgid "Floating rounded theme" -msgstr "Temë lundruese e rrumbullakosur" diff --git a/po/sr.po b/po/sr.po index 17b914f..32d4269 100644 --- a/po/sr.po +++ b/po/sr.po @@ -21,6 +21,3 @@ msgstr "Прикажи икону радне површине" msgid "Show Desktop button padding (px)" msgstr "Подлога „Прикажи дугме радне површине“ (пк)" - -msgid "Floating rounded theme" -msgstr "Плутајућа заобљена тема" diff --git a/po/sv.po b/po/sv.po index daa2aa8..336c77c 100644 --- a/po/sv.po +++ b/po/sv.po @@ -1504,9 +1504,6 @@ msgstr "Visa Skrivbordsikonen" msgid "Show Desktop button padding (px)" msgstr "Fyllnad (px) för knappen Visa Skrivbordet" -msgid "Floating rounded theme" -msgstr "Flytande rundat tema" - #~ msgid "Top, with plugin icons collapsed to bottom" #~ msgstr "Topp, med tilläggsikoner kollapsade till botten" diff --git a/po/ta.po b/po/ta.po index cc8c9f2..fa40a4d 100644 --- a/po/ta.po +++ b/po/ta.po @@ -21,6 +21,3 @@ msgstr "டெஸ்க்டாப் ஐகானைக் காட்டு" msgid "Show Desktop button padding (px)" msgstr "டெஸ்க்டாப் பொத்தானைக் காட்டு திணிப்பு (px)" - -msgid "Floating rounded theme" -msgstr "மிதக்கும் வட்டமான தீம்" diff --git a/po/te.po b/po/te.po index 0cb3205..3ca9150 100644 --- a/po/te.po +++ b/po/te.po @@ -21,6 +21,3 @@ msgstr "డెస్క్‌టాప్ చిహ్నాన్ని చూ msgid "Show Desktop button padding (px)" msgstr "డెస్క్‌టాప్ బటన్ చూపించు పాడింగ్ (px)" - -msgid "Floating rounded theme" -msgstr "తేలియాడే గుండ్రని థీమ్" diff --git a/po/th.po b/po/th.po index da784e7..8dad220 100644 --- a/po/th.po +++ b/po/th.po @@ -21,6 +21,3 @@ msgstr "แสดงไอคอนเดสก์ท็อป" msgid "Show Desktop button padding (px)" msgstr "ช่องว่างภายใน แสดงปุ่มเดสก์ท็อป (px)" - -msgid "Floating rounded theme" -msgstr "ธีมกลมลอย" diff --git a/po/tr.po b/po/tr.po index 4760bfa..1419897 100644 --- a/po/tr.po +++ b/po/tr.po @@ -1525,9 +1525,6 @@ msgstr "Masaüstünü Göster simgesi" msgid "Show Desktop button padding (px)" msgstr "Masaüstünü göster butonu boşluğu (px)" -msgid "Floating rounded theme" -msgstr "Yüzen yuvarlatılmış tema" - msgid "Isolate Workspaces and Monitors in Application Switching settings" msgstr "Uygulama Geçişi ayarlarında Çalışma Alanlarını ve Monitörleri Yalıtım" diff --git a/po/uk.po b/po/uk.po index 403ac0d..266ea1e 100644 --- a/po/uk.po +++ b/po/uk.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-04-03 00:40+0300\n" -"PO-Revision-Date: 2024-04-03 01:12+0300\n" +"POT-Creation-Date: 2025-03-09 22:39+0200\n" +"PO-Revision-Date: 2025-03-09 22:56+0200\n" "Last-Translator: Yevhen Popok \n" "Language-Team: \n" "Language: uk\n" @@ -17,171 +17,239 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Poedit 3.4.2\n" +"X-Generator: Poedit 3.4.4\n" -#: prefs.js:243 +#: src/extension.js:92 +msgid "Dash to Panel has been updated!" +msgstr "Dash to Panel оновлено!" + +#: src/extension.js:93 +msgid "You are now running version" +msgstr "Тепер у вас версія" + +#: src/extension.js:99 +msgid "See what's new" +msgstr "Що нового" + +#: src/prefs.js:275 msgid "Show Desktop button height (px)" msgstr "Висота кнопки «Показати Стільницю» (пкс)" -#: prefs.js:243 +#: src/prefs.js:276 msgid "Show Desktop button width (px)" msgstr "Ширина кнопки «Показати Стільницю» (пкс)" -#: prefs.js:255 -msgid "Unavailable when gnome-shell top panel is present" -msgstr "Недоступно, якщо присутня верхня панель gnome-shell" - -#: prefs.js:313 ui/SettingsPosition.ui:13 ui/SettingsStyle.ui:15 +#: src/prefs.js:348 ui/SettingsPosition.ui:133 ui/SettingsStyle.ui:204 msgid "Left" msgstr "Ліворуч" -#: prefs.js:314 +#: src/prefs.js:349 msgid "Center" -msgstr "У центрі" +msgstr "По центру" -#: prefs.js:315 ui/SettingsPosition.ui:14 ui/SettingsStyle.ui:16 +#: src/prefs.js:350 ui/SettingsPosition.ui:142 ui/SettingsStyle.ui:212 msgid "Right" msgstr "Праворуч" -#: prefs.js:317 ui/BoxWindowPreviewOptions.ui:38 ui/SettingsPosition.ui:12 -#: ui/SettingsStyle.ui:14 +#: src/prefs.js:352 ui/BoxWindowPreviewOptions.ui:334 +#: ui/SettingsPosition.ui:124 ui/SettingsStyle.ui:196 msgid "Top" msgstr "Вгорі" -#: prefs.js:318 prefs.js:323 ui/SettingsPosition.ui:22 +#: src/prefs.js:353 src/prefs.js:358 ui/SettingsPosition.ui:198 msgid "Middle" msgstr "Посередині" -#: prefs.js:319 ui/BoxWindowPreviewOptions.ui:37 ui/SettingsPosition.ui:11 -#: ui/SettingsStyle.ui:13 +#: src/prefs.js:354 ui/BoxWindowPreviewOptions.ui:325 +#: ui/SettingsPosition.ui:115 ui/SettingsStyle.ui:188 msgid "Bottom" msgstr "Знизу" -#: prefs.js:322 ui/SettingsPosition.ui:21 +#: src/prefs.js:357 ui/SettingsPosition.ui:197 msgid "Start" msgstr "На початку" -#: prefs.js:324 ui/SettingsPosition.ui:23 +#: src/prefs.js:359 ui/SettingsPosition.ui:199 msgid "End" msgstr "В кінці" -#: prefs.js:409 +#: src/prefs.js:484 msgid "Show Applications button" msgstr "Кнопка «Показати програми»" -#: prefs.js:410 +#: src/prefs.js:485 msgid "Activities button" msgstr "Кнопка «Діяльність»" -#: prefs.js:411 +#: src/prefs.js:486 msgid "Taskbar" msgstr "Панель завдань" -#: prefs.js:412 +#: src/prefs.js:487 msgid "Date menu" msgstr "Меню дати" -#: prefs.js:413 +#: src/prefs.js:488 msgid "System menu" msgstr "Меню системи" -#: prefs.js:414 +#: src/prefs.js:489 msgid "Left box" msgstr "Лівий блок" -#: prefs.js:415 +#: src/prefs.js:490 msgid "Center box" msgstr "Центральний блок" -#: prefs.js:416 +#: src/prefs.js:491 msgid "Right box" msgstr "Правий блок" -#: prefs.js:417 +#: src/prefs.js:492 msgid "Desktop button" msgstr "Кнопка «Стільниця»" -#: prefs.js:423 +#: src/prefs.js:502 src/prefs.js:2788 msgid "Move up" msgstr "Перемістити вище" -#: prefs.js:425 +#: src/prefs.js:504 src/prefs.js:2796 msgid "Move down" msgstr "Перемістити нижче" -#: prefs.js:427 +#: src/prefs.js:510 msgid "Visible" -msgstr "Видимий" +msgstr "Видимість" -#: prefs.js:428 +#: src/prefs.js:514 msgid "Select element position" -msgstr "Оберіть розміщення елемента" +msgstr "Визначте розміщення елемента" -#: prefs.js:439 +#: src/prefs.js:528 msgid "Stacked to top" -msgstr "Укладний вгору" +msgstr "Укладено вгору" -#: prefs.js:439 +#: src/prefs.js:528 msgid "Stacked to left" -msgstr "Укладений ліворуч" +msgstr "Укладено ліворуч" -#: prefs.js:440 +#: src/prefs.js:532 msgid "Stacked to bottom" -msgstr "Укладений донизу" +msgstr "Укладено вниз" -#: prefs.js:440 +#: src/prefs.js:532 msgid "Stacked to right" -msgstr "Укладений праворуч" +msgstr "Укладено праворуч" -#: prefs.js:441 +#: src/prefs.js:534 msgid "Centered" -msgstr "Відцентрований" +msgstr "По центру" -#: prefs.js:442 +#: src/prefs.js:535 msgid "Monitor Center" -msgstr "У центрі монітора" +msgstr "По центру монітора" -#: prefs.js:461 +#: src/prefs.js:562 msgid "More options" msgstr "Більше параметрів" -#: prefs.js:493 +#: src/prefs.js:597 msgid "Reset to defaults" msgstr "Скинути налаштування" -#: prefs.js:516 +#: src/prefs.js:620 msgid "Show Applications options" msgstr "Параметри «Показати програми»" -#: prefs.js:526 +#: src/prefs.js:643 msgid "Open icon" msgstr "Відкрити піктограму" -#: prefs.js:573 +#: src/prefs.js:708 msgid "Show Desktop options" -msgstr "Параметри «Показати стільницю»" +msgstr "Параметри «Показати Cтільницю»" -#: prefs.js:657 +#: src/prefs.js:800 +msgid "Primary monitor" +msgstr "Основний монітор" + +#: src/prefs.js:801 +msgid "Monitor " +msgstr "Монітор " + +#: src/prefs.js:928 +msgid "Running Indicator Options" +msgstr "Параметри індикатора роботи" + +#: src/prefs.js:1461 +msgid "Dynamic opacity options" +msgstr "Параметри динамічної непрозорості" + +#: src/prefs.js:1710 +msgid "Intellihide options" +msgstr "Параметри розумного автоприховування" + +#: src/prefs.js:1916 +msgid "Window preview options" +msgstr "Параметри мініатюр вікон" + +#: src/prefs.js:2467 +msgid "Ungrouped application options" +msgstr "Параметри не згрупованих програм" + +#: src/prefs.js:2634 +msgid "Customize middle-click behavior" +msgstr "Поведінка під час клацання середньою" + +#: src/prefs.js:2763 +msgid "Text" +msgstr "Текст" + +#: src/prefs.js:2772 +msgid "Command" +msgstr "Команда" + +#: src/prefs.js:2804 +msgid "Remove" +msgstr "Вилучити" + +#: src/prefs.js:2832 +msgid "Customize panel scroll behavior" +msgstr "Налаштування гортання над панеллю" + +#: src/prefs.js:2878 +msgid "Customize icon scroll behavior" +msgstr "Налаштування гортання над піктограмою" + +#: src/prefs.js:3002 +msgid "Advanced hotkeys options" +msgstr "Розширені параметри гарячих клавіш" + +#: src/prefs.js:3030 +msgid "Secondary Menu Options" +msgstr "Параметри контекстного меню" + +#: src/prefs.js:3230 #, javascript-format msgid "%d ms" msgstr "%d мс" -#: prefs.js:662 +#: src/prefs.js:3236 #, javascript-format msgid "%d °" msgstr "%d °" -#: prefs.js:667 prefs.js:672 +#: src/prefs.js:3242 src/prefs.js:3248 #, javascript-format msgid "%d %%" msgstr "%d %%" -#: prefs.js:677 +#: src/prefs.js:3254 #, javascript-format msgid "%.1f" msgstr "%.1f" -#: prefs.js:682 +#: src/prefs.js:3260 #, javascript-format msgid "%d icon" msgid_plural "%d icons" @@ -189,75 +257,28 @@ msgstr[0] "%d піктограма" msgstr[1] "%d піктограми" msgstr[2] "%d піктограм" -#: prefs.js:784 -msgid "Running Indicator Options" -msgstr "Налаштування індикатора роботи" - -#: prefs.js:930 -msgid "Primary monitor" -msgstr "Основний монітор" - -#: prefs.js:930 -msgid "Monitor " -msgstr "Монітор" - -#: prefs.js:1124 -msgid "Dynamic opacity options" -msgstr "Параметри динамічної непрозорості" - -#: prefs.js:1257 -msgid "Intellihide options" -msgstr "Параметри розумного автоприховування" - -#: prefs.js:1363 -msgid "Window preview options" -msgstr "Параметри мініатюр вікон" - -#: prefs.js:1639 -msgid "Ungrouped application options" -msgstr "Параметри незгрупованих програм" - -#: prefs.js:1718 -msgid "Customize middle-click behavior" -msgstr "Поведінка при кліку середньою кнопкою" - -#: prefs.js:1768 -msgid "Customize panel scroll behavior" -msgstr "Параметри гортання над панеллю" - -#: prefs.js:1796 -msgid "Customize icon scroll behavior" -msgstr "Параметри гортання над піктограмою" - -#: prefs.js:1877 -msgid "Advanced hotkeys options" -msgstr "Розширені параметри гарячих клавіш" - -#: prefs.js:1895 -msgid "Secondary Menu Options" -msgstr "Параметри контекстного меню" - -#: prefs.js:1922 ui/SettingsFineTune.ui:23 -msgid "Advanced Options" -msgstr "Розширені налаштування" - -#: prefs.js:2038 +#: src/prefs.js:3387 msgid "App icon animation options" -msgstr "Параметри анімування піктограм програм" +msgstr "Параметри анімації піктограм програм" -#: prefs.js:2088 +#: src/prefs.js:3510 +msgid "App icon highlight options" +msgstr "Параметри підсвічування піктограм програм" + +#: src/prefs.js:3599 msgid "Export settings" -msgstr "Експортувати параметри" +msgstr "Експортувати налаштування" -#: prefs.js:2104 +#: src/prefs.js:3620 msgid "Import settings" -msgstr "Імпортувати параметри" +msgstr "Імпортувати налаштування" -#: appIcons.js:1485 appIcons.js:1495 ui/BoxMiddleClickOptions.ui:10 +#: src/appIcons.js:1912 src/appIcons.js:1929 ui/BoxMiddleClickOptions.ui:33 +#: ui/BoxMiddleClickOptions.ui:62 ui/BoxMiddleClickOptions.ui:91 msgid "Quit" msgstr "Закрити" -#: appIcons.js:1497 +#: src/appIcons.js:1932 #, javascript-format msgid "Quit %d Window" msgid_plural "Quit %d Windows" @@ -265,249 +286,246 @@ msgstr[0] "Закрити %d вікно" msgstr[1] "Закрити %d вікна" msgstr[2] "Закрити %d вікон" -#: appIcons.js:1772 +#: src/appIcons.js:2254 msgid "Power options" msgstr "Параметри живлення" -#: appIcons.js:1777 +#: src/appIcons.js:2259 msgid "Event logs" msgstr "Журнали" -#: appIcons.js:1782 +#: src/appIcons.js:2264 msgid "System" msgstr "Система" -#: appIcons.js:1787 +#: src/appIcons.js:2269 msgid "Device Management" msgstr "Керування пристроями" -#: appIcons.js:1792 +#: src/appIcons.js:2274 msgid "Disk Management" msgstr "Керування дисками" -#: appIcons.js:1805 -msgid "Terminal" -msgstr "Термінал" - -#: appIcons.js:1810 msgid "System Monitor" msgstr "Системний монітор" -#: appIcons.js:1815 msgid "Files" msgstr "Файли" -#: appIcons.js:1820 -msgid "Extensions" -msgstr "Розширення" - -#: appIcons.js:1825 msgid "Settings" msgstr "Параметри" -#: appIcons.js:1836 +#: src/appIcons.js:2305 msgid "Unlock taskbar" msgstr "Розблокувати панель завдань" -#: appIcons.js:1836 +#: src/appIcons.js:2306 msgid "Lock taskbar" msgstr "Заблокувати панель завдань" -#: appIcons.js:1841 +#: src/appIcons.js:2316 +msgid "Gnome Settings" +msgstr "Параметри Gnome" + +#: src/appIcons.js:2320 msgid "Taskbar Settings" msgstr "Налаштування панелі завдань" -#: appIcons.js:1846 +#: src/appIcons.js:2327 msgid "Restore Windows" msgstr "Відновити вікна" -#: appIcons.js:1846 +#: src/appIcons.js:2328 msgid "Show Desktop" msgstr "Показати Стільницю" -#: ui/BoxAdvancedOptions.ui:1 -msgid "Nothing yet!" -msgstr "Поки нічого!" +#: src/taskbar.js:66 +msgid "Donation options" +msgstr "Можливості фінансової підтримки" -#: ui/BoxAdvancedOptions.ui:2 -msgid "For real..." -msgstr "Справді..." +#: src/taskbar.js:79 +msgid "Thank you!" +msgstr "Дякую!" -#: ui/BoxAnimateAppIconHoverOptions.ui:1 +#: src/taskbar.js:79 +msgid "Please donate :)" +msgstr "Прохання підтримати фінансово :)" + +#: ui/BoxAnimateAppIconHoverOptions.ui:62 msgid "Animation type" -msgstr "Тип анімації" +msgstr "Вид анімації" -#: ui/BoxAnimateAppIconHoverOptions.ui:2 +#: ui/BoxAnimateAppIconHoverOptions.ui:67 msgid "Simple" msgstr "Проста" -#: ui/BoxAnimateAppIconHoverOptions.ui:3 +#: ui/BoxAnimateAppIconHoverOptions.ui:68 msgid "Ripple" msgstr "Хвиля" -#: ui/BoxAnimateAppIconHoverOptions.ui:4 +#: ui/BoxAnimateAppIconHoverOptions.ui:69 msgid "Plank" msgstr "Plank" -#: ui/BoxAnimateAppIconHoverOptions.ui:5 +#: ui/BoxAnimateAppIconHoverOptions.ui:84 msgid "Duration" msgstr "Тривалість" -#: ui/BoxAnimateAppIconHoverOptions.ui:6 +#: ui/BoxAnimateAppIconHoverOptions.ui:101 msgid "Rotation" msgstr "Обертання" -#: ui/BoxAnimateAppIconHoverOptions.ui:7 +#: ui/BoxAnimateAppIconHoverOptions.ui:118 msgid "Travel" msgstr "Переміщення" -#: ui/BoxAnimateAppIconHoverOptions.ui:8 +#: ui/BoxAnimateAppIconHoverOptions.ui:135 msgid "Zoom" msgstr "Масштабування" -#: ui/BoxAnimateAppIconHoverOptions.ui:9 +#: ui/BoxAnimateAppIconHoverOptions.ui:152 msgid "Convexity" msgstr "Вигин" -#: ui/BoxAnimateAppIconHoverOptions.ui:10 +#: ui/BoxAnimateAppIconHoverOptions.ui:169 msgid "Extent" msgstr "Поширення" -#: ui/BoxDotOptions.ui:1 +#: ui/BoxDotOptions.ui:37 msgid "Highlight focused application" msgstr "Підсвічувати програми у фокусі" -#: ui/BoxDotOptions.ui:2 +#: ui/BoxDotOptions.ui:54 msgid "Icon dominant color" msgstr "Домінантний колір піктограми" -#: ui/BoxDotOptions.ui:3 +#: ui/BoxDotOptions.ui:65 msgid "Custom color" msgstr "Власний колір" -#: ui/BoxDotOptions.ui:4 +#: ui/BoxDotOptions.ui:76 msgid "Highlight opacity" msgstr "Непрозорість підсвічування" -#: ui/BoxDotOptions.ui:5 +#: ui/BoxDotOptions.ui:96 msgid "Indicator size (px)" msgstr "Розмір індикатора (пкс)" -#: ui/BoxDotOptions.ui:6 +#: ui/BoxDotOptions.ui:109 msgid "Indicator color - Icon Dominant" msgstr "Колір індикатора - домінантний в піктограмі" -#: ui/BoxDotOptions.ui:7 +#: ui/BoxDotOptions.ui:126 msgid "Indicator color - Override Theme" -msgstr "Колір індикатора - не як в темі" +msgstr "Колір індикатора - замінює той, що в темі" -#: ui/BoxDotOptions.ui:8 +#: ui/BoxDotOptions.ui:142 ui/BoxDotOptions.ui:215 msgid "1 window open (or ungrouped)" -msgstr "1 відкрите вікно (або незгруповане)" +msgstr "1 вікно відкрито (або не згруповано)" -#: ui/BoxDotOptions.ui:9 +#: ui/BoxDotOptions.ui:146 ui/BoxDotOptions.ui:219 msgid "Apply to all" msgstr "Застосувати для всіх" -#: ui/BoxDotOptions.ui:10 +#: ui/BoxDotOptions.ui:159 ui/BoxDotOptions.ui:232 msgid "2 windows open" -msgstr "2 відкритих вікна" +msgstr "2 вікна відкрито" -#: ui/BoxDotOptions.ui:11 +#: ui/BoxDotOptions.ui:170 ui/BoxDotOptions.ui:243 msgid "3 windows open" -msgstr "3 відкритих вікна" +msgstr "3 вікна відкрито" -#: ui/BoxDotOptions.ui:12 +#: ui/BoxDotOptions.ui:181 ui/BoxDotOptions.ui:254 msgid "4+ windows open" -msgstr "4 та більше відкритих вікон" +msgstr "4 та більше вікон відкрито" -#: ui/BoxDotOptions.ui:13 +#: ui/BoxDotOptions.ui:198 msgid "Use different for unfocused" msgstr "Окремо для вікон не у фокусі" -#: ui/BoxDynamicOpacityOptions.ui:1 +#: ui/BoxDynamicOpacityOptions.ui:37 msgid "The panel background opacity is affected by" -msgstr "Непрозорий фон панелі зумовлюють" +msgstr "Непрозорість тла панелі зумовлюють" -#: ui/BoxDynamicOpacityOptions.ui:2 ui/BoxIntellihideOptions.ui:3 +#: ui/BoxDynamicOpacityOptions.ui:42 ui/BoxIntellihideOptions.ui:62 msgid "All windows" msgstr "Всі вікна" -#: ui/BoxDynamicOpacityOptions.ui:3 ui/BoxIntellihideOptions.ui:4 +#: ui/BoxDynamicOpacityOptions.ui:43 ui/BoxIntellihideOptions.ui:63 msgid "Focused windows" msgstr "Вікна у фокусі" -#: ui/BoxDynamicOpacityOptions.ui:4 ui/BoxIntellihideOptions.ui:5 +#: ui/BoxDynamicOpacityOptions.ui:44 ui/BoxIntellihideOptions.ui:64 msgid "Maximized windows" msgstr "Розгорнуті вікна" -#: ui/BoxDynamicOpacityOptions.ui:5 +#: ui/BoxDynamicOpacityOptions.ui:53 msgid "Change opacity when a window gets closer than (px)" msgstr "Змінювати непрозорість, якщо вікно ближче, ніж (пкс)" -#: ui/BoxDynamicOpacityOptions.ui:7 -#, no-c-format +#: ui/BoxDynamicOpacityOptions.ui:69 msgid "Change opacity to (%)" msgstr "Значення непрозорості (%)" -#: ui/BoxDynamicOpacityOptions.ui:8 ui/BoxShowApplicationsOptions.ui:3 -#: ui/BoxWindowPreviewOptions.ui:57 ui/SettingsStyle.ui:31 +#: ui/BoxDynamicOpacityOptions.ui:72 ui/BoxShowApplicationsOptions.ui:64 +#: ui/BoxWindowPreviewOptions.ui:472 ui/SettingsStyle.ui:374 +#: ui/SettingsStyle.ui:431 ui/SettingsStyle.ui:449 msgid "0" msgstr "0" -#: ui/BoxDynamicOpacityOptions.ui:9 +#: ui/BoxDynamicOpacityOptions.ui:82 msgid "Opacity change animation duration (ms)" msgstr "Тривалість анімації зміни непрозорості (в мс)" -#: ui/BoxGroupAppsOptions.ui:1 +#: ui/BoxGroupAppsOptions.ui:32 msgid "Font size (px) of the application titles (default is 14)" -msgstr "Розмір шрифту (пкс) в заголовках програм (типово - 14)" +msgstr "Розмір шрифту (пкс) заголовків програм (типово - 14)" -#: ui/BoxGroupAppsOptions.ui:2 +#: ui/BoxGroupAppsOptions.ui:47 msgid "Font weight of application titles" -msgstr "Товщина шрифту в заголовках програм" +msgstr "Товщина шрифту заголовків програм" -#: ui/BoxGroupAppsOptions.ui:3 ui/BoxWindowPreviewOptions.ui:44 +#: ui/BoxGroupAppsOptions.ui:52 ui/BoxWindowPreviewOptions.ui:406 msgid "inherit from theme" -msgstr "Як в темі" +msgstr "як в темі" -#: ui/BoxGroupAppsOptions.ui:4 ui/BoxWindowPreviewOptions.ui:45 +#: ui/BoxGroupAppsOptions.ui:53 ui/BoxWindowPreviewOptions.ui:407 msgid "normal" -msgstr "Звичайний" +msgstr "звичайний" -#: ui/BoxGroupAppsOptions.ui:5 ui/BoxWindowPreviewOptions.ui:46 +#: ui/BoxGroupAppsOptions.ui:54 ui/BoxWindowPreviewOptions.ui:408 msgid "lighter" -msgstr "Світліший" +msgstr "світліший" -#: ui/BoxGroupAppsOptions.ui:6 ui/BoxWindowPreviewOptions.ui:47 +#: ui/BoxGroupAppsOptions.ui:55 ui/BoxWindowPreviewOptions.ui:409 msgid "bold" -msgstr "Жирний" +msgstr "жирний" -#: ui/BoxGroupAppsOptions.ui:7 ui/BoxWindowPreviewOptions.ui:48 +#: ui/BoxGroupAppsOptions.ui:56 ui/BoxWindowPreviewOptions.ui:410 msgid "bolder" -msgstr "Жирніший" +msgstr "жирніший" -#: ui/BoxGroupAppsOptions.ui:8 +#: ui/BoxGroupAppsOptions.ui:65 msgid "Font color of the application titles" -msgstr "Колір шрифту в заголовках програм" +msgstr "Колір шрифту заголовків програм" -#: ui/BoxGroupAppsOptions.ui:9 +#: ui/BoxGroupAppsOptions.ui:77 msgid "Font color of the minimized application titles" -msgstr "Колір шрифту в заголовках згорнутих програм" +msgstr "Колір шрифту заголовків згорнутих програм" -#: ui/BoxGroupAppsOptions.ui:10 +#: ui/BoxGroupAppsOptions.ui:95 msgid "Maximum width (px) of the application titles" msgstr "Максимальна довжина (пкс) заголовків програм" -#: ui/BoxGroupAppsOptions.ui:11 +#: ui/BoxGroupAppsOptions.ui:96 msgid "(default is 160)" msgstr "(типово - 160)" -#: ui/BoxGroupAppsOptions.ui:12 +#: ui/BoxGroupAppsOptions.ui:111 msgid "Use a fixed width for the application titles" msgstr "Використовувати сталу довжину заголовків програм" -#: ui/BoxGroupAppsOptions.ui:13 +#: ui/BoxGroupAppsOptions.ui:112 msgid "" "The application titles all have the same width, even if their texts are " "shorter than the maximum width. The maximum width value is used as the fixed " @@ -517,498 +535,559 @@ msgstr "" "максимальної довжини. Значення максимальної довжини буде використано для " "сталої довжини." -#: ui/BoxGroupAppsOptions.ui:14 +#: ui/BoxGroupAppsOptions.ui:129 msgid "Display running indicators on unfocused applications" msgstr "Показувати індикатор роботи для програм не у фокусі" -#: ui/BoxGroupAppsOptions.ui:15 +#: ui/BoxGroupAppsOptions.ui:140 msgid "Use the favorite icons as application launchers" -msgstr "Піктограми пришпилених програм використовуються як запускачі" +msgstr "Зробити піктограми пришпилених програм запускачами" -#: ui/BoxIntellihideOptions.ui:1 +#: ui/BoxHighlightAppIconHoverOptions.ui:26 +msgid "Highlight AppIcon color" +msgstr "Колір підсвічування піктограм програм" + +#: ui/BoxHighlightAppIconHoverOptions.ui:38 +msgid "Pressed AppIcon color" +msgstr "Колір піктограм програм під час натискання" + +#: ui/BoxHighlightAppIconHoverOptions.ui:50 +msgid "Highlight AppIcon border radius" +msgstr "Радіус рамки підсвічування піктограм програм" + +#: ui/BoxHighlightAppIconHoverOptions.ui:51 +msgid "Overrides global border radius (default is 0)" +msgstr "Замінює глобальний радіус межі (типово - 0)" + +#: ui/BoxIntellihideOptions.ui:47 msgid "Only hide the panel when it is obstructed by windows" msgstr "Приховувати панель лише якщо її перекривають вікна" -#: ui/BoxIntellihideOptions.ui:2 +#: ui/BoxIntellihideOptions.ui:57 msgid "The panel hides from" msgstr "Приховування панелі зумовлюють" -#: ui/BoxIntellihideOptions.ui:6 +#: ui/BoxIntellihideOptions.ui:76 msgid "Require pressure at the edge of the screen to reveal the panel" -msgstr "Для появи панелі потрібне притискання на краю екрану" +msgstr "Для появи панелі потрібне притискання на межі екрану" -#: ui/BoxIntellihideOptions.ui:7 +#: ui/BoxIntellihideOptions.ui:86 msgid "Required pressure threshold (px)" msgstr "Поріг притискання (пкс)" -#: ui/BoxIntellihideOptions.ui:8 +#: ui/BoxIntellihideOptions.ui:100 msgid "Required pressure timeout (ms)" msgstr "Затримка після притискання (мс)" -#: ui/BoxIntellihideOptions.ui:9 +#: ui/BoxIntellihideOptions.ui:118 msgid "Allow the panel to be revealed while in fullscreen mode" msgstr "Дозволити появу панелі в повноекранному режимі" -#: ui/BoxIntellihideOptions.ui:10 +#: ui/BoxIntellihideOptions.ui:128 +msgid "(requires multi-monitors option)" +msgstr "(необхідна присутність на кількох моніторах)" + +#: ui/BoxIntellihideOptions.ui:129 msgid "Only hide secondary panels" msgstr "Приховувати лише додаткові панелі" -#: ui/BoxIntellihideOptions.ui:11 -msgid "(requires multi-monitors option)" -msgstr "(потрібне налаштування для декількох моніторів)" - -#: ui/BoxIntellihideOptions.ui:12 -msgid "Keyboard shortcut to reveal and hold the panel" -msgstr "Клавіатурне скорочення для появи та утримання панелі" - -#: ui/BoxIntellihideOptions.ui:13 ui/BoxOverlayShortcut.ui:12 +#: ui/BoxIntellihideOptions.ui:139 ui/BoxOverlayShortcut.ui:65 msgid "Syntax: <Shift>, <Ctrl>, <Alt>, <Super>" msgstr "Синтаксис: <Shift>, <Ctrl>, <Alt>, <Super>" -#: ui/BoxIntellihideOptions.ui:14 +#: ui/BoxIntellihideOptions.ui:140 +msgid "Keyboard shortcut to reveal and hold the panel" +msgstr "Клавіатурне скорочення для появи та утримання панелі" + +#: ui/BoxIntellihideOptions.ui:143 msgid "e.g. i" msgstr "напр., i" -#: ui/BoxIntellihideOptions.ui:15 +#: ui/BoxIntellihideOptions.ui:152 +msgid "Persist state across restarts" +msgstr "Зберігати стан між перезапусками" + +#: ui/BoxIntellihideOptions.ui:162 +msgid "" +"(respects \"Do Not Disturb\" and requires show notification counter " +"badge option)" +msgstr "" +"(враховує режим «Не турбувати» та потребує «Показувати лічильник сповіщень»)" + +#: ui/BoxIntellihideOptions.ui:163 +msgid "Reveal and hold the panel on notification" +msgstr "Показувати та утримувати панель, коли є сповіщення" + +#: ui/BoxIntellihideOptions.ui:177 msgid "Hide and reveal animation duration (ms)" msgstr "Тривалість анімації приховування та появи (мс)" -#: ui/BoxIntellihideOptions.ui:16 +#: ui/BoxIntellihideOptions.ui:191 msgid "Delay before hiding the panel (ms)" -msgstr "Затримка перед приховання панелі (мс)" +msgstr "Затримка перед приховуванням панелі (мс)" -#: ui/BoxIntellihideOptions.ui:17 +#: ui/BoxIntellihideOptions.ui:206 msgid "Delay before enabling intellihide on start (ms)" -msgstr "Затримка перед активацією першого інтелектуального приховування (мс)" +msgstr "Затримка перед активацією розумного приховування під час запуску (мс)" -#: ui/BoxMiddleClickOptions.ui:1 +#: ui/BoxMiddleClickOptions.ui:19 msgid "Shift+Click action" -msgstr "Дія для Shift+Клік" +msgstr "Дія для Shift+клацання" -#: ui/BoxMiddleClickOptions.ui:2 +#: ui/BoxMiddleClickOptions.ui:20 msgid "" "When set to minimize, double clicking minimizes all the windows of the " "application." msgstr "" -"Якщо вибрано \"Згортання вікна\", подвійний клік згорне всі вікна програми." +"Якщо вибрано \"Згортання вікна\", то подвійне клацання буде згортати всі " +"вікна програми." -#: ui/BoxMiddleClickOptions.ui:3 ui/SettingsAction.ui:9 +#: ui/BoxMiddleClickOptions.ui:25 ui/BoxMiddleClickOptions.ui:54 +#: ui/BoxMiddleClickOptions.ui:83 ui/SettingsAction.ui:40 msgid "Raise windows" msgstr "Підняття вікон" -#: ui/BoxMiddleClickOptions.ui:4 +#: ui/BoxMiddleClickOptions.ui:26 ui/BoxMiddleClickOptions.ui:55 +#: ui/BoxMiddleClickOptions.ui:84 msgid "Minimize window" msgstr "Згортання вікна" -#: ui/BoxMiddleClickOptions.ui:5 ui/SettingsAction.ui:10 +#: ui/BoxMiddleClickOptions.ui:27 ui/BoxMiddleClickOptions.ui:56 +#: ui/BoxMiddleClickOptions.ui:85 ui/SettingsAction.ui:41 msgid "Launch new instance" msgstr "Запуск нового екземпляра" -#: ui/BoxMiddleClickOptions.ui:6 ui/SettingsAction.ui:5 +#: ui/BoxMiddleClickOptions.ui:28 ui/BoxMiddleClickOptions.ui:57 +#: ui/BoxMiddleClickOptions.ui:86 ui/SettingsAction.ui:35 msgid "Cycle through windows" msgstr "Циклічне перемикання вікон" -#: ui/BoxMiddleClickOptions.ui:7 ui/SettingsAction.ui:4 +#: ui/BoxMiddleClickOptions.ui:29 ui/BoxMiddleClickOptions.ui:58 +#: ui/BoxMiddleClickOptions.ui:87 ui/SettingsAction.ui:34 msgid "Cycle windows + minimize" msgstr "Циклічне перемикання + згортання вікон" -#: ui/BoxMiddleClickOptions.ui:8 ui/SettingsAction.ui:6 +#: ui/BoxMiddleClickOptions.ui:30 ui/BoxMiddleClickOptions.ui:59 +#: ui/BoxMiddleClickOptions.ui:88 ui/SettingsAction.ui:36 msgid "Toggle single / Preview multiple" -msgstr "Згортання одного / мініатюри декількох" +msgstr "Видимість одного / мініатюри кількох" -#: ui/BoxMiddleClickOptions.ui:9 ui/SettingsAction.ui:7 +#: ui/BoxMiddleClickOptions.ui:31 ui/BoxMiddleClickOptions.ui:60 +#: ui/BoxMiddleClickOptions.ui:89 ui/SettingsAction.ui:37 msgid "Toggle single / Cycle multiple" -msgstr "Згортання одного / перемикання декількох" +msgstr "Видимість одного / перемикання кількох" -#: ui/BoxMiddleClickOptions.ui:11 +#: ui/BoxMiddleClickOptions.ui:32 ui/BoxMiddleClickOptions.ui:61 +#: ui/BoxMiddleClickOptions.ui:90 ui/SettingsAction.ui:38 +msgid "Toggle single / Spread multiple" +msgstr "Видимість одного / показ кількох" + +#: ui/BoxMiddleClickOptions.ui:48 msgid "Middle-Click action" -msgstr "Дія при кліку середньою кнопкою" +msgstr "Дія під час клацання середньою" -#: ui/BoxMiddleClickOptions.ui:12 +#: ui/BoxMiddleClickOptions.ui:49 msgid "Behavior for Middle-Click." -msgstr "Поведінка при кліку середньою кнопкою" +msgstr "Поведінка під час клацання середньою." -#: ui/BoxMiddleClickOptions.ui:13 +#: ui/BoxMiddleClickOptions.ui:77 msgid "Shift+Middle-Click action" -msgstr "Дія при Shift+Клік середньою кнопкою" +msgstr "Дія під час Shift+клацання середньою" -#: ui/BoxMiddleClickOptions.ui:14 +#: ui/BoxMiddleClickOptions.ui:78 msgid "Behavior for Shift+Middle-Click." -msgstr "Поведінка при Shift+Клік середньою кнопкою" +msgstr "Поведінка під час Shift+клацання середньою." -#: ui/BoxOverlayShortcut.ui:1 -msgid "Hotkeys prefix" -msgstr "Префікс гарячих клавіш" - -#: ui/BoxOverlayShortcut.ui:2 +#: ui/BoxOverlayShortcut.ui:23 msgid "Hotkeys will either be Super+Number or Super+Alt+Num" msgstr "Гарячими клавішами будуть або Super+Число, або Super+Alt+Num" -#: ui/BoxOverlayShortcut.ui:3 +#: ui/BoxOverlayShortcut.ui:24 +msgid "Hotkeys prefix" +msgstr "Префікс гарячих клавіш" + +#: ui/BoxOverlayShortcut.ui:29 msgid "Super" msgstr "Super" -#: ui/BoxOverlayShortcut.ui:4 +#: ui/BoxOverlayShortcut.ui:30 msgid "Super + Alt" msgstr "Super + Alt" -#: ui/BoxOverlayShortcut.ui:5 -msgid "Number overlay" -msgstr "Накладання номерів" - -#: ui/BoxOverlayShortcut.ui:6 +#: ui/BoxOverlayShortcut.ui:38 msgid "" "Temporarily show the application numbers over the icons when using the " "hotkeys." -msgstr "Накладати на піктограми номери при використанні гарячих клавіш." +msgstr "" +"Тимчасово накладати на піктограми номери програм під час використання " +"гарячих клавіш." -#: ui/BoxOverlayShortcut.ui:7 +#: ui/BoxOverlayShortcut.ui:39 +msgid "Number overlay" +msgstr "Накладання номерів" + +#: ui/BoxOverlayShortcut.ui:44 msgid "Never" msgstr "Ніколи" -#: ui/BoxOverlayShortcut.ui:8 +#: ui/BoxOverlayShortcut.ui:45 msgid "Show temporarily" -msgstr "Тимчасовий показ" +msgstr "Тимчасово" -#: ui/BoxOverlayShortcut.ui:9 +#: ui/BoxOverlayShortcut.ui:46 msgid "Always visible" -msgstr "Постійний показ" +msgstr "Завжди" -#: ui/BoxOverlayShortcut.ui:10 +#: ui/BoxOverlayShortcut.ui:54 msgid "Hide timeout (ms)" -msgstr "Час зникнення (мс)" +msgstr "Затримка приховування (мс)" -#: ui/BoxOverlayShortcut.ui:11 +#: ui/BoxOverlayShortcut.ui:66 msgid "Shortcut to show the overlay for 2 seconds" -msgstr "Комбінація для 2-секундного показу накладання" +msgstr "Клавіатурне скорочення для активації накладання на 2 секунди" -#: ui/BoxOverlayShortcut.ui:13 +#: ui/BoxOverlayShortcut.ui:69 msgid "e.g. q" msgstr "напр., q" -#: ui/BoxOverlayShortcut.ui:14 -msgid "Show window previews on hotkey" -msgstr "Гаряча клавіша викликає показ мініатюр вікон" +#: ui/BoxOverlayShortcut.ui:78 +msgid "" +"On secondary monitors, show the overlay on icons matching the primary monitor" +msgstr "" +"На додаткових моніторах показувати те саме накладання на піктограми, що й на " +"основному моніторі" -#: ui/BoxOverlayShortcut.ui:15 +#: ui/BoxOverlayShortcut.ui:79 +msgid "Show the overlay on all monitors" +msgstr "Показувати накладання на всіх моніторах" + +#: ui/BoxOverlayShortcut.ui:89 msgid "Show previews when the application have multiple instances" -msgstr "Показувати мініатюри, коли запущено декілька екземплярів програми" +msgstr "Показувати мініатюри, коли запущено кілька екземплярів програми" -#: ui/BoxOverlayShortcut.ui:16 +#: ui/BoxOverlayShortcut.ui:90 +msgid "Show window previews on hotkey" +msgstr "Показувати мініатюри вікон після натискання гарячої клавіші" + +#: ui/BoxOverlayShortcut.ui:100 +msgid "Select which keyboard number keys are used to activate the hotkeys" +msgstr "" +"Оберіть, які цифри на клавіатурі використовувати для активації гарячих клавіш" + +#: ui/BoxOverlayShortcut.ui:101 msgid "Hotkeys are activated with" msgstr "Гарячі клавіші активуються" -#: ui/BoxOverlayShortcut.ui:17 -msgid "Select which keyboard number keys are used to activate the hotkeys" -msgstr "" -"Оберіть, які цифрові клавіші використовувати для активації гарячих клавіш" - -#: ui/BoxOverlayShortcut.ui:18 +#: ui/BoxOverlayShortcut.ui:106 msgid "Number row" msgstr "Цифровим рядом" -#: ui/BoxOverlayShortcut.ui:19 +#: ui/BoxOverlayShortcut.ui:107 msgid "Numeric keypad" msgstr "Цифровою клавіатурою" -#: ui/BoxOverlayShortcut.ui:20 +#: ui/BoxOverlayShortcut.ui:108 msgid "Both" msgstr "Обома" -#: ui/BoxScrollIconOptions.ui:1 ui/BoxScrollPanelOptions.ui:1 +#: ui/BoxScrollIconOptions.ui:25 ui/BoxScrollPanelOptions.ui:25 msgid "Delay between mouse scroll events (ms)" msgstr "Затримка між подіями гортання мишею (мс)" -#: ui/BoxScrollIconOptions.ui:2 ui/BoxScrollPanelOptions.ui:2 +#: ui/BoxScrollIconOptions.ui:26 ui/BoxScrollPanelOptions.ui:26 msgid "Use this value to limit the number of captured mouse scroll events." -msgstr "Це значення обмежує кількість захоплених подій гортання мишею" +msgstr "Це значення обмежує кількість захоплених подій гортання мишею." -#: ui/BoxScrollPanelOptions.ui:3 +#: ui/BoxScrollPanelOptions.ui:42 msgid "Show popup when changing workspace" -msgstr "Показувати сповіщення щодо зміни робочого простору" +msgstr "Показувати сповіщення про зміну робочого простору" -#: ui/BoxScrollPanelOptions.ui:4 +#: ui/BoxScrollPanelOptions.ui:43 msgid "This affects workspace popup when scrolling on the panel only." msgstr "" "Це впливає на сповіщення зміни робочого простору лише під час гортання над " -"панеллю" +"панеллю." -#: ui/BoxSecondaryMenuOptions.ui:1 +#: ui/BoxSecondaryMenuOptions.ui:19 msgid "Integrate AppMenu items" -msgstr "Вбудувати елементи Меню" +msgstr "Вбудувати пункти Меню програм" -#: ui/BoxSecondaryMenuOptions.ui:2 -msgid "Show Details menu item" +#: ui/BoxSecondaryMenuOptions.ui:30 +msgid "App Details menu item" msgstr "Пункт меню Подробиці щодо програми" -#: ui/BoxShowApplicationsOptions.ui:1 +#: ui/BoxSecondaryMenuOptions.ui:31 +msgid "App Details is only available when Gnome Software is installed" +msgstr "" +"Пункт Подробиці щодо програми видимий лише коли встановлено " +"«Програми» GNOME" + +#: ui/BoxShowApplicationsOptions.ui:25 msgid "Show Applications icon" msgstr "Піктограма Показати програми" -#: ui/BoxShowApplicationsOptions.ui:2 +#: ui/BoxShowApplicationsOptions.ui:60 msgid "Show Applications icon side padding (px)" -msgstr "Відступи піктограми «Показати програми» (пкс)" +msgstr "Бічний відступ піктограми «Показати програми» (пкс)" -#: ui/BoxShowApplicationsOptions.ui:4 +#: ui/BoxShowApplicationsOptions.ui:73 msgid "Override escape key and return to desktop" -msgstr "Перепризначення клавіші «Escape» та повернення до стільниці" +msgstr "Клавіша «Escape» повертає до стільниці" -#: ui/BoxShowDesktopOptions.ui:1 +#: ui/BoxShowDesktopOptions.ui:53 msgid "Override Show Desktop line color" -msgstr "Заміна кольору смужки «Показати Стільницю»" +msgstr "Замінити колір смужки «Показати Стільницю»" -#: ui/BoxShowDesktopOptions.ui:2 +#: ui/BoxShowDesktopOptions.ui:77 msgid "Reveal the desktop when hovering the Show Desktop button" -msgstr "Показувати стільницю при наведенні на кнопку «Показати Стільницю»" +msgstr "Показувати стільницю під час наведення на кнопку «Показати Стільницю»" -#: ui/BoxShowDesktopOptions.ui:3 +#: ui/BoxShowDesktopOptions.ui:90 msgid "Delay before revealing the desktop (ms)" -msgstr "Затримка перед показом стільниці" +msgstr "Затримка перед появою стільниці" -#: ui/BoxShowDesktopOptions.ui:4 +#: ui/BoxShowDesktopOptions.ui:106 msgid "Fade duration (ms)" msgstr "Час зникнення (мс)" -#: ui/BoxWindowPreviewOptions.ui:1 +#: ui/BoxWindowPreviewOptions.ui:89 msgid "Time (ms) before showing" -msgstr "Затримка (мс) перед показом (100 за замовчуванням)" +msgstr "Затримка (мс) перед появою" -#: ui/BoxWindowPreviewOptions.ui:2 +#: ui/BoxWindowPreviewOptions.ui:90 msgid "(400 is default)" msgstr "(типово - 400)" -#: ui/BoxWindowPreviewOptions.ui:3 +#: ui/BoxWindowPreviewOptions.ui:105 msgid "Time (ms) before hiding" -msgstr "Затримка (мс) перед показом (100 за замовчуванням)" +msgstr "Затримка (мс) перед приховуванням" -#: ui/BoxWindowPreviewOptions.ui:4 +#: ui/BoxWindowPreviewOptions.ui:106 msgid "(100 is default)" msgstr "(типово - 100)" -#: ui/BoxWindowPreviewOptions.ui:5 +#: ui/BoxWindowPreviewOptions.ui:117 msgid "Immediate on application icon click" -msgstr "Відразу після кліку на піктограму програми" +msgstr "Відразу після клацання на піктограмі програми" -#: ui/BoxWindowPreviewOptions.ui:6 +#: ui/BoxWindowPreviewOptions.ui:138 msgid "Animation time (ms)" msgstr "Час зникнення (мс)" -#: ui/BoxWindowPreviewOptions.ui:7 +#: ui/BoxWindowPreviewOptions.ui:159 msgid "Middle click on the preview to close the window" -msgstr "Клік середньою кнопкою на мініатюрі закриває вікно" +msgstr "Клацання середньою на мініатюрі закриває вікно" -#: ui/BoxWindowPreviewOptions.ui:8 +#: ui/BoxWindowPreviewOptions.ui:176 msgid "Window previews preferred size (px)" msgstr "Бажаний розмір мініатюр вікон (пкс)" -#: ui/BoxWindowPreviewOptions.ui:9 +#: ui/BoxWindowPreviewOptions.ui:192 msgid "Window previews aspect ratio X (width)" msgstr "Співвідношення сторін мініатюр вікон по осі X (ширина)" -#: ui/BoxWindowPreviewOptions.ui:10 +#: ui/BoxWindowPreviewOptions.ui:197 ui/BoxWindowPreviewOptions.ui:238 msgid "1" msgstr "1" -#: ui/BoxWindowPreviewOptions.ui:11 +#: ui/BoxWindowPreviewOptions.ui:198 ui/BoxWindowPreviewOptions.ui:239 msgid "2" msgstr "2" -#: ui/BoxWindowPreviewOptions.ui:12 +#: ui/BoxWindowPreviewOptions.ui:199 ui/BoxWindowPreviewOptions.ui:240 msgid "3" msgstr "3" -#: ui/BoxWindowPreviewOptions.ui:13 +#: ui/BoxWindowPreviewOptions.ui:200 ui/BoxWindowPreviewOptions.ui:241 msgid "4" msgstr "4" -#: ui/BoxWindowPreviewOptions.ui:14 +#: ui/BoxWindowPreviewOptions.ui:201 ui/BoxWindowPreviewOptions.ui:242 +#: ui/BoxWindowPreviewOptions.ui:306 msgid "5" msgstr "5" -#: ui/BoxWindowPreviewOptions.ui:15 +#: ui/BoxWindowPreviewOptions.ui:202 ui/BoxWindowPreviewOptions.ui:243 msgid "6" msgstr "6" -#: ui/BoxWindowPreviewOptions.ui:16 +#: ui/BoxWindowPreviewOptions.ui:203 ui/BoxWindowPreviewOptions.ui:244 msgid "7" msgstr "7" -#: ui/BoxWindowPreviewOptions.ui:17 +#: ui/BoxWindowPreviewOptions.ui:204 ui/BoxWindowPreviewOptions.ui:245 msgid "8" msgstr "8" -#: ui/BoxWindowPreviewOptions.ui:18 +#: ui/BoxWindowPreviewOptions.ui:205 ui/BoxWindowPreviewOptions.ui:246 msgid "9" msgstr "9" -#: ui/BoxWindowPreviewOptions.ui:19 +#: ui/BoxWindowPreviewOptions.ui:206 ui/BoxWindowPreviewOptions.ui:247 msgid "10" msgstr "10" -#: ui/BoxWindowPreviewOptions.ui:20 +#: ui/BoxWindowPreviewOptions.ui:207 ui/BoxWindowPreviewOptions.ui:248 msgid "11" msgstr "11" -#: ui/BoxWindowPreviewOptions.ui:21 +#: ui/BoxWindowPreviewOptions.ui:208 ui/BoxWindowPreviewOptions.ui:249 msgid "12" msgstr "12" -#: ui/BoxWindowPreviewOptions.ui:22 +#: ui/BoxWindowPreviewOptions.ui:209 ui/BoxWindowPreviewOptions.ui:250 msgid "13" msgstr "13" -#: ui/BoxWindowPreviewOptions.ui:23 +#: ui/BoxWindowPreviewOptions.ui:210 ui/BoxWindowPreviewOptions.ui:251 msgid "14" msgstr "14" -#: ui/BoxWindowPreviewOptions.ui:24 +#: ui/BoxWindowPreviewOptions.ui:211 ui/BoxWindowPreviewOptions.ui:252 msgid "15" msgstr "15" -#: ui/BoxWindowPreviewOptions.ui:25 +#: ui/BoxWindowPreviewOptions.ui:212 ui/BoxWindowPreviewOptions.ui:253 msgid "16" msgstr "16" -#: ui/BoxWindowPreviewOptions.ui:26 +#: ui/BoxWindowPreviewOptions.ui:213 ui/BoxWindowPreviewOptions.ui:254 msgid "17" msgstr "17" -#: ui/BoxWindowPreviewOptions.ui:27 +#: ui/BoxWindowPreviewOptions.ui:214 ui/BoxWindowPreviewOptions.ui:255 msgid "18" msgstr "18" -#: ui/BoxWindowPreviewOptions.ui:28 +#: ui/BoxWindowPreviewOptions.ui:215 ui/BoxWindowPreviewOptions.ui:256 msgid "19" msgstr "19" -#: ui/BoxWindowPreviewOptions.ui:29 +#: ui/BoxWindowPreviewOptions.ui:216 ui/BoxWindowPreviewOptions.ui:257 msgid "20" msgstr "20" -#: ui/BoxWindowPreviewOptions.ui:30 +#: ui/BoxWindowPreviewOptions.ui:217 ui/BoxWindowPreviewOptions.ui:258 msgid "21" msgstr "21" -#: ui/BoxWindowPreviewOptions.ui:31 +#: ui/BoxWindowPreviewOptions.ui:223 ui/BoxWindowPreviewOptions.ui:264 msgid "Fixed" -msgstr "Фіксоване" +msgstr "Стале" -#: ui/BoxWindowPreviewOptions.ui:32 +#: ui/BoxWindowPreviewOptions.ui:233 msgid "Window previews aspect ratio Y (height)" msgstr "Співвідношення сторін мініатюр вікон по осі У (висота)" -#: ui/BoxWindowPreviewOptions.ui:33 +#: ui/BoxWindowPreviewOptions.ui:274 msgid "Window previews padding (px)" -msgstr "Відступи мініатюр вікон (пкс)" +msgstr "Відступи між мініатюрами вікон (пкс)" -#: ui/BoxWindowPreviewOptions.ui:34 +#: ui/BoxWindowPreviewOptions.ui:296 msgid "Use custom opacity for the previews background" -msgstr "Використати інше значення непрозорості для фону мініатюр" +msgstr "Використати власне значення непрозорості для тла мініатюр" -#: ui/BoxWindowPreviewOptions.ui:35 +#: ui/BoxWindowPreviewOptions.ui:297 msgid "" "If disabled, the previews background have the same opacity as the panel." msgstr "" -"Якщо вимкнено, то фон мініатюр використає те саме значення непрозорості, що " -"й панель." +"Якщо вимкнено, то тло мініатюр буде використовувати те саме значення " +"непрозорості, що й панель." -#: ui/BoxWindowPreviewOptions.ui:36 +#: ui/BoxWindowPreviewOptions.ui:322 msgid "Close button and header position" -msgstr "Позиція кнопки закриття та заголовку" +msgstr "Розміщення заголовку та кнопки закриття" -#: ui/BoxWindowPreviewOptions.ui:39 +#: ui/BoxWindowPreviewOptions.ui:352 msgid "Display window preview headers" msgstr "Показувати заголовки до мініатюр вікон" -#: ui/BoxWindowPreviewOptions.ui:40 +#: ui/BoxWindowPreviewOptions.ui:363 msgid "Icon size (px) of the window preview" msgstr "Розмір піктограми (пкс) для мініатюри вікна" -#: ui/BoxWindowPreviewOptions.ui:41 +#: ui/BoxWindowPreviewOptions.ui:364 msgid "If disabled, the previews icon size will be based on headerbar size" msgstr "" "Якщо вимкнено, то розмір піктограм в мініатюрах буде залежати від розміру " "заголовка" -#: ui/BoxWindowPreviewOptions.ui:42 +#: ui/BoxWindowPreviewOptions.ui:385 msgid "Font size (px) of the preview titles" -msgstr "Розмір шрифту (пкс) " +msgstr "Розмір шрифту (пкс) в заголовках до мініатюр" -#: ui/BoxWindowPreviewOptions.ui:43 +#: ui/BoxWindowPreviewOptions.ui:401 msgid "Font weight of the preview titles" msgstr "Товщина шрифту в заголовках до мініатюр" -#: ui/BoxWindowPreviewOptions.ui:49 +#: ui/BoxWindowPreviewOptions.ui:419 msgid "Font color of the preview titles" msgstr "Колір шрифту в заголовках до мініатюр" -#: ui/BoxWindowPreviewOptions.ui:50 +#: ui/BoxWindowPreviewOptions.ui:437 msgid "Enable window peeking" -msgstr "Активувати показ вікон" +msgstr "Активувати перегляд вікон" -#: ui/BoxWindowPreviewOptions.ui:51 +#: ui/BoxWindowPreviewOptions.ui:438 msgid "" "When hovering over a window preview for some time, the window gets " "distinguished." msgstr "" -"Показ певного вікна через деякий час після утримання курсора над його " -"мініатюрою." +"Після утримання курсора над вікном мініатюри деякий час, вікно буде " +"виокремлено." -#: ui/BoxWindowPreviewOptions.ui:52 +#: ui/BoxWindowPreviewOptions.ui:449 msgid "Enter window peeking mode timeout (ms)" -msgstr "Затримка перед активацією режиму показу (мс)" +msgstr "Затримка перед активацією режиму перегляду вікон (мс)" -#: ui/BoxWindowPreviewOptions.ui:53 +#: ui/BoxWindowPreviewOptions.ui:450 msgid "" "Time of inactivity while hovering over a window preview needed to enter the " "window peeking mode." msgstr "" -"Час бездіяльності після наведення курсора на мініатюру для активації режиму " -"показу" +"Час бездіяльності після наведення курсора на мініатюру перед активацією " +"режиму перегляду вікон." -#: ui/BoxWindowPreviewOptions.ui:54 +#: ui/BoxWindowPreviewOptions.ui:455 msgid "50" msgstr "50" -#: ui/BoxWindowPreviewOptions.ui:55 +#: ui/BoxWindowPreviewOptions.ui:466 msgid "Window peeking mode opacity" -msgstr "Непрозорість в режимі показу вікон" +msgstr "Непрозорість в режимі перегляду вікон" -#: ui/BoxWindowPreviewOptions.ui:56 +#: ui/BoxWindowPreviewOptions.ui:467 msgid "" "All windows except for the peeked one have their opacity set to the same " "value." -msgstr "Всі вікна, окрім того, що " +msgstr "" +"Всі вікна, окрім того, що показується, мають однакове значення непрозорості." -#: ui/SettingsAbout.ui:1 +#: ui/SettingsAbout.ui:8 msgid "About" -msgstr "Інформація" +msgstr "Деталі" -#: ui/SettingsAbout.ui:2 +#: ui/SettingsAbout.ui:11 msgid "Info" msgstr "Інформація" -#: ui/SettingsAbout.ui:3 +#: ui/SettingsAbout.ui:14 msgid "Version" msgstr "Версія" -#: ui/SettingsAbout.ui:4 +#: ui/SettingsAbout.ui:22 msgid "Source" msgstr "Вихідний код" -#: ui/SettingsAbout.ui:5 +#: ui/SettingsAbout.ui:26 msgid "GitHub" msgstr "GitHub" -#: ui/SettingsAbout.ui:6 +#: ui/SettingsAbout.ui:37 msgid "Export and Import" msgstr "Експорт та імпорт" -#: ui/SettingsAbout.ui:7 -msgid "Export and import settings" -msgstr "Експорт та імпорт налаштувань" - -#: ui/SettingsAbout.ui:8 +#: ui/SettingsAbout.ui:40 msgid "" "Use the buttons below to create a settings file from your current " "preferences that can be imported on a different machine." @@ -1016,15 +1095,19 @@ msgstr "" "Скористайтеся кнопками нижче, щоб створити файл налаштувань з вашими " "поточними вподобаннями, який можна буде імпортувати на іншій машині." -#: ui/SettingsAbout.ui:9 +#: ui/SettingsAbout.ui:41 +msgid "Export and import settings" +msgstr "Експорт та імпорт налаштувань" + +#: ui/SettingsAbout.ui:56 msgid "Export to file" msgstr "Експорт до файлу" -#: ui/SettingsAbout.ui:10 +#: ui/SettingsAbout.ui:62 msgid "Import from file" msgstr "Імпорт з файлу" -#: ui/SettingsAbout.ui:11 +#: ui/SettingsAbout.ui:77 msgid "" "This program comes with ABSOLUTELY NO WARRANTY.\n" "See the GNU General Public License, version 2 or later, щоб дізнатися " "більше." -#: ui/SettingsAction.ui:1 +#: ui/SettingsAction.ui:8 msgid "Action" msgstr "Дії" -#: ui/SettingsAction.ui:2 +#: ui/SettingsAction.ui:11 ui/SettingsAction.ui:15 msgid "Click action" -msgstr "Дія при кліку" +msgstr "Дія під час клацання" -#: ui/SettingsAction.ui:3 +#: ui/SettingsAction.ui:14 msgid "Behaviour when clicking on the icon of a running application." -msgstr "Поведінка при кліку на піктограмі запущеної програми" +msgstr "Поведінка під час клацання на піктограмі запущеної програми." -#: ui/SettingsAction.ui:8 +#: ui/SettingsAction.ui:39 msgid "Toggle windows" msgstr "Згортання вікон" -#: ui/SettingsAction.ui:11 +#: ui/SettingsAction.ui:51 msgid "Scroll action" -msgstr "Дія при гортанні" +msgstr "Дія під час гортання" -#: ui/SettingsAction.ui:12 -msgid "Scroll panel action" -msgstr "Дія при гортанні над панеллю" - -#: ui/SettingsAction.ui:13 +#: ui/SettingsAction.ui:54 msgid "Behavior when mouse scrolling over the panel." -msgstr "Поведінка при гортанні над панеллю" +msgstr "Поведінка під час гортанні над панеллю." -#: ui/SettingsAction.ui:14 +#: ui/SettingsAction.ui:55 +msgid "Scroll panel action" +msgstr "Дія під час гортання над панеллю" + +#: ui/SettingsAction.ui:79 ui/SettingsAction.ui:112 msgid "Do nothing" msgstr "Нічого не робити" -#: ui/SettingsAction.ui:15 +#: ui/SettingsAction.ui:80 msgid "Switch workspace" msgstr "Перемкнути робочий простір" -#: ui/SettingsAction.ui:16 +#: ui/SettingsAction.ui:81 ui/SettingsAction.ui:113 msgid "Cycle windows" msgstr "Циклічне перемикання вікон" -#: ui/SettingsAction.ui:17 +#: ui/SettingsAction.ui:82 msgid "Change volume" msgstr "Зміна гучності" -#: ui/SettingsAction.ui:18 -msgid "Scroll icon action" -msgstr "Дія при гортанні над піктограмою" - -#: ui/SettingsAction.ui:19 +#: ui/SettingsAction.ui:90 msgid "Behavior when mouse scrolling over an application icon." -msgstr "Поведінка під час гортання над піктограмою програми" +msgstr "Поведінка під час гортання над піктограмою програми." -#: ui/SettingsAction.ui:20 +#: ui/SettingsAction.ui:91 +msgid "Scroll icon action" +msgstr "Дія під час гортання над піктограмою" + +#: ui/SettingsAction.ui:114 msgid "Same as panel" msgstr "Та сама, що і для панелі" -#: ui/SettingsAction.ui:21 +#: ui/SettingsAction.ui:124 msgid "Hotkey overlay" -msgstr "Накладання для гарячих клавіш" +msgstr "Накладання гарячих клавіш" -#: ui/SettingsAction.ui:22 -msgid "Use hotkeys to activate apps" -msgstr "Використовувати гарячі клавіші для активації програм" - -#: ui/SettingsAction.ui:23 +#: ui/SettingsAction.ui:127 msgid "" "Enable Super+(0-9) as shortcuts to activate apps. It can also be used " "together with Shift and Ctrl." @@ -1107,343 +1186,397 @@ msgstr "" "Використовувати клавіші Super+(0-9) для активації програм. Також можна " "комбінувати з клавішами Shift та Ctrl." -#: ui/SettingsBehavior.ui:1 +#: ui/SettingsAction.ui:128 +msgid "Use hotkeys to activate apps" +msgstr "Використовувати гарячі клавіші для активації програм" + +#: ui/SettingsAction.ui:154 +msgid "Application icons context menu" +msgstr "Контекстне меню піктограм програм" + +#: ui/SettingsAction.ui:157 +msgid "(right-click menu)" +msgstr "(меню правої клавіші)" + +#: ui/SettingsAction.ui:158 +msgid "Secondary menu" +msgstr "Контекстне меню" + +#: ui/SettingsAction.ui:179 +msgid "Panel context menu entries" +msgstr "Пункти контекстного меню панелі" + +#: ui/SettingsAction.ui:190 +msgid "Add entry" +msgstr "Додати пункт" + +#: ui/SettingsBehavior.ui:5 msgid "Behavior" msgstr "Поведінка" -#: ui/SettingsBehavior.ui:2 +#: ui/SettingsBehavior.ui:11 msgid "Applications" msgstr "Програми" -#: ui/SettingsBehavior.ui:3 +#: ui/SettingsBehavior.ui:15 msgid "Show favorite applications" msgstr "Показувати улюблені програми" -#: ui/SettingsBehavior.ui:4 +#: ui/SettingsBehavior.ui:26 msgid "Show favorite applications on secondary panels" msgstr "Показувати улюблені програми на додаткових панелях" -#: ui/SettingsBehavior.ui:5 +#: ui/SettingsBehavior.ui:37 msgid "Show running applications" msgstr "Показувати запущені програми" -#: ui/SettingsBehavior.ui:6 +#: ui/SettingsBehavior.ui:48 msgid "Ungroup applications" msgstr "Розгрупувати програми" -#: ui/SettingsBehavior.ui:7 +#: ui/SettingsBehavior.ui:73 msgid "Show notification counter badge" msgstr "Показувати лічильник сповіщень" -#: ui/SettingsBehavior.ui:8 +#: ui/SettingsBehavior.ui:90 msgid "Hover" msgstr "Наведення" -#: ui/SettingsBehavior.ui:9 +#: ui/SettingsBehavior.ui:94 msgid "Show window previews on hover" -msgstr "Показувати мініатюри вікон при наведенні" +msgstr "Показувати мініатюри вікон під час наведення" -#: ui/SettingsBehavior.ui:10 +#: ui/SettingsBehavior.ui:119 msgid "Show tooltip on hover" -msgstr "Показувати підказки при наведенні" +msgstr "Показувати підказки під час наведення" -#: ui/SettingsBehavior.ui:11 +#: ui/SettingsBehavior.ui:134 msgid "Isolate" msgstr "Ізоляція" -#: ui/SettingsBehavior.ui:12 +#: ui/SettingsBehavior.ui:138 msgid "Isolate Workspaces" msgstr "Ізолювати робочі простори" -#: ui/SettingsBehavior.ui:13 +#: ui/SettingsBehavior.ui:149 msgid "Isolate monitors" msgstr "Ізолювати монітори" -#: ui/SettingsBehavior.ui:14 +#: ui/SettingsBehavior.ui:164 msgid "Overview" msgstr "Огляд" -#: ui/SettingsBehavior.ui:15 +#: ui/SettingsBehavior.ui:168 msgid "Click empty space to close overview" -msgstr "Клік на пустому місці закриває «Огляд»" +msgstr "Клацання на пустому місці закриває «Огляд»" -#: ui/SettingsBehavior.ui:16 +#: ui/SettingsBehavior.ui:179 msgid "Disable show overview on startup" msgstr "Не показувати «Огляд» під час запуску" -#: ui/SettingsFineTune.ui:1 +#: ui/SettingsFineTune.ui:38 msgid "Fine-Tune" msgstr "Доналаштування" -#: ui/SettingsFineTune.ui:2 +#: ui/SettingsFineTune.ui:41 msgid "Font size" msgstr "Розмір шрифту" -#: ui/SettingsFineTune.ui:3 -msgid "Tray Font Size" -msgstr "Розмір шрифту лотка" - -#: ui/SettingsFineTune.ui:4 +#: ui/SettingsFineTune.ui:44 ui/SettingsFineTune.ui:60 msgid "(0 = theme default)" msgstr "(0 = як в темі)" -#: ui/SettingsFineTune.ui:5 -msgid "LeftBox Font Size" -msgstr "Розмір шрифту лівого блоку" +#: ui/SettingsFineTune.ui:45 +msgid "Tray Font Size" +msgstr "Розмір шрифту лотка" -#: ui/SettingsFineTune.ui:6 +#: ui/SettingsFineTune.ui:61 +msgid "LeftBox Font Size" +msgstr "Розмір шрифту лівого блока" + +#: ui/SettingsFineTune.ui:78 msgid "Padding" msgstr "Внутрішні відступи" -#: ui/SettingsFineTune.ui:7 -msgid "Tray Item Padding" -msgstr "Відступи між елементами лотка" - -#: ui/SettingsFineTune.ui:8 +#: ui/SettingsFineTune.ui:81 ui/SettingsFineTune.ui:97 +#: ui/SettingsFineTune.ui:113 msgid "(-1 = theme default)" msgstr "(-1 = як в темі)" -#: ui/SettingsFineTune.ui:9 +#: ui/SettingsFineTune.ui:82 +msgid "Tray Item Padding" +msgstr "Відступи між елементами лотка" + +#: ui/SettingsFineTune.ui:98 msgid "Status Icon Padding" msgstr "Відступи між піктограмами в рядку стану" -#: ui/SettingsFineTune.ui:10 +#: ui/SettingsFineTune.ui:114 msgid "LeftBox Padding" -msgstr "Відступи лівого блоку" +msgstr "Відступи лівого блока" -#: ui/SettingsFineTune.ui:11 +#: ui/SettingsFineTune.ui:131 msgid "Animate" msgstr "Анімації" -#: ui/SettingsFineTune.ui:12 +#: ui/SettingsFineTune.ui:134 msgid "Animate switching applications" -msgstr "Анімувати перемикання між програмами" +msgstr "Анімація перемикання між програмами" -#: ui/SettingsFineTune.ui:13 +#: ui/SettingsFineTune.ui:144 msgid "Animate launching new windows" -msgstr "Анімувати запуск нових вікон" +msgstr "Анімація запуску нових вікон" -#: ui/SettingsFineTune.ui:14 +#: ui/SettingsFineTune.ui:156 msgid "Gnome functionality" msgstr "Функціонал GNOME" -#: ui/SettingsFineTune.ui:15 -msgid "Keep original gnome-shell dash" -msgstr "Зберегти оригінальну панель програм gnome-shell" - -#: ui/SettingsFineTune.ui:16 +#: ui/SettingsFineTune.ui:159 msgid "(overview)" msgstr "(огляд)" -#: ui/SettingsFineTune.ui:17 +#: ui/SettingsFineTune.ui:160 +msgid "Keep original gnome-shell dash" +msgstr "Зберегти оригінальну панель програм оболонки GNOME" + +#: ui/SettingsFineTune.ui:170 msgid "Keep original gnome-shell top panel" -msgstr "Зберегти оригінальну верхню панель gnome-shell" +msgstr "Зберегти оригінальну верхню панель оболонки GNOME" -#: ui/SettingsFineTune.ui:18 -msgid "Activate panel menu buttons on click only" -msgstr "Кнопки меню на панелі активуються лише при кліку" - -#: ui/SettingsFineTune.ui:19 +#: ui/SettingsFineTune.ui:180 msgid "(e.g. date menu)" msgstr "(напр., меню дати)" -#: ui/SettingsFineTune.ui:20 +#: ui/SettingsFineTune.ui:181 +msgid "Activate panel menu buttons on click only" +msgstr "Активувати кнопки меню панелі лише під час клацання" + +#: ui/SettingsFineTune.ui:191 msgid "Force Activities hot corner on primary monitor" msgstr "Примусово розмістити гарячий кут «Діяльності» на основному моніторі" -#: ui/SettingsFineTune.ui:21 -msgid "App icon secondary menu" -msgstr "Контекстне меню піктограм програм" - -#: ui/SettingsFineTune.ui:22 -msgid "(right-click menu)" -msgstr "(меню правої кнопки)" - -#: ui/SettingsPosition.ui:1 +#: ui/SettingsPosition.ui:19 msgid "Position" msgstr "Розміщення" -#: ui/SettingsPosition.ui:2 +#: ui/SettingsPosition.ui:22 msgid "Panel" msgstr "Панель" -#: ui/SettingsPosition.ui:3 +#: ui/SettingsPosition.ui:25 msgid "Display the main panel on" msgstr "Головна панель присутня на" -#: ui/SettingsPosition.ui:4 +#: ui/SettingsPosition.ui:35 msgid "Display panels on all monitors" -msgstr "Панель присутня на всіх моніторах" +msgstr "Показувати панелі на всіх моніторах" -#: ui/SettingsPosition.ui:5 -msgid "Panel Intellihide" -msgstr "Інтелектуальне приховування панелі" - -#: ui/SettingsPosition.ui:6 +#: ui/SettingsPosition.ui:49 msgid "Hide and reveal the panel according to preferences" -msgstr "Приховувати та виводити панель залежно від налаштувань" +msgstr "Приховувати та показувати панель залежно від налаштувань" -#: ui/SettingsPosition.ui:7 +#: ui/SettingsPosition.ui:50 +msgid "Panel Intellihide" +msgstr "Розумне приховування панелі" + +#: ui/SettingsPosition.ui:76 msgid "Order and Position on monitors" msgstr "Порядок та розміщення на моніторах" -#: ui/SettingsPosition.ui:8 +#: ui/SettingsPosition.ui:79 msgid "Monitor" msgstr "Монітор" -#: ui/SettingsPosition.ui:9 +#: ui/SettingsPosition.ui:90 msgid "Apply changes to all monitors" msgstr "Застосовувати зміни на всіх моніторах" -#: ui/SettingsPosition.ui:10 +#: ui/SettingsPosition.ui:111 msgid "Panel screen position" msgstr "Розміщення панелі на екрані" -#: ui/SettingsPosition.ui:15 -msgid "Panel thickness" -msgstr "Товщина панелі" - -#: ui/SettingsPosition.ui:16 +#: ui/SettingsPosition.ui:152 msgid "(default is 48)" msgstr "(типово - 48)" -#: ui/SettingsPosition.ui:18 -#, no-c-format -msgid "Panel length (%)" -msgstr "Довжина панелі (%)" +#: ui/SettingsPosition.ui:153 +msgid "Panel thickness" +msgstr "Товщина панелі" -#: ui/SettingsPosition.ui:19 +#: ui/SettingsPosition.ui:168 msgid "(default is 100)" msgstr "(типово - 100)" -#: ui/SettingsPosition.ui:20 +#: ui/SettingsPosition.ui:169 +msgid "Panel length" +msgstr "Довжина панелі" + +#: ui/SettingsPosition.ui:174 +msgid "Dynamic" +msgstr "Динамічна" + +#: ui/SettingsPosition.ui:192 msgid "Anchor" msgstr "Вирівнювання" -#: ui/SettingsPosition.ui:24 +#: ui/SettingsPosition.ui:211 msgid "Taskbar Display" msgstr "Показ панелі завдань" -#: ui/SettingsStyle.ui:1 +#: ui/SettingsStyle.ui:59 msgid "Style" msgstr "Стиль" -#: ui/SettingsStyle.ui:2 +#: ui/SettingsStyle.ui:62 +msgid "Global style" +msgstr "Глобальний стиль" + +#: ui/SettingsStyle.ui:65 +msgid "Border radius" +msgstr "Радіус межі" + +#: ui/SettingsStyle.ui:82 msgid "AppIcon style" msgstr "Стиль піктограм програм" -#: ui/SettingsStyle.ui:3 -msgid "App Icon Margin" -msgstr "Зовнішні відступи піктограм" - -#: ui/SettingsStyle.ui:4 +#: ui/SettingsStyle.ui:85 msgid "(default is 8)" msgstr "(типово - 8)" -#: ui/SettingsStyle.ui:5 -msgid "App Icon Padding" -msgstr "Внутрішні відступи піктограм" +#: ui/SettingsStyle.ui:86 +msgid "App Icon Margin" +msgstr "Зовнішні відступи піктограм програм" -#: ui/SettingsStyle.ui:6 +#: ui/SettingsStyle.ui:101 msgid "(default is 4)" msgstr "(типово - 4)" -#: ui/SettingsStyle.ui:7 -msgid "Animate hovering app icons" -msgstr "Анімувати перемикання між програмами" +#: ui/SettingsStyle.ui:102 +msgid "App Icon Padding" +msgstr "Відступи між піктограмами програм" -#: ui/SettingsStyle.ui:8 +#: ui/SettingsStyle.ui:117 +msgid "Animate hovering app icons" +msgstr "Анімація під час наведення на піктограми програмами" + +#: ui/SettingsStyle.ui:141 +msgid "Highlight hovering app icons" +msgstr "Підсвічування піктограм програм під час наведення" + +#: ui/SettingsStyle.ui:165 msgid "Icon style" msgstr "Стиль піктограм" -#: ui/SettingsStyle.ui:9 +#: ui/SettingsStyle.ui:170 msgid "Normal" msgstr "Звичайний" -#: ui/SettingsStyle.ui:10 +#: ui/SettingsStyle.ui:171 msgid "Symbolic" msgstr "Символьний" -#: ui/SettingsStyle.ui:11 -msgid "Running indicator" -msgstr "Індикатора роботи" +#: ui/SettingsStyle.ui:172 +msgid "Grayscale" +msgstr "Відтінки сірого" -#: ui/SettingsStyle.ui:12 +#: ui/SettingsStyle.ui:182 +msgid "Running indicator" +msgstr "Індикатор роботи" + +#: ui/SettingsStyle.ui:185 msgid "Running indicator position" msgstr "Розміщення індикатора роботи" -#: ui/SettingsStyle.ui:17 +#: ui/SettingsStyle.ui:221 msgid "Running indicator style (Focused app)" -msgstr "Стиль індикатора роботи (програма у фокусі)" +msgstr "Стиль індикатора роботи (для програми у фокусі)" -#: ui/SettingsStyle.ui:18 +#: ui/SettingsStyle.ui:239 ui/SettingsStyle.ui:258 msgid "Dots" msgstr "Крапки" -#: ui/SettingsStyle.ui:19 +#: ui/SettingsStyle.ui:240 ui/SettingsStyle.ui:259 msgid "Squares" msgstr "Квадрати" -#: ui/SettingsStyle.ui:20 +#: ui/SettingsStyle.ui:241 ui/SettingsStyle.ui:260 msgid "Dashes" msgstr "Рисочки" -#: ui/SettingsStyle.ui:21 +#: ui/SettingsStyle.ui:242 ui/SettingsStyle.ui:261 msgid "Segmented" msgstr "Сегментований" -#: ui/SettingsStyle.ui:22 +#: ui/SettingsStyle.ui:243 ui/SettingsStyle.ui:262 msgid "Solid" msgstr "Суцільний" -#: ui/SettingsStyle.ui:23 +#: ui/SettingsStyle.ui:244 ui/SettingsStyle.ui:263 msgid "Ciliora" -msgstr "Циліора" +msgstr "Вії" -#: ui/SettingsStyle.ui:24 +#: ui/SettingsStyle.ui:245 ui/SettingsStyle.ui:264 msgid "Metro" msgstr "Метро" -#: ui/SettingsStyle.ui:25 +#: ui/SettingsStyle.ui:253 msgid "Running indicator style (Unfocused apps)" -msgstr "Стиль індикатора роботи (програма не у фокусі)" +msgstr "Стиль індикатора роботи (для програми не у фокусі)" -#: ui/SettingsStyle.ui:26 +#: ui/SettingsStyle.ui:274 msgid "Panel style" msgstr "Стиль панелі" -#: ui/SettingsStyle.ui:27 +#: ui/SettingsStyle.ui:277 ui/SettingsStyle.ui:293 ui/SettingsStyle.ui:309 +#: ui/SettingsStyle.ui:325 +msgid "(default is 0)" +msgstr "(типово - 0)" + +#: ui/SettingsStyle.ui:278 +msgid "Side margins" +msgstr "Бічні зовнішні відступи" + +#: ui/SettingsStyle.ui:294 +msgid "Top and bottom margins" +msgstr "Верхній та нижній зовнішні відступи" + +#: ui/SettingsStyle.ui:310 +msgid "Side padding" +msgstr "Бічні внутрішні відступи" + +#: ui/SettingsStyle.ui:326 +msgid "Top and bottom padding" +msgstr "Верхній та нижній внутрішні відступи" + +#: ui/SettingsStyle.ui:345 msgid "Override panel theme background color" msgstr "Зміна кольору тла панелі (типово - як в темі)" -#: ui/SettingsStyle.ui:28 +#: ui/SettingsStyle.ui:360 msgid "Override panel theme background opacity" msgstr "Зміна непрозорості тла панелі (типово - як в темі)" -#: ui/SettingsStyle.ui:30 -#, no-c-format +#: ui/SettingsStyle.ui:370 msgid "Panel background opacity (%)" msgstr "Непрозорість тла панелі (%)" -#: ui/SettingsStyle.ui:32 -msgid "Dynamic background opacity" -msgstr "Динамічна непрозорість тла" - -#: ui/SettingsStyle.ui:33 +#: ui/SettingsStyle.ui:382 msgid "Change opacity when a window gets close to the panel" msgstr "Зміна непрозорості, коли вікно наближається до панелі" -#: ui/SettingsStyle.ui:34 -msgid "Override panel theme gradient" -msgstr "Зміна градієнта панелі (типово - як в темі)" +#: ui/SettingsStyle.ui:383 +msgid "Dynamic background opacity" +msgstr "Динамічна непрозорість тла" -#: ui/SettingsStyle.ui:36 -#, no-c-format +#: ui/SettingsStyle.ui:411 +msgid "Override panel theme gradient" +msgstr "Зміна градієнта панелі" + +#: ui/SettingsStyle.ui:421 msgid "Gradient top color and opacity (%)" msgstr "Колір та непрозорість градієнта вгорі (%)" -#: ui/SettingsStyle.ui:38 -#, no-c-format +#: ui/SettingsStyle.ui:439 msgid "Gradient bottom color and opacity (%)" msgstr "Колір та непрозорість градієнта знизу (%)" @@ -1452,6 +1585,3 @@ msgstr "Показати значок робочого столу" msgid "Show Desktop button padding (px)" msgstr "Відступ Показати кнопку робочого столу (px)" - -msgid "Floating rounded theme" -msgstr "Плаваюча округла тема" diff --git a/po/vi.po b/po/vi.po index c68a5e6..cee7b7f 100644 --- a/po/vi.po +++ b/po/vi.po @@ -21,6 +21,3 @@ msgstr "Hiển thị biểu tượng Desktop" msgid "Show Desktop button padding (px)" msgstr "Phần đệm Show Desktop button (px)" - -msgid "Floating rounded theme" -msgstr "Theme có dạng bo tròn nỗi" diff --git a/po/zh_CN.po b/po/zh_CN.po index 367a73b..d0fef77 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -1441,9 +1441,6 @@ msgstr "显示桌面图标" msgid "Show Desktop button padding (px)" msgstr "“显示桌面按钮”的内边距(px)" -msgid "Floating rounded theme" -msgstr "悬浮圆角主题" - #, javascript-format #~ msgid "%d ." #~ msgstr "%d ." diff --git a/po/zh_TW.po b/po/zh_TW.po index 7396ee9..0547580 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -1498,9 +1498,6 @@ msgstr "顯示桌面圖示" msgid "Show Desktop button padding (px)" msgstr "顯示桌面按鈕 空間 (像素)" -msgid "Floating rounded theme" -msgstr "浮動圓滑主題" - #~ msgid "Show Details" #~ msgstr "顯示詳細資訊" diff --git a/prefs.js b/prefs.js deleted file mode 100644 index a40d5fe..0000000 --- a/prefs.js +++ /dev/null @@ -1,1170 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * Some code was also adapted from the upstream Gnome Shell source code. - */ - -import GdkPixbuf from 'gi://GdkPixbuf'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Gtk from 'gi://Gtk'; -import Gdk from 'gi://Gdk'; - -import * as PanelSettings from './panelSettings.js'; -import * as Pos from './panelPositions.js'; - -import {ExtensionPreferences, gettext as _, ngettext} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; - -const SCALE_UPDATE_TIMEOUT = 500; -const DEFAULT_PANEL_SIZES = [ 96, 64, 48, 32, 24 ]; -const DEFAULT_MARGIN_SIZES = [ 32, 24, 16, 12, 8, 4, 0 ]; -const DEFAULT_PADDING_SIZES = [ 32, 24, 16, 12, 8, 4, 0, -1 ]; -// Minimum length could be 0, but a higher value may help prevent confusion about where the panel went. -const LENGTH_MARKS = [ 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 ]; - -const SCHEMA_PATH = '/org.gnome.shell.extensions.zorin-taskbar/'; - -/** - * This function was copied from the activities-config extension - * https://github.com/nls1729/acme-code/tree/master/activities-config - * by Norman L. Smith. - */ -function cssHexString(css) { - let rrggbb = '#'; - let start; - for (let loop = 0; loop < 3; loop++) { - let end = 0; - let xx = ''; - for (let loop = 0; loop < 2; loop++) { - while (true) { - let x = css.slice(end, end + 1); - if ((x == '(') || (x == ',') || (x == ')')) - break; - end++; - } - if (loop == 0) { - end++; - start = end; - } - } - xx = parseInt(css.slice(start, end)).toString(16); - if (xx.length == 1) - xx = '0' + xx; - rrggbb += xx; - css = css.slice(end); - } - return rrggbb; -} - -function setShortcut(settings, shortcutName) { - let shortcut_text = settings.get_string(shortcutName + '-text'); - let [success, key, mods] = Gtk.accelerator_parse(shortcut_text); - - if (success && Gtk.accelerator_valid(key, mods)) { - let shortcut = Gtk.accelerator_name(key, mods); - settings.set_strv(shortcutName, [shortcut]); - } - else { - settings.set_strv(shortcutName, []); - } -} - -function checkHotkeyPrefix(settings) { - settings.delay(); - - let hotkeyPrefix = settings.get_string('hotkey-prefix-text'); - if (hotkeyPrefix == 'Super') - hotkeyPrefix = ''; - else if (hotkeyPrefix == 'SuperAlt') - hotkeyPrefix = ''; - let [ , , mods] = Gtk.accelerator_parse(hotkeyPrefix); - let [ , , shift_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); - let [ , , ctrl_mods] = Gtk.accelerator_parse('' + hotkeyPrefix); - - let numHotkeys = 10; - for (let i = 1; i <= numHotkeys; i++) { - let number = i; - if (number == 10) - number = 0; - let key = Gdk.keyval_from_name(number.toString()); - let key_kp = Gdk.keyval_from_name('KP_' + number.toString()); - if (Gtk.accelerator_valid(key, mods)) { - let shortcut = Gtk.accelerator_name(key, mods); - let shortcut_kp = Gtk.accelerator_name(key_kp, mods); - - // Setup shortcut strings - settings.set_strv('app-hotkey-' + i, [shortcut]); - settings.set_strv('app-hotkey-kp-' + i, [shortcut_kp]); - - // With - shortcut = Gtk.accelerator_name(key, shift_mods); - shortcut_kp = Gtk.accelerator_name(key_kp, shift_mods); - settings.set_strv('app-shift-hotkey-' + i, [shortcut]); - settings.set_strv('app-shift-hotkey-kp-' + i, [shortcut_kp]); - - // With - shortcut = Gtk.accelerator_name(key, ctrl_mods); - shortcut_kp = Gtk.accelerator_name(key_kp, ctrl_mods); - settings.set_strv('app-ctrl-hotkey-' + i, [shortcut]); - settings.set_strv('app-ctrl-hotkey-kp-' + i, [shortcut_kp]); - } - else { - // Reset default settings for the relevant keys if the - // accelerators are invalid - let keys = ['app-hotkey-' + i, 'app-shift-hotkey-' + i, 'app-ctrl-hotkey-' + i, // Regular numbers - 'app-hotkey-kp-' + i, 'app-shift-hotkey-kp-' + i, 'app-ctrl-hotkey-kp-' + i]; // Key-pad numbers - keys.forEach(function(val) { - settings.set_value(val, settings.get_default_value(val)); - }, this); - } - } - - settings.apply(); -} - -function mergeObjects(main, bck) { - for (const prop in bck) { - if (!main.hasOwnProperty(prop) && bck.hasOwnProperty(prop)) { - main[prop] = bck[prop]; - } - } - - return main; -} - -const Preferences = class { - - constructor(window, settings, gnomeInterfaceSettings, path) { - // this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.zorin-taskbar'); - this._rtl = (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL); - this._builder = new Gtk.Builder(); - this._builder.set_scope(new BuilderScope(this)); - this._settings = settings; - this._gnomeInterfaceSettings = gnomeInterfaceSettings; - this._path = path; - - this._metadata = ExtensionPreferences.lookupByURL(import.meta.url).metadata; - this._builder.set_translation_domain(this._metadata['gettext-domain']); - - window.set_search_enabled(true); - - // dialogs - this._builder.add_from_file(this._path + '/ui/BoxShowDesktopOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxDynamicOpacityOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxIntellihideOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxShowDateMenuOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxWindowPreviewOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxGroupAppsOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxMiddleClickOptions.ui'); - this._builder.add_from_file(this._path + '/ui/BoxOverlayShortcut.ui'); - - // pages - this._builder.add_from_file(this._path + '/ui/SettingsStyle.ui'); - let pageStyle = this._builder.get_object('style'); - window.add(pageStyle); - - this._builder.add_from_file(this._path + '/ui/SettingsPosition.ui'); - let pagePosition = this._builder.get_object('position'); - window.add(pagePosition); - - this._builder.add_from_file(this._path + '/ui/SettingsBehavior.ui'); - let pageBehavior = this._builder.get_object('behavior'); - window.add(pageBehavior); - - this._builder.add_from_file(this._path + '/ui/SettingsAction.ui'); - let pageAction = this._builder.get_object('action'); - window.add(pageAction); - - let listbox = this._builder.get_object('taskbar_display_listbox'); - let provider = new Gtk.CssProvider(); - provider.load_from_data('list { background-color: transparent; }', -1); - let context = listbox.get_style_context(); - context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - - // set the window as notebook, it is being used as parent for dialogs - this.notebook = window; - - // Timeout to delay the update of the settings - this._panel_size_timeout = 0; - this._dot_height_timeout = 0; - this._opacity_timeout = 0; - this._addFormatValueCallbacks(); - this._bindSettings(); - } - - /** - * Connect signals - */ - _connector(builder, object, signal, handler) { - object.connect(signal, this._SignalHandler[handler].bind(this)); - } - - _updateVerticalRelatedOptions() { - let position = this._getPanelPosition(this._currentMonitorIndex); - let isVertical = position == Pos.LEFT || position == Pos.RIGHT; - let showDesktopWidthLabel = this._builder.get_object('show_showdesktop_width_label'); - - showDesktopWidthLabel.set_title(_('Show Desktop button padding (px)')); - - this._displayPanelPositionsForMonitor(this._currentMonitorIndex); - } - - _maybeDisableTopPosition() { - let keepTopPanel = this._settings.get_boolean('stockgs-keep-top-panel'); - let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - let topAvailable = !keepTopPanel || (!monitorSync && this._currentMonitorIndex != this.monitors[0]); - let topRadio = this._builder.get_object('position_top_button'); - - topRadio.set_sensitive(topAvailable); - topRadio.set_tooltip_text(!topAvailable ? _('Unavailable when gnome-shell top panel is present') : ''); - } - - _getPanelPosition(monitorIndex) { - return PanelSettings.getPanelPosition(this._settings, monitorIndex); - } - - _setPanelPosition(position) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelPosition(this._settings, monitorIndex, position); - }); - this._setAnchorLabels(this._currentMonitorIndex); - } - - _setPositionRadios(position) { - this._ignorePositionRadios = true; - - switch (position) { - case Pos.BOTTOM: - this._builder.get_object('position_bottom_button').set_active(true); - break; - case Pos.TOP: - this._builder.get_object('position_top_button').set_active(true); - break; - case Pos.LEFT: - this._builder.get_object('position_left_button').set_active(true); - break; - case Pos.RIGHT: - this._builder.get_object('position_right_button').set_active(true); - break; - } - - this._ignorePositionRadios = false; - } - - /** - * Set panel anchor combo labels according to whether the monitor's panel is vertical - * or horizontal, or if all monitors' panels are being configured and they are a mix - * of vertical and horizontal. - */ - _setAnchorLabels(currentMonitorIndex) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [currentMonitorIndex]; - const allVertical = monitorsToSetFor.every(i => { - const position = PanelSettings.getPanelPosition(this._settings, i); - return position === Pos.LEFT || position === Pos.RIGHT - }); - const allHorizontal = monitorsToSetFor.every(i => { - const position = PanelSettings.getPanelPosition(this._settings, i); - return position === Pos.TOP || position === Pos.BOTTOM; - }); - - const anchor_combo = this._builder.get_object('panel_anchor_combo'); - anchor_combo.remove_all(); - - if (allHorizontal) { - anchor_combo.append(Pos.START, _('Left')); - anchor_combo.append(Pos.MIDDLE, _('Center')); - anchor_combo.append(Pos.END, _('Right')); - } else if (allVertical) { - anchor_combo.append(Pos.START, _('Top')); - anchor_combo.append(Pos.MIDDLE, _('Middle')); - anchor_combo.append(Pos.END, _('Bottom')); - } else { - // Setting for a mix of horizontal and vertical panels on different monitors. - anchor_combo.append(Pos.START, _('Start')); - anchor_combo.append(Pos.MIDDLE, _('Middle')); - anchor_combo.append(Pos.END, _('End')); - } - - // Set combo box after re-populating its options. But only if it's for a single-panel - // configuration, or a multi-panel configuration where they all have the same anchor - // setting. So don't set the combo box if there is a multi-panel configuration with - // different anchor settings. - const someAnchor = PanelSettings.getPanelAnchor(this._settings, currentMonitorIndex); - if (monitorsToSetFor.every(i => - PanelSettings.getPanelAnchor(this._settings, i) === someAnchor)) { - const panel_anchor = PanelSettings.getPanelAnchor(this._settings, currentMonitorIndex); - this._builder.get_object('panel_anchor_combo').set_active_id(panel_anchor); - } - } - - /** - * When a monitor is selected, update the widgets for panel position, size, anchoring, - * and contents so they accurately show the settings for the panel on that monitor. - */ - _updateWidgetSettingsForMonitor(monitorIndex) { - // Update display of panel screen position setting - this._maybeDisableTopPosition(); - const panelPosition = this._getPanelPosition(monitorIndex); - this._setPositionRadios(panelPosition); - - // Update display of thickness, length, and anchor settings - const panel_size_scale = this._builder.get_object('panel_size_scale'); - const size = PanelSettings.getPanelSize(this._settings, monitorIndex); - panel_size_scale.set_value(size); - - const panel_length_scale = this._builder.get_object('panel_length_scale'); - const length = PanelSettings.getPanelLength(this._settings, monitorIndex); - panel_length_scale.set_value(length); - this._setAnchorWidgetSensitivity(length); - - this._setAnchorLabels(monitorIndex); - - // Update display of panel content settings - this._displayPanelPositionsForMonitor(monitorIndex); - } - - /** - * Anchor is only relevant if panel length is less than 100%. Enable or disable - * anchor widget sensitivity accordingly. - */ - _setAnchorWidgetSensitivity(panelLength) { - const isPartialLength = panelLength < 100; - this._builder.get_object('panel_anchor_label').set_sensitive(isPartialLength); - this._builder.get_object('panel_anchor_combo').set_sensitive(isPartialLength); - } - - _displayPanelPositionsForMonitor(monitorIndex) { - let taskbarListBox = this._builder.get_object('taskbar_display_listbox'); - - while(taskbarListBox.get_first_child()) - { - taskbarListBox.remove(taskbarListBox.get_first_child()); - } - - let labels = {}; - let panelPosition = this._getPanelPosition(monitorIndex); - let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT; - let panelElementPositionsSettings = PanelSettings.getSettingsJson(this._settings, 'panel-element-positions'); - let panelElementPositions = panelElementPositionsSettings[monitorIndex] || Pos.defaults; - let updateElementsSettings = () => { - let newPanelElementPositions = []; - let monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - let monitors = monitorSync ? this.monitors : [monitorIndex]; - - let child = taskbarListBox.get_first_child(); - while (child != null) - { - newPanelElementPositions.push({ - element: child.id, - visible: child.visibleToggleBtn.get_active(), - position: child.positionCombo.get_active_id() - }); - child = child.get_next_sibling(); - } - - monitors.forEach(m => panelElementPositionsSettings[m] = newPanelElementPositions); - this._settings.set_string('panel-element-positions', JSON.stringify(panelElementPositionsSettings)); - }; - - - labels[Pos.SHOW_APPS_BTN] = _('Show Applications button'); - labels[Pos.ACTIVITIES_BTN] = _('Activities button'); - labels[Pos.TASKBAR] = _('Taskbar'); - labels[Pos.DATE_MENU] = _('Date menu'); - labels[Pos.SYSTEM_MENU] = _('System menu'); - labels[Pos.LEFT_BOX] = _('Left box'); - labels[Pos.CENTER_BOX] = _('Center box'); - labels[Pos.RIGHT_BOX] = _('Right box'); - labels[Pos.DESKTOP_BTN] = _('Desktop button'); - - panelElementPositions.forEach(el => { - let row = new Gtk.ListBoxRow(); - let grid = new Gtk.Grid({ margin_start: 12, margin_end: 12, column_spacing: 8 }); - let upDownGrid = new Gtk.Grid({ column_spacing: 2 }); - let upBtn = new Gtk.Button({ tooltip_text: _('Move up') }); - let upImg = new Gtk.Image({ icon_name: 'go-up-symbolic', pixel_size: 12 }); - let downBtn = new Gtk.Button({ tooltip_text: _('Move down') }); - let downImg = new Gtk.Image({ icon_name: 'go-down-symbolic', pixel_size: 12 }); - let visibleToggleBtn = new Gtk.ToggleButton({ label: _('Visible'), active: el.visible }); - let positionCombo = new Gtk.ComboBoxText({ tooltip_text: _('Select element position') }); - let upDownClickHandler = limit => { - let index = row.get_index(); - - if (index != limit) { - taskbarListBox.remove(row); - taskbarListBox.insert(row, index + (!limit ? -1 : 1)); - updateElementsSettings(); - } - }; - - positionCombo.append(Pos.STACKED_TL, isVertical ? _('Stacked to top') : _('Stacked to left')); - positionCombo.append(Pos.STACKED_BR, isVertical ? _('Stacked to bottom') :_('Stacked to right')); - positionCombo.append(Pos.CENTERED, _('Centered')); - positionCombo.append(Pos.CENTERED_MONITOR, _('Monitor Center')); - positionCombo.set_active_id(el.position); - - upBtn.connect('clicked', () => upDownClickHandler(0)); - downBtn.connect('clicked', () => upDownClickHandler(panelElementPositions.length - 1)); - visibleToggleBtn.connect('toggled', () => updateElementsSettings()); - positionCombo.connect('changed', () => updateElementsSettings()); - - upBtn.set_child(upImg); - downBtn.set_child(downImg); - - upDownGrid.attach(upBtn, 0, 0, 1, 1); - upDownGrid.attach(downBtn, 1, 0, 1, 1); - - grid.attach(upDownGrid, 0, 0, 1, 1); - grid.attach(new Gtk.Label({ label: labels[el.element], xalign: 0, hexpand: true }), 1, 0, 1, 1); - - if (Pos.optionDialogFunctions[el.element]) { - let cogImg = new Gtk.Image({ icon_name: 'emblem-system-symbolic' }); - let optionsBtn = new Gtk.Button({ tooltip_text: _('More options') }); - - optionsBtn.get_style_context().add_class('circular'); - optionsBtn.set_child(cogImg); - grid.attach(optionsBtn, 2, 0, 1, 1); - - optionsBtn.connect('clicked', () => this[Pos.optionDialogFunctions[el.element]]()); - } - - grid.attach(visibleToggleBtn, 3, 0, 1, 1); - grid.attach(positionCombo, 4, 0, 1, 1); - - row.id = el.element; - row.visibleToggleBtn = visibleToggleBtn; - row.positionCombo = positionCombo; - - row.set_child(grid); - taskbarListBox.insert(row, -1); - }); - } - - _createPreferencesDialog(title, content, reset_function = null) { - let dialog; - - dialog = new Gtk.Dialog({ title: title, - transient_for: this.notebook.get_root(), - use_header_bar: true, - modal: true }); - - // GTK+ leaves positive values for application-defined response ids. - // Use +1 for the reset action - if (reset_function != null) - dialog.add_button(_('Reset to defaults'), 1); - - dialog.get_content_area().append(content); - - dialog.connect('response', (dialog, id) => { - if (id == 1) { - // restore default settings - if (reset_function) - reset_function(); - } else { - // remove the settings content so it doesn't get destroyed; - dialog.get_content_area().remove(content); - dialog.destroy(); - } - return; - }); - - return dialog; - } - - _showDateMenuOptions() { - let box = this._builder.get_object('box_date_menu_options'); - - let dialog = this._createPreferencesDialog(_('Date Menu options'), box, () => - { - // restore default settings - this._gnomeInterfaceSettings.set_value('clock-show-date', this._gnomeInterfaceSettings.get_default_value('clock-show-date')); - this._gnomeInterfaceSettings.set_value('clock-show-weekday', this._gnomeInterfaceSettings.get_default_value('clock-show-weekday')); - this._gnomeInterfaceSettings.set_value('clock-show-seconds', this._gnomeInterfaceSettings.get_default_value('clock-show-seconds')); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - } - - _showDesktopButtonOptions() { - let box = this._builder.get_object('box_show_showdesktop_options'); - - let dialog = this._createPreferencesDialog(_('Show Desktop options'), box, () => - { - // restore default settings - this._settings.set_value('show-showdesktop-icon', this._settings.get_default_value('show-showdesktop-icon')); - - this._settings.set_value('showdesktop-button-width', this._settings.get_default_value('showdesktop-button-width')); - this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); - - this._settings.set_value('show-showdesktop-hover', this._settings.get_default_value('show-showdesktop-hover')); - }); - - this._builder.get_object('show_showdesktop_width_spinbutton').set_value(this._settings.get_int('showdesktop-button-width')); - this._builder.get_object('show_showdesktop_width_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('showdesktop-button-width', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - } - - _addFormatValueCallbacks() { - // position - this._builder.get_object('panel_size_scale') - .set_format_value_func((scale, value) => { - return value + ' px'; - }); - } - - _bindSettings() { - // size options - let panel_size_scale = this._builder.get_object('panel_size_scale'); - panel_size_scale.set_range(DEFAULT_PANEL_SIZES[DEFAULT_PANEL_SIZES.length - 1], DEFAULT_PANEL_SIZES[0]); - DEFAULT_PANEL_SIZES.slice(1, -1).forEach(function(val) { - panel_size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); - }); - - // Correct for rtl languages - if (this._rtl) { - // Flip value position: this is not done automatically - panel_size_scale.set_value_pos(Gtk.PositionType.LEFT); - // I suppose due to a bug, having a more than one mark and one above a value of 100 - // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable - // and then manually inverting it - panel_size_scale.set_flippable(false); - panel_size_scale.set_inverted(true); - } - - //multi-monitor - this.monitors = this._settings.get_value('available-monitors').deep_unpack(); - - let dtpPrimaryMonitorIndex = 0; - - this._currentMonitorIndex = this.monitors[dtpPrimaryMonitorIndex]; - - this._settings.connect('changed::panel-positions', () => this._updateVerticalRelatedOptions()); - this._updateVerticalRelatedOptions(); - - for (let i = 0; i < this.monitors.length; ++i) { - //the gnome-shell primary index is the first one in the "available-monitors" setting - let label = !i ? _('Primary monitor') : _('Monitor ') + (i + 1); - - this._builder.get_object('taskbar_position_monitor_combo').append_text(label); - } - - this._builder.get_object('taskbar_position_monitor_combo').set_active(dtpPrimaryMonitorIndex); - - this._settings.bind('panel-element-positions-monitors-sync', - this._builder.get_object('taskbar_position_sync_button'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('panel-element-positions-monitors-sync', - this._builder.get_object('taskbar_position_monitor_combo'), - 'sensitive', - Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._builder.get_object('taskbar_position_monitor_combo').connect('changed', (widget) => { - this._currentMonitorIndex = this.monitors[widget.get_active()]; - this._updateWidgetSettingsForMonitor(this._currentMonitorIndex); - }); - - this._settings.bind('multi-monitors', - this._builder.get_object('multimon_multi_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - if (this.monitors.length === 1) { - this._builder.get_object('multimon_multi_switch').set_sensitive(false); - } - - const panel_length_scale = this._builder.get_object('panel_length_scale'); - panel_length_scale.connect('value-changed', (widget) => { - const value = widget.get_value(); - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelLength(this._settings, monitorIndex, value); - }); - - this._setAnchorWidgetSensitivity(value); - }); - - this._builder.get_object('panel_anchor_combo').connect('changed', (widget) => { - const value = widget.get_active_id(); - // Value can be null while anchor labels are being swapped out - if (value !== null) { - const monitorSync = this._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this.monitors : [this._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelAnchor(this._settings, monitorIndex, value); - }); - } - }); - - this._updateWidgetSettingsForMonitor(this._currentMonitorIndex); - - //panel style - this._settings.bind('floating-rounded-theme', - this._builder.get_object('floating_rounded_theme_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - //dynamic opacity - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_override_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_box'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-custom-opacity', - this._builder.get_object('trans_opacity_box2'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('trans_opacity_override_switch').connect('notify::active', (widget) => { - if (!widget.get_active()) - this._builder.get_object('trans_dyn_switch').set_active(false); - }); - - this._builder.get_object('trans_opacity_spinbutton').set_value(this._settings.get_double('trans-panel-opacity') * 100); - this._builder.get_object('trans_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-panel-opacity', widget.get_value() * 0.01); - }); - - this._settings.bind('trans-use-dynamic-opacity', - this._builder.get_object('trans_dyn_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-use-dynamic-opacity', - this._builder.get_object('trans_dyn_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('trans-dynamic-behavior', - this._builder.get_object('trans_options_window_type_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('trans_options_min_opacity_spinbutton').set_value(this._settings.get_double('trans-dynamic-anim-target') * 100); - this._builder.get_object('trans_options_min_opacity_spinbutton').connect('value-changed', (widget) => { - this._settings.set_double('trans-dynamic-anim-target', widget.get_value() * 0.01); - }); - - this._builder.get_object('trans_dyn_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_dynamic_opacity_options'); - - let dialog = this._createPreferencesDialog(_('Dynamic opacity options'), box, () => - { - // restore default settings - this._settings.set_value('trans-dynamic-behavior', this._settings.get_default_value('trans-dynamic-behavior')); - - this._settings.set_value('trans-dynamic-anim-target', this._settings.get_default_value('trans-dynamic-anim-target')); - this._builder.get_object('trans_options_min_opacity_spinbutton').set_value(this._settings.get_double('trans-dynamic-anim-target') * 100); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - - }); - - this._settings.bind('intellihide', - this._builder.get_object('intellihide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide', - this._builder.get_object('intellihide_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-hide-from-windows', - this._builder.get_object('intellihide_window_hide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-hide-from-windows', - this._builder.get_object('intellihide_behaviour_options'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-behaviour', - this._builder.get_object('intellihide_behaviour_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-use-pressure', - this._builder.get_object('intellihide_use_pressure_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-show-in-fullscreen', - this._builder.get_object('intellihide_show_in_fullscreen_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('intellihide-only-secondary', - this._builder.get_object('intellihide_only_secondary_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('multi-monitors', - this._builder.get_object('grid_intellihide_only_secondary'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('multimon_multi_switch').connect('notify::active', (widget) => { - if (!widget.get_active()) - this._builder.get_object('intellihide_only_secondary_switch').set_active(false); - }); - - this._settings.bind('intellihide-key-toggle-text', - this._builder.get_object('intellihide_toggle_entry'), - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.connect('changed::intellihide-key-toggle-text', () => setShortcut(this._settings, 'intellihide-key-toggle')); - - this._builder.get_object('intellihide_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_intellihide_options'); - - let dialog = this._createPreferencesDialog(_('Intellihide options'), box, () => - { - // restore default settings - this._settings.set_value('intellihide-hide-from-windows', this._settings.get_default_value('intellihide-hide-from-windows')); - this._settings.set_value('intellihide-behaviour', this._settings.get_default_value('intellihide-behaviour')); - this._settings.set_value('intellihide-use-pressure', this._settings.get_default_value('intellihide-use-pressure')); - this._settings.set_value('intellihide-show-in-fullscreen', this._settings.get_default_value('intellihide-show-in-fullscreen')); - this._settings.set_value('intellihide-only-secondary', this._settings.get_default_value('intellihide-only-secondary')); - - this._settings.set_value('intellihide-key-toggle-text', this._settings.get_default_value('intellihide-key-toggle-text')); - }); - - dialog.show(); - dialog.set_default_size(1, 1); - - }); - - // Behavior panel - - this._gnomeInterfaceSettings.bind('clock-show-date', - this._builder.get_object('date_menu_date_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._gnomeInterfaceSettings.bind('clock-show-weekday', - this._builder.get_object('date_menu_weekday_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._gnomeInterfaceSettings.bind('clock-show-seconds', - this._builder.get_object('date_menu_seconds_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-showdesktop-icon', - this._builder.get_object('show_showdesktop_icon_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-showdesktop-hover', - this._builder.get_object('show_showdesktop_hide_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-window-previews', - this._builder.get_object('show_window_previews_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-window-previews', - this._builder.get_object('show_window_previews_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-tooltip', - this._builder.get_object('show_tooltip_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites', - this._builder.get_object('show_favorite_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites-all-monitors', - this._builder.get_object('multimon_multi_show_favorites_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-favorites', - this._builder.get_object('multimon_multi_show_favorites_switch'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('show-running-apps', - this._builder.get_object('show_runnning_apps_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('show_window_previews_button').connect('clicked', () => { - let scrolledWindow = this._builder.get_object('box_window_preview_options'); - - let dialog = this._createPreferencesDialog(_('Window preview options'), scrolledWindow, () => - { - // restore default settings - this._settings.set_value('peek-mode', this._settings.get_default_value('peek-mode')); - - this._settings.set_value('window-preview-size', this._settings.get_default_value('window-preview-size')); - this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); - - this._settings.set_value('preview-middle-click-close', this._settings.get_default_value('preview-middle-click-close')); - }); - - this._settings.bind('preview-middle-click-close', - this._builder.get_object('preview_middle_click_close_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('peek-mode', - this._builder.get_object('peek_mode_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('preview_size_spinbutton').set_value(this._settings.get_int('window-preview-size')); - this._builder.get_object('preview_size_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('window-preview-size', widget.get_value()); - }); - - dialog.show(); - - }); - - this._settings.bind('group-apps', - this._builder.get_object('group_apps_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.bind('group-apps', - this._builder.get_object('show_group_apps_options_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN); - - this._settings.bind('progress-show-count', - this._builder.get_object('show_notification_badge_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('group-apps-use-fixed-width', - this._builder.get_object('group_apps_use_fixed_width_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('group-apps-use-launchers', - this._builder.get_object('group_apps_use_launchers_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('display_multitasking_settings').connect('activated', () => { - try { - const output = GLib.spawn_command_line_async('gnome-control-center multitasking'); - } catch (e) { - logError(e); - } - }); - - this._builder.get_object('show_group_apps_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_group_apps_options'); - - let dialog = this._createPreferencesDialog(_('Ungrouped application options'), box, () => - { - // restore default settings - this._settings.set_value('group-apps-label-max-width', this._settings.get_default_value('group-apps-label-max-width')); - this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); - - this._settings.set_value('group-apps-use-fixed-width', this._settings.get_default_value('group-apps-use-fixed-width')); - this._settings.set_value('group-apps-use-launchers', this._settings.get_default_value('group-apps-use-launchers')); - }); - - this._builder.get_object('group_apps_label_max_width_spinbutton').set_value(this._settings.get_int('group-apps-label-max-width')); - this._builder.get_object('group_apps_label_max_width_spinbutton').connect('value-changed', (widget) => { - this._settings.set_int('group-apps-label-max-width', widget.get_value()); - }); - - dialog.show(); - dialog.set_default_size(600, 1); - - }); - - this._builder.get_object('click_action_combo').set_active_id(this._settings.get_string('click-action')); - this._builder.get_object('click_action_combo').connect('changed', (widget) => { - this._settings.set_string('click-action', widget.get_active_id()); - }); - - this._builder.get_object('shift_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('shift-click-action', widget.get_active_id()); - }); - - this._builder.get_object('middle_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('middle-click-action', widget.get_active_id()); - }); - this._builder.get_object('shift_middle_click_action_combo').connect('changed', (widget) => { - this._settings.set_string('shift-middle-click-action', widget.get_active_id()); - }); - - // Create dialog for middle-click options - this._builder.get_object('middle_click_options_button').connect('clicked', () => { - let box = this._builder.get_object('box_middle_click_options'); - - let dialog = this._createPreferencesDialog(_('Customize middle-click behavior'), box, () => - { - // restore default settings for the relevant keys - let keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action']; - keys.forEach(function(val) { - this._settings.set_value(val, this._settings.get_default_value(val)); - }, this); - this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); - this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); - this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); - }); - - this._builder.get_object('shift_click_action_combo').set_active_id(this._settings.get_string('shift-click-action')); - - this._builder.get_object('middle_click_action_combo').set_active_id(this._settings.get_string('middle-click-action')); - - this._builder.get_object('shift_middle_click_action_combo').set_active_id(this._settings.get_string('shift-middle-click-action')); - - this._settings.bind('shift-click-action', - this._builder.get_object('shift_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('middle-click-action', - this._builder.get_object('middle_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('shift-middle-click-action', - this._builder.get_object('shift_middle_click_action_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - dialog.show(); - dialog.set_default_size(700, 1); - - }); - - this._builder.get_object('scroll_icon_combo').set_active_id(this._settings.get_string('scroll-icon-action')); - this._builder.get_object('scroll_icon_combo').connect('changed', (widget) => { - this._settings.set_string('scroll-icon-action', widget.get_active_id()); - }); - - this._settings.bind('hot-keys', - this._builder.get_object('hot_keys_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('hot-keys', - this._builder.get_object('overlay_button'), - 'sensitive', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('overlay_combo').connect('changed', (widget) => { - this._settings.set_string('hotkeys-overlay-combo', widget.get_active_id()); - }); - - this._settings.bind('shortcut-previews', - this._builder.get_object('shortcut_preview_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('shortcut_num_keys_combo').set_active_id(this._settings.get_string('shortcut-num-keys')); - this._builder.get_object('shortcut_num_keys_combo').connect('changed', (widget) => { - this._settings.set_string('shortcut-num-keys', widget.get_active_id()); - }); - - this._settings.connect('changed::hotkey-prefix-text', () => {checkHotkeyPrefix(this._settings);}); - - this._builder.get_object('hotkey_prefix_combo').set_active_id(this._settings.get_string('hotkey-prefix-text')); - - this._settings.bind('hotkey-prefix-text', - this._builder.get_object('hotkey_prefix_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('overlay_combo').set_active_id(this._settings.get_string('hotkeys-overlay-combo')); - - this._settings.bind('hotkeys-overlay-combo', - this._builder.get_object('overlay_combo'), - 'active-id', - Gio.SettingsBindFlags.DEFAULT); - - this._settings.bind('shortcut-text', - this._builder.get_object('shortcut_entry'), - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.connect('changed::shortcut-text', () => {setShortcut(this._settings, 'shortcut');}); - - // Create dialog for number overlay options - this._builder.get_object('overlay_button').connect('clicked', () => { - let box = this._builder.get_object('box_overlay_shortcut'); - - let dialog = this._createPreferencesDialog(_('Advanced hotkeys options'), box, () => - { - // restore default settings for the relevant keys - let keys = ['hotkey-prefix-text', 'shortcut-text', 'hotkeys-overlay-combo', 'shortcut-previews']; - keys.forEach(function(val) { - this._settings.set_value(val, this._settings.get_default_value(val)); - }, this); - }); - - dialog.show(); - dialog.set_default_size(600, 1); - - }); - - // Fine-tune panel - - let sizeScales = [ - {objectName: 'panel_length_scale', valueName: '', range: LENGTH_MARKS } - ]; - - for(const idx in sizeScales) { - let size_scale = this._builder.get_object(sizeScales[idx].objectName); - let range = sizeScales[idx].range; - size_scale.set_range(range[range.length - 1], range[0]); - let value; - if (sizeScales[idx].objectName === 'panel_length_scale') { - value = PanelSettings.getPanelLength(this._settings, this._currentMonitorIndex); - } else { - value = this._settings.get_int(sizeScales[idx].valueName); - } - size_scale.set_value(value); - // Add marks from range arrays, omitting the first and last values. - range.slice(1, -1).forEach(function(val) { - size_scale.add_mark(val, Gtk.PositionType.TOP, val.toString()); - }); - - // Corrent for rtl languages - if (this._rtl) { - // Flip value position: this is not done automatically - size_scale.set_value_pos(Gtk.PositionType.LEFT); - // I suppose due to a bug, having a more than one mark and one above a value of 100 - // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable - // and then manually inverting it - size_scale.set_flippable(false); - size_scale.set_inverted(true); - } - } - - this._settings.bind('stockgs-keep-top-panel', - this._builder.get_object('stockgs_top_panel_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - - - this._settings.connect('changed::stockgs-keep-top-panel', () => this._maybeDisableTopPosition()); - - this._maybeDisableTopPosition(); - - this._settings.bind('stockgs-panelbtn-click-only', - this._builder.get_object('stockgs_panelbtn_switch'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - } -} - - -const BuilderScope = GObject.registerClass({ - Implements: [Gtk.BuilderScope], -}, class BuilderScope extends GObject.Object { - - _init(preferences) { - this._preferences = preferences; - super._init(); - } - - vfunc_create_closure(builder, handlerName, flags, connectObject) { - if (flags & Gtk.BuilderClosureFlags.SWAPPED) - throw new Error('Unsupported template signal flag "swapped"'); - - if (typeof this[handlerName] === 'undefined') - throw new Error(`${handlerName} is undefined`); - - return this[handlerName].bind(connectObject || this); - } - - on_btn_click(connectObject) { - connectObject.set_label("Clicked"); - } - - position_bottom_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.BOTTOM); - } - - position_top_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.TOP); - } - - position_left_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.LEFT); - } - - position_right_button_clicked_cb(button) { - if (!this._preferences._ignorePositionRadios && button.get_active()) this._preferences._setPanelPosition(Pos.RIGHT); - } - - panel_size_scale_value_changed_cb(scale) { - // Avoid settings the size continuously - if (this._preferences._panel_size_timeout > 0) - GLib.Source.remove(this._preferences._panel_size_timeout); - - this._preferences._panel_size_timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => { - const value = scale.get_value(); - const monitorSync = this._preferences._settings.get_boolean('panel-element-positions-monitors-sync'); - const monitorsToSetFor = monitorSync ? this._preferences.monitors : [this._preferences._currentMonitorIndex]; - monitorsToSetFor.forEach(monitorIndex => { - PanelSettings.setPanelSize(this._preferences._settings, monitorIndex, value); - }); - - this._preferences._panel_size_timeout = 0; - return GLib.SOURCE_REMOVE; - }); - } -}); - -export default class ZorinTaskbarPreferences extends ExtensionPreferences { - fillPreferencesWindow(window) { - window._settings = this.getSettings('org.gnome.shell.extensions.zorin-taskbar'); - window._gnomeInterfaceSettings = this.getSettings('org.gnome.desktop.interface'); - - // use default width or window - window.set_default_size(0, 625); - - let preferences = new Preferences(window, window._settings, window._gnomeInterfaceSettings, this.path); - } -} diff --git a/progress.js b/progress.js deleted file mode 100644 index db457c6..0000000 --- a/progress.js +++ /dev/null @@ -1,601 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - */ - -import Cairo from 'cairo'; -import Gio from 'gi://Gio'; -import Clutter from 'gi://Clutter'; -import Pango from 'gi://Pango'; -import St from 'gi://St'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; - -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; - - -export const ProgressManager = class extends EventEmitter { - - constructor() { - super(); - - this._entriesByDBusName = {}; - - this._launcher_entry_dbus_signal_id = - Gio.DBus.session.signal_subscribe(null, // sender - 'com.canonical.Unity.LauncherEntry', // iface - null, // member - null, // path - null, // arg0 - Gio.DBusSignalFlags.NONE, - this._onEntrySignalReceived.bind(this)); - - this._dbus_name_owner_changed_signal_id = - Gio.DBus.session.signal_subscribe('org.freedesktop.DBus', // sender - 'org.freedesktop.DBus', // interface - 'NameOwnerChanged', // member - '/org/freedesktop/DBus', // path - null, // arg0 - Gio.DBusSignalFlags.NONE, - this._onDBusNameOwnerChanged.bind(this)); - - this._acquireUnityDBus(); - } - - destroy() { - if (this._launcher_entry_dbus_signal_id) { - Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id); - } - - if (this._dbus_name_owner_changed_signal_id) { - Gio.DBus.session.signal_unsubscribe(this._dbus_name_owner_changed_signal_id); - } - - this._releaseUnityDBus(); - } - - size() { - return Object.keys(this._entriesByDBusName).length; - } - - lookupByDBusName(dbusName) { - return this._entriesByDBusName.hasOwnProperty(dbusName) ? this._entriesByDBusName[dbusName] : null; - } - - lookupById(appId) { - let ret = []; - for (let dbusName in this._entriesByDBusName) { - let entry = this._entriesByDBusName[dbusName]; - if (entry && entry.appId() == appId) { - ret.push(entry); - } - } - - return ret; - } - - addEntry(entry) { - let existingEntry = this.lookupByDBusName(entry.dbusName()); - if (existingEntry) { - existingEntry.update(entry); - } else { - this._entriesByDBusName[entry.dbusName()] = entry; - this.emit('progress-entry-added', entry); - } - } - - removeEntry(entry) { - delete this._entriesByDBusName[entry.dbusName()] - this.emit('progress-entry-removed', entry); - } - - _acquireUnityDBus() { - if (!this._unity_bus_id) { - Gio.DBus.session.own_name('com.canonical.Unity', - Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); - } - } - - _releaseUnityDBus() { - if (this._unity_bus_id) { - Gio.DBus.session.unown_name(this._unity_bus_id); - this._unity_bus_id = 0; - } - } - - _onEntrySignalReceived(connection, sender_name, object_path, - interface_name, signal_name, parameters, user_data) { - if (!parameters || !signal_name) - return; - - if (signal_name == 'Update') { - if (!sender_name) { - return; - } - - this._handleUpdateRequest(sender_name, parameters); - } - } - - _onDBusNameOwnerChanged(connection, sender_name, object_path, - interface_name, signal_name, parameters, user_data) { - if (!parameters || !this.size()) - return; - - let [name, before, after] = parameters.deep_unpack(); - - if (!after) { - if (this._entriesByDBusName.hasOwnProperty(before)) { - this.removeEntry(this._entriesByDBusName[before]); - } - } - } - - _handleUpdateRequest(senderName, parameters) { - if (!senderName || !parameters) { - return; - } - - let [appUri, properties] = parameters.deep_unpack(); - let appId = appUri.replace(/(^\w+:|^)\/\//, ''); - let entry = this.lookupByDBusName(senderName); - - if (entry) { - entry.setDBusName(senderName); - entry.update(properties); - } else { - let entry = new AppProgress(senderName, appId, properties); - this.addEntry(entry); - } - } -}; - -export class AppProgress extends EventEmitter { - - constructor(dbusName, appId, properties) { - super(); - - this._dbusName = dbusName; - this._appId = appId; - this._count = 0; - this._countVisible = false; - this._progress = 0.0; - this._progressVisible = false; - this._urgent = false; - this.update(properties); - } - - appId() { - return this._appId; - } - - dbusName() { - return this._dbusName; - } - - count() { - return this._count; - } - - setCount(count) { - if (this._count != count) { - this._count = count; - this.emit('count-changed', this._count); - } - } - - countVisible() { - return this._countVisible; - } - - setCountVisible(countVisible) { - if (this._countVisible != countVisible) { - this._countVisible = countVisible; - this.emit('count-visible-changed', this._countVisible); - } - } - - progress() { - return this._progress; - } - - setProgress(progress) { - if (this._progress != progress) { - this._progress = progress; - this.emit('progress-changed', this._progress); - } - } - - progressVisible() { - return this._progressVisible; - } - - setProgressVisible(progressVisible) { - if (this._progressVisible != progressVisible) { - this._progressVisible = progressVisible; - this.emit('progress-visible-changed', this._progressVisible); - } - } - - urgent() { - return this._urgent; - } - - setUrgent(urgent) { - if (this._urgent != urgent) { - this._urgent = urgent; - this.emit('urgent-changed', this._urgent); - } - } - - setDBusName(dbusName) { - if (this._dbusName != dbusName) { - let oldName = this._dbusName; - this._dbusName = dbusName; - this.emit('dbus-name-changed', oldName); - } - } - - update(other) { - if (other instanceof AppProgress) { - this.setDBusName(other.dbusName()) - this.setCount(other.count()); - this.setCountVisible(other.countVisible()); - this.setProgress(other.progress()); - this.setProgressVisible(other.progressVisible()) - this.setUrgent(other.urgent()); - } else { - for (let property in other) { - if (other.hasOwnProperty(property)) { - if (property == 'count') { - this.setCount(other[property].get_int64()); - } else if (property == 'count-visible') { - this.setCountVisible(SETTINGS.get_boolean('progress-show-count') && other[property].get_boolean()); - } else if (property == 'progress') { - this.setProgress(other[property].get_double()); - } else if (property == 'progress-visible') { - this.setProgressVisible(SETTINGS.get_boolean('progress-show-bar') && other[property].get_boolean()); - } else if (property == 'urgent') { - this.setUrgent(other[property].get_boolean()); - } else { - // Not implemented yet - } - } - } - } - } -} - - -export const ProgressIndicator = class { - - constructor(source, progressManager) { - this._source = source; - this._progressManager = progressManager; - this._signalsHandler = new Utils.GlobalSignalsHandler(); - - this._sourceDestroyId = this._source.connect('destroy', () => { - this._signalsHandler.destroy(); - }); - - this._notificationBadgeLabel = new St.Label({ style_class: 'badge' }); - this._notificationBadgeBin = new St.Bin({ - child: this._notificationBadgeLabel, - x_align: Clutter.ActorAlign.END, - y_align: Clutter.ActorAlign.START, - x_expand: true, y_expand: true - }); - this._notificationBadgeLabel.add_style_class_name('notification-badge'); - this._notificationBadgeCount = 0; - this._notificationBadgeBin.hide(); - - this._source._dtpIconContainer.add_child(this._notificationBadgeBin); - this._source._dtpIconContainer.connect('notify::allocation', this.updateNotificationBadge.bind(this)); - - this._progressManagerEntries = []; - this._progressManager.lookupById(this._source.app.id).forEach( - (entry) => { - this.insertEntry(entry); - } - ); - - this._signalsHandler.add([ - this._progressManager, - 'progress-entry-added', - this._onEntryAdded.bind(this) - ], [ - this._progressManager, - 'progress-entry-removed', - this._onEntryRemoved.bind(this) - ]); - } - - destroy() { - this._source.disconnect(this._sourceDestroyId); - this._signalsHandler.destroy(); - } - - _onEntryAdded(appProgress, entry) { - if (!entry || !entry.appId()) - return; - if (this._source && this._source.app && this._source.app.id == entry.appId()) { - this.insertEntry(entry); - } - } - - _onEntryRemoved(appProgress, entry) { - if (!entry || !entry.appId()) - return; - - if (this._source && this._source.app && this._source.app.id == entry.appId()) { - this.removeEntry(entry); - } - } - - updateNotificationBadge() { - this._source.updateNumberOverlay(this._notificationBadgeBin); - this._notificationBadgeLabel.clutter_text.ellipsize = Pango.EllipsizeMode.MIDDLE; - } - - _notificationBadgeCountToText(count) { - if (count <= 9999) { - return count.toString(); - } else if (count < 1e5) { - let thousands = count / 1e3; - return thousands.toFixed(1).toString() + "k"; - } else if (count < 1e6) { - let thousands = count / 1e3; - return thousands.toFixed(0).toString() + "k"; - } else if (count < 1e8) { - let millions = count / 1e6; - return millions.toFixed(1).toString() + "M"; - } else if (count < 1e9) { - let millions = count / 1e6; - return millions.toFixed(0).toString() + "M"; - } else { - let billions = count / 1e9; - return billions.toFixed(1).toString() + "B"; - } - } - - setNotificationBadge(count) { - this._notificationBadgeCount = count; - let text = this._notificationBadgeCountToText(count); - this._notificationBadgeLabel.set_text(text); - } - - toggleNotificationBadge(activate) { - if (activate && this._notificationBadgeCount > 0) { - this.updateNotificationBadge(); - this._notificationBadgeBin.show(); - } - else - this._notificationBadgeBin.hide(); - } - - _showProgressOverlay() { - if (this._progressOverlayArea) { - this._updateProgressOverlay(); - return; - } - - this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true}); - this._progressOverlayArea.add_style_class_name('progress-bar'); - this._progressOverlayArea.connect('repaint', () => { - this._drawProgressOverlay(this._progressOverlayArea); - }); - - this._source._iconContainer.add_child(this._progressOverlayArea); - let node = this._progressOverlayArea.get_theme_node(); - - let [hasColor, color] = node.lookup_color('-progress-bar-background', false); - if (hasColor) - this._progressbar_background = color - else - this._progressbar_background = new Utils.ColorUtils.Color({red: 204, green: 204, blue: 204, alpha: 255}); - - [hasColor, color] = node.lookup_color('-progress-bar-border', false); - if (hasColor) - this._progressbar_border = color; - else - this._progressbar_border = new Utils.ColorUtils.Color({red: 230, green: 230, blue: 230, alpha: 255}); - - this._updateProgressOverlay(); - } - - _hideProgressOverlay() { - if (this._progressOverlayArea) - this._progressOverlayArea.destroy(); - - this._progressOverlayArea = null; - this._progressbar_background = null; - this._progressbar_border = null; - } - - _updateProgressOverlay() { - - if (this._progressOverlayArea) { - this._progressOverlayArea.queue_repaint(); - } - } - - _drawProgressOverlay(area) { - let scaleFactor = Utils.getScaleFactor(); - let [surfaceWidth, surfaceHeight] = area.get_surface_size(); - let cr = area.get_context(); - - let iconSize = this._source.icon.iconSize * scaleFactor; - - let x = Math.floor((surfaceWidth - iconSize) / 2); - let y = Math.floor((surfaceHeight - iconSize) / 2); - - let lineWidth = Math.floor(1.0 * scaleFactor); - let padding = Math.floor(iconSize * 0.05); - let width = iconSize - 2.0*padding; - let height = Math.floor(Math.min(18.0*scaleFactor, 0.20*iconSize)); - x += padding; - y += iconSize - height - padding; - - cr.setLineWidth(lineWidth); - - // Draw the outer stroke - let stroke = new Cairo.LinearGradient(0, y, 0, y + height); - let fill = null; - stroke.addColorStopRGBA(1.0, 1.0, 1.0, 1.0, 0.1); - stroke.addColorStopRGBA(1.0, 1.0, 1.0, 1.0, 0.1); - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill); - - // Draw the background - x += lineWidth; - y += lineWidth; - width -= 2.0*lineWidth; - height -= 2.0*lineWidth; - - stroke = null; - fill = new Cairo.LinearGradient(0, y, 0, y + height); - fill.addColorStopRGBA(0.0, 0.0, 0.0, 0.0, 1.0); - fill.addColorStopRGBA(0.0, 0.0, 0.0, 0.0, 1.0); - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, width, height, true, true, stroke, fill); - - // Draw the finished bar - x += lineWidth; - y += lineWidth; - width -= 2.0*lineWidth; - height -= 2.0*lineWidth; - - let finishedWidth = Math.ceil(this._progress * width); - - let bg = this._progressbar_background; - let bd = this._progressbar_border; - - stroke = Cairo.SolidPattern.createRGBA(bd.red/255, bd.green/255, bd.blue/255, bd.alpha/255); - fill = Cairo.SolidPattern.createRGBA(bg.red/255, bg.green/255, bg.blue/255, bg.alpha/255); - - if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) - Utils.drawRoundedLine(cr, x + lineWidth/2.0 + width - finishedWidth, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill); - else - Utils.drawRoundedLine(cr, x + lineWidth/2.0, y + lineWidth/2.0, finishedWidth, height, true, true, stroke, fill); - - cr.$dispose(); - } - - setProgress(progress) { - this._progress = Math.min(Math.max(progress, 0.0), 1.0); - this._updateProgressOverlay(); - } - - toggleProgressOverlay(activate) { - if (activate) { - this._showProgressOverlay(); - } - else { - this._hideProgressOverlay(); - } - } - - insertEntry(appProgress) { - if (!appProgress || this._progressManagerEntries.indexOf(appProgress) !== -1) - return; - - this._progressManagerEntries.push(appProgress); - this._selectEntry(appProgress); - } - - removeEntry(appProgress) { - if (!appProgress || this._progressManagerEntries.indexOf(appProgress) == -1) - return; - - this._progressManagerEntries.splice(this._progressManagerEntries.indexOf(appProgress), 1); - - if (this._progressManagerEntries.length > 0) { - this._selectEntry(this._progressManagerEntries[this._progressManagerEntries.length-1]); - } else { - this.setNotificationBadge(0); - this.toggleNotificationBadge(false); - this.setProgress(0); - this.toggleProgressOverlay(false); - this.setUrgent(false); - } - } - - _selectEntry(appProgress) { - if (!appProgress) - return; - - this._signalsHandler.removeWithLabel('progress-entry'); - - this._signalsHandler.addWithLabel('progress-entry', - [ - appProgress, - 'count-changed', - (appProgress, value) => { - this.setNotificationBadge(value); - } - ], [ - appProgress, - 'count-visible-changed', - (appProgress, value) => { - this.toggleNotificationBadge(value); - } - ], [ - appProgress, - 'progress-changed', - (appProgress, value) => { - this.setProgress(value); - } - ], [ - appProgress, - 'progress-visible-changed', - (appProgress, value) => { - this.toggleProgressOverlay(value); - } - ], [ - appProgress, - 'urgent-changed', - (appProgress, value) => { - this.setUrgent(value) - } - ]); - - this.setNotificationBadge(appProgress.count()); - this.toggleNotificationBadge(appProgress.countVisible()); - this.setProgress(appProgress.progress()); - this.toggleProgressOverlay(appProgress.progressVisible()); - - this._isUrgent = false; - } - - setUrgent(urgent) { - const icon = this._source.icon._iconBin; - if (urgent) { - if (!this._isUrgent) { - icon.set_pivot_point(0.5, 0.5); - this._source.iconAnimator.addAnimation(icon, 'dance'); - this._isUrgent = true; - } - } else { - if (this._isUrgent) { - this._source.iconAnimator.removeAnimation(icon, 'dance'); - this._isUrgent = false; - } - icon.rotation_angle_z = 0; - } - } -}; diff --git a/proximity.js b/proximity.js deleted file mode 100644 index 29d7492..0000000 --- a/proximity.js +++ /dev/null @@ -1,260 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Panel extension - */ - -import Meta from 'gi://Meta'; -import Mtk from 'gi://Mtk'; - -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; - -import * as Utils from './utils.js'; - -//timeout intervals -const MIN_UPDATE_MS = 200; - -//timeout names -const T1 = 'limitUpdateTimeout'; - -export const Mode = { - ALL_WINDOWS: 0, - FOCUSED_WINDOWS: 1, - MAXIMIZED_WINDOWS: 2 -}; - -export class ProximityWatch { - - constructor(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { - this.actor = actor; - this.monitorIndex = monitorIndex - this.overlap = false; - this.mode = mode; - this.threshold = [xThreshold, yThreshold]; - this.handler = handler; - - this._allocationChangedId = actor.connect('notify::allocation', () => this._updateWatchRect()); - - this._updateWatchRect(); - } - - destroy() { - this.actor.disconnect(this._allocationChangedId); - } - - _updateWatchRect() { - let [actorX, actorY] = this.actor.get_position(); - - this.rect = new Mtk.Rectangle({ - x: actorX - this.threshold[0], - y: actorY - this.threshold[1], - width: this.actor.width + this.threshold[0] * 2, - height: this.actor.height + this.threshold[1] * 2 - }); - } -}; - -export const ProximityManager = class { - - constructor() { - this._counter = 1; - this._watches = {}; - this._focusedWindowInfo = null; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); - - this._bindSignals(); - this._setFocusedWindow(); - } - - createWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { - let watch = new ProximityWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler); - - this._watches[this._counter] = watch; - this.update(); - - return this._counter++; - } - - removeWatch(id) { - if (this._watches[id]) { - this._watches[id].destroy(); - delete this._watches[id]; - } - } - - update() { - this._queueUpdate(true); - } - - destroy() { - this._signalsHandler.destroy(); - this._timeoutsHandler.destroy(); - this._disconnectFocusedWindow(); - Object.keys(this._watches).forEach(id => this.removeWatch(id)); - } - - _bindSignals() { - this._signalsHandler.add( - [ - global.window_manager, - 'switch-workspace', - () => this._queueUpdate() - ], - [ - Main.overview, - 'hidden', - () => this._queueUpdate() - ], - [ - global.display, - 'notify::focus-window', - () => { - this._setFocusedWindow(); - this._queueUpdate(); - } - ], - [ - global.display, - 'restacked', - () => this._queueUpdate() - ] - ); - } - - _setFocusedWindow() { - this._disconnectFocusedWindow(); - - let focusedWindow = global.display.focus_window; - - if (focusedWindow) { - let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow); - - if (focusedWindowInfo && this._checkIfHandledWindowType(focusedWindowInfo.metaWindow)) { - focusedWindowInfo.allocationId = focusedWindowInfo.window.connect('notify::allocation', () => this._queueUpdate()); - focusedWindowInfo.destroyId = focusedWindowInfo.window.connect('destroy', () => this._disconnectFocusedWindow(true)); - - this._focusedWindowInfo = focusedWindowInfo; - } - } - } - - _getFocusedWindowInfo(focusedWindow) { - let window = focusedWindow.get_compositor_private(); - let focusedWindowInfo; - - if (window) { - focusedWindowInfo = { window: window }; - focusedWindowInfo.metaWindow = focusedWindow; - - if (focusedWindow.is_attached_dialog()) { - let mainMetaWindow = focusedWindow.get_transient_for(); - - if (focusedWindowInfo.metaWindow.get_frame_rect().height < mainMetaWindow.get_frame_rect().height) { - focusedWindowInfo.window = mainMetaWindow.get_compositor_private(); - focusedWindowInfo.metaWindow = mainMetaWindow; - } - } - } - - return focusedWindowInfo; - } - - _disconnectFocusedWindow(destroy) { - if (this._focusedWindowInfo && !destroy) { - this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.allocationId); - this._focusedWindowInfo.window.disconnect(this._focusedWindowInfo.destroyId); - } - - this._focusedWindowInfo = null; - } - - _getHandledWindows() { - return Utils.getCurrentWorkspace() - .list_windows() - .filter(mw => this._checkIfHandledWindow(mw)); - } - - _checkIfHandledWindow(metaWindow) { - return metaWindow && - !metaWindow.minimized && - !metaWindow.customJS_ding && - metaWindow.window_type != Meta.WindowType.DESKTOP && - this._checkIfHandledWindowType(metaWindow); - } - - _checkIfHandledWindowType(metaWindow) { - let metaWindowType = metaWindow.get_window_type(); - - //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html - return metaWindowType <= Meta.WindowType.SPLASHSCREEN && - metaWindowType != Meta.WindowType.DESKTOP; - } - - _queueUpdate(noDelay) { - if (!noDelay && this._timeoutsHandler.getId(T1)) { - //limit the number of updates - this._pendingUpdate = true; - return; - } - - this._timeoutsHandler.add([T1, MIN_UPDATE_MS, () => this._endLimitUpdate()]); - - let metaWindows = this._getHandledWindows(); - - Object.keys(this._watches).forEach(id => { - let watch = this._watches[id]; - let overlap = !!this._update(watch, metaWindows); - - if (overlap !== watch.overlap) { - watch.handler(overlap); - watch.overlap = overlap; - } - }); - } - - _endLimitUpdate() { - if (this._pendingUpdate) { - this._pendingUpdate = false; - this._queueUpdate(); - } - } - - _update(watch, metaWindows) { - if (watch.mode === Mode.FOCUSED_WINDOWS) - return (this._focusedWindowInfo && - this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && - this._checkProximity(this._focusedWindowInfo.metaWindow, watch)); - - if (watch.mode === Mode.MAXIMIZED_WINDOWS) - return metaWindows.some(mw => mw.maximized_vertically && mw.maximized_horizontally && - mw.get_monitor() == watch.monitorIndex); - - //Mode.ALL_WINDOWS - return metaWindows.some(mw => this._checkProximity(mw, watch)); - } - - _checkProximity(metaWindow, watch) { - let windowRect = metaWindow.get_frame_rect(); - - return windowRect.overlap(watch.rect) && - ((!watch.threshold[0] && !watch.threshold[1]) || - metaWindow.get_monitor() == watch.monitorIndex || - windowRect.overlap(global.display.get_monitor_geometry(watch.monitorIndex))); - } -}; diff --git a/schemas/org.gnome.shell.extensions.zorin-taskbar.gschema.xml b/schemas/org.gnome.shell.extensions.zorin-taskbar.gschema.xml index eb5b221..6570540 100644 --- a/schemas/org.gnome.shell.extensions.zorin-taskbar.gschema.xml +++ b/schemas/org.gnome.shell.extensions.zorin-taskbar.gschema.xml @@ -18,6 +18,7 @@ + @@ -71,7 +72,7 @@ '{}' - Percentages of screen edge for panel to span + Percentages of screen edge for panel to span, -1 for dynamic length (dock mode) Length of the panels, in percent (JSON). @@ -99,11 +100,6 @@ Style of the running indicator (unfocused) Style of the running indicator for the icon for applications which are not currently focused - - false - Running indicator dominant color - Whether to use the app icon's dominant color for .app-well-running-dot - false Keep top panel @@ -119,6 +115,10 @@ Lock the taskbar Specifies if the user can modify the taskbar + + 0 + Panel margin + false Custom background color @@ -171,9 +171,13 @@ false - Intellihide pressure + Allow revealing the panel while in fullscreen Allow the panel to be revealed while an application is in fullscreen mode + + false + Reveal the panel on notification + false Intellihide only secondary @@ -189,10 +193,9 @@ Keybinding toggle intellihide Keybinding to reveal the panel while in intellihide mode - - false - Floating rounded theme - Display the panel with a floating rounded theme + + -1 + Persisted intellihide hold status. -1 means the option is disabled [] @@ -279,11 +282,6 @@ Display panels on all monitors Specifies if a panel is shown on every monitors - - [] - Available monitors - Available gnome-shell (Mutter) monitors, internal use - false Provide monitor isolation @@ -292,7 +290,7 @@ true Display the favorites on all monitors - Specifies if every panel should display the favorite applications. If false, the favorite appplications are only displayed on the primary monitor. + Specifies if every panel should display the favorite applications. If false, the favorite applications are only displayed on the primary monitor. true @@ -337,6 +335,10 @@ Middle click preview to close window Middle click on the window preview to close that window + + 0 + Border radius of panel elements + "<Super>q" Keybinding to show the dock and the number overlay. @@ -802,5 +804,9 @@ Show badge count on app icon Whether to show badge count overlay on app icon, for supported applications. + + '' + The preferences page name to display + diff --git a/src/appIcons.js b/src/appIcons.js new file mode 100644 index 0000000..40b4f8e --- /dev/null +++ b/src/appIcons.js @@ -0,0 +1,2030 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import Clutter from 'gi://Clutter' +import GLib from 'gi://GLib' +import Gio from 'gi://Gio' +import Graphene from 'gi://Graphene' +import GObject from 'gi://GObject' +import Mtk from 'gi://Mtk' +import Shell from 'gi://Shell' +import St from 'gi://St' + +import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js' +import * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import * as Util from 'resource:///org/gnome/shell/misc/util.js' +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' + +import { Hold } from './intellihide.js' +import * as Utils from './utils.js' +import * as PanelSettings from './panelSettings.js'; +import * as Taskbar from './taskbar.js' +import { + DTP_EXTENSION, + SETTINGS, + DESKTOPSETTINGS, + EXTENSION_PATH, + tracker, +} from './extension.js' +import { + gettext as _, + ngettext, +} from 'resource:///org/gnome/shell/extensions/extension.js' + +//timeout names +const T2 = 'mouseScrollTimeout' +const T3 = 'showDotsTimeout' +const T4 = 'overviewWindowDragEndTimeout' +const T5 = 'switchWorkspaceTimeout' +const T6 = 'displayProperIndicatorTimeout' + +const SCROLL_ICON_DELAY = 400 +const DOT_SIZE = 3 + +//right padding defined for .overview-label in stylesheet.css +const TITLE_RIGHT_PADDING = 8 +const DOUBLE_CLICK_DELAY_MS = 450 + +let LABEL_GAP = 5 +let MAX_INDICATORS = 4 +export const DEFAULT_PADDING_SIZE = 0 + +let DOT_STYLE = { + DOTS: 'DOTS', + SQUARES: 'SQUARES', + DASHES: 'DASHES', + SEGMENTED: 'SEGMENTED', + CILIORA: 'CILIORA', + METRO: 'METRO', + SOLID: 'SOLID', +} + +let DOT_POSITION = { + TOP: 'TOP', + BOTTOM: 'BOTTOM', + LEFT: 'LEFT', + RIGHT: 'RIGHT', +} + +let recentlyClickedAppLoopId = 0 +let recentlyClickedApp = null +let recentlyClickedAppWindows = null +let recentlyClickedAppIndex = 0 +let recentlyClickedAppMonitorIndex + +/** + * Extend AppIcon + * + * - Apply a css class based on the number of windows of each application (#N); + * - Draw a dot for each window of the application based on the default "dot" style which is hidden (#N); + * a class of the form "running#N" is applied to the AppWellIcon actor. + * like the original .running one. + * - add a .focused style to the focused app + * - Customize click actions. + * - Update minimization animation target + * + */ + +export const TaskbarAppIcon = GObject.registerClass( + {}, + class TaskbarAppIcon extends AppDisplay.AppIcon { + _init(appInfo, panel, iconParams, previewMenu, iconAnimator) { + this.dtpPanel = panel + this._nWindows = 0 + this.window = appInfo.window + this.isLauncher = appInfo.isLauncher + this._previewMenu = previewMenu + this.iconAnimator = iconAnimator + this.lastClick = 0 + this._appicon_normalstyle = '' + this._appicon_hoverstyle = '' + this._appicon_pressedstyle = '' + + super._init(appInfo.app, iconParams) + + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + // Fix touchscreen issues before the listener is added by the parent constructor. + this._onTouchEvent = function (actor, event) { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) { + // Open the popup menu on long press. + this._setPopupTimeout() + } else if ( + this._menuTimeoutId != 0 && + (event.type() == Clutter.EventType.TOUCH_END || + event.type() == Clutter.EventType.TOUCH_CANCEL) + ) { + // Activate/launch the application. + this.activate(1) + this._removeMenuTimeout() + } + // Disable dragging via touch screen as it's buggy as hell. Not perfect for tablet users, but the alternative is way worse. + // Also, EVENT_PROPAGATE launches applications twice with this solution, so this.activate(1) above must only be called if there's already a window. + return Clutter.EVENT_STOP + } + // Hack for missing TOUCH_END event. + this._onLeaveEvent = function () { + this.fake_release() + if (this._menuTimeoutId != 0) this.activate(1) // Activate/launch the application if TOUCH_END didn't fire. + this._removeMenuTimeout() + } + + this._dot.set_width(0) + this._isGroupApps = SETTINGS.get_boolean('group-apps') + + this._container = new St.Widget({ + style_class: 'dtp-container', + layout_manager: new Clutter.BinLayout(), + }) + this._dotsContainer = new St.Widget({ + style_class: 'dtp-dots-container', + layout_manager: new Clutter.BinLayout(), + }) + this._dtpIconContainer = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + style: getIconContainerStyle(panel.checkIfVertical()), + }) + + this.remove_child(this._iconContainer) + this.icon._iconBin.set_pivot_point(0.5, 0.5) + + this._dtpIconContainer.add_child(this._iconContainer) + + if (appInfo.window) { + let box = Utils.createBoxLayout() + + this._windowTitle = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + x_align: Clutter.ActorAlign.START, + style_class: 'overview-label', + }) + + this._updateWindowTitle() + this._updateWindowTitleStyle() + + box.add_child(this._dtpIconContainer) + box.add_child(this._windowTitle) + + this._dotsContainer.add_child(box) + } else { + this._dotsContainer.add_child(this._dtpIconContainer) + } + + this._container.add_child(this._dotsContainer) + this.set_child(this._container) + + if (panel.checkIfVertical()) { + this.set_width(panel.geom.innerSize) + } + + // Monitor windows-changes instead of app state. + // Keep using the same Id and function callback (that is extended) + if (this._stateChangedId > 0) { + this.app.disconnect(this._stateChangedId) + this._stateChangedId = 0 + } + + this._setAppIconPadding() + this._showDots() + this._numberOverlay() + + this._signalsHandler.add( + [ + this, + 'notify::mapped', + () => + this.mapped && !this.dtpPanel.intellihide.enabled + ? this._handleNotifications() + : null, + ], + [ + Utils.getStageTheme(), + 'changed', + this._updateWindowTitleStyle.bind(this), + ], + [ + global.display, + 'notify::focus-window', + this._onFocusAppChanged.bind(this), + ], + [this.app, 'windows-changed', this.onWindowsChanged.bind(this)], + ) + + if (!this.window) { + if (SETTINGS.get_boolean('isolate-monitors')) { + this._signalsHandler.add([ + Utils.DisplayWrapper.getScreen(), + ['window-entered-monitor', 'window-left-monitor'], + this.onWindowEnteredOrLeft.bind(this), + ]) + } + + this._signalsHandler.add([ + Utils.DisplayWrapper.getScreen(), + 'in-fullscreen-changed', + () => { + if ( + global.display.focus_window?.get_monitor() == + this.dtpPanel.monitor.index && + !this.dtpPanel.monitor.inFullscreen + ) { + this._resetDots(true) + this._displayProperIndicator() + } + }, + ]) + } else { + this._signalsHandler.add( + [this.window, 'notify::title', this._updateWindowTitle.bind(this)], + [ + this.window, + 'notify::minimized', + this._updateWindowTitleStyle.bind(this), + ], + ) + } + + this._signalsHandler.add( + [this, 'scroll-event', this._onMouseScroll.bind(this)], + [ + Main.overview, + 'window-drag-end', + this._onOverviewWindowDragEnd.bind(this), + ], + [ + global.window_manager, + 'switch-workspace', + this._onSwitchWorkspace.bind(this), + ], + [ + this, + 'notify::hover', + () => { + this._onAppIconHoverChanged() + this._onAppIconHoverChanged_GtkWorkaround() + }, + ], + [ + this, + 'notify::pressed', + this._onAppIconPressedChanged_GtkWorkaround.bind(this), + ], + [ + this.dtpPanel.panelManager.notificationsMonitor, + `update-${this.app.id}`, + this._handleNotifications.bind(this), + ], + [ + SETTINGS, + 'changed::progress-show-count', + this._handleNotifications.bind(this), + ], + [ + SETTINGS, + [ + 'changed::panel-positions', + 'changed::dot-style-focused', + 'changed::dot-style-unfocused', + ], + this._settingsChangeRefresh.bind(this), + ], + [ + SETTINGS, + [ + 'changed::group-apps-label-max-width', + 'changed::group-apps-use-fixed-width', + ], + this._updateWindowTitleStyle.bind(this), + ], + ) + } + + getDragActor() { + return this.app.create_icon_texture(this.dtpPanel.taskbar.iconSize) + } + + shouldShowTooltip() { + if ( + !SETTINGS.get_boolean('show-tooltip') || + (!this.isLauncher && + SETTINGS.get_boolean('show-window-previews') && + this.getAppIconInterestingWindows().length > 0) + ) { + return false + } else { + return ( + this.hover && + !this.window && + (!this._menu || !this._menu.isOpen) && + this._previewMenu.getCurrentAppIcon() !== this + ) + } + } + + _onAppIconHoverChanged() { + if ( + !SETTINGS.get_boolean('show-window-previews') || + (!this.window && !this._nWindows) + ) { + return + } + + if (this.hover) { + this._previewMenu.requestOpen(this) + } else { + this._previewMenu.requestClose() + } + } + + _onDestroy() { + super._onDestroy() + + this._timeoutsHandler.destroy() + this._signalsHandler.destroy() + + this._previewMenu.close(true) + } + + onWindowsChanged() { + this._updateWindows() + this.updateIcon() + + if (this._isGroupApps) this._setIconStyle() + } + + onWindowEnteredOrLeft(display, number, metaWindow) { + if (number > 0 && tracker.get_window_app(metaWindow) == this.app) { + this._updateWindows() + this._displayProperIndicator() + } + } + + updateTitleStyle() { + this._updateWindowTitleStyle() + } + + // Update indicator and target for minimization animation + updateIcon() { + // If (for unknown reason) the actor is not on the stage the reported size + // and position are random values, which might exceeds the integer range + // resulting in an error when assigned to the a rect. This is a more like + // a workaround to prevent flooding the system with errors. + if (this.get_stage() == null) return + + let rect = new Mtk.Rectangle() + + ;[rect.x, rect.y] = this.get_transformed_position() + ;[rect.width, rect.height] = this.get_transformed_size() + + let windows = this.window + ? [this.window] + : this.getAppIconInterestingWindows(true) + windows.forEach(function (w) { + w.set_icon_geometry(rect) + }) + } + + _onAppIconHoverChanged_GtkWorkaround() { + if (this.hover && this._appicon_hoverstyle) { + this._container.set_style(this._appicon_hoverstyle) + } else if (this._appicon_normalstyle) { + this._container.set_style(this._appicon_normalstyle) + } else { + this._container.set_style('') + } + } + + _onAppIconPressedChanged_GtkWorkaround() { + if (this.pressed && this._appicon_pressedstyle) { + this._container.set_style(this._appicon_pressedstyle) + } else if (this.hover && this._appicon_hoverstyle) { + this._container.set_style(this._appicon_hoverstyle) + } else if (this._appicon_normalstyle) { + this._container.set_style(this._appicon_normalstyle) + } else { + this._container.set_style('') + } + } + + _onMouseScroll(actor, event) { + let scrollAction = SETTINGS.get_string('scroll-icon-action') + + if ( + scrollAction === 'NOTHING' || + (!this.window && !this._nWindows) + ) { + return + } + + let direction = Utils.getMouseScrollDirection(event) + + if (direction && !this._timeoutsHandler.getId(T2)) { + this._timeoutsHandler.add([ + T2, + SCROLL_ICON_DELAY, + () => {}, + ]) + + let windows = this.getAppIconInterestingWindows() + + windows.sort(Taskbar.sortWindowsCompareFunction) + Utils.activateSiblingWindow(windows, direction, this.window) + } + } + + _showDots() { + // Just update style if dots already exist + if (this._focusedDots && this._unfocusedDots) { + this._updateWindows() + return + } + + if (!this._isGroupApps) { + this._focusedDots = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + x_expand: true, + y_expand: true, + visible: false, + }) + + let mappedId = this.connect('notify::mapped', () => { + this._displayProperIndicator() + this.disconnect(mappedId) + }) + } else { + ;(this._focusedDots = new St.DrawingArea()), + (this._unfocusedDots = new St.DrawingArea()) + + this._focusedDots.connect('repaint', () => { + if (!this._dashItemContainer.animatingOut) + // don't draw and trigger more animations if the icon is in the middle of + // being removed from the panel + this._drawRunningIndicator( + this._focusedDots, + SETTINGS.get_string('dot-style-focused'), + true, + ) + }) + + this._unfocusedDots.connect('repaint', () => { + if (!this._dashItemContainer.animatingOut) + this._drawRunningIndicator( + this._unfocusedDots, + SETTINGS.get_string('dot-style-unfocused'), + false, + ) + }) + + this._dotsContainer.add_child(this._unfocusedDots) + + this._updateWindows() + + this._timeoutsHandler.add([ + T3, + 0, + () => { + this._resetDots() + this._displayProperIndicator() + }, + ]) + } + + this._dotsContainer.add_child(this._focusedDots) + } + + _resetDots(ignoreSizeReset) { + let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index) + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM + let sizeProp = isHorizontalDots ? 'width' : 'height' + let focusedDotStyle = SETTINGS.get_string('dot-style-focused') + let unfocusedDotStyle = SETTINGS.get_string('dot-style-unfocused') + + this._focusedIsWide = this._isWideDotStyle(focusedDotStyle) + this._unfocusedIsWide = this._isWideDotStyle(unfocusedDotStyle) + ;[, this._containerSize] = + this._container[`get_preferred_${sizeProp}`](-1) + + if (!ignoreSizeReset) { + ;[this._focusedDots, this._unfocusedDots].forEach((d) => { + d.set_size(-1, -1) + d.x_expand = d.y_expand = false + + d[sizeProp] = 1 + d[(isHorizontalDots ? 'y' : 'x') + '_expand'] = true + }) + } + } + + _settingsChangeRefresh() { + if (this._isGroupApps) { + this._updateWindows() + this._resetDots() + this._focusedDots.queue_repaint() + this._unfocusedDots.queue_repaint() + } + + this._displayProperIndicator() + } + + _updateWindowTitleStyle() { + if (this._windowTitle) { + let useFixedWidth = SETTINGS.get_boolean('group-apps-use-fixed-width') + let scaleFactor = Utils.getScaleFactor() + let maxLabelWidth = + SETTINGS.get_int('group-apps-label-max-width') * scaleFactor + let variableWidth = + !useFixedWidth || + this.dtpPanel.checkIfVertical() || + this.dtpPanel.taskbar.fullScrollView + + this._windowTitle[maxLabelWidth > 0 ? 'show' : 'hide']() + this._windowTitle.set_width( + variableWidth + ? -1 + : maxLabelWidth + TITLE_RIGHT_PADDING * scaleFactor, + ) + + this._windowTitle.clutter_text.natural_width = useFixedWidth + ? maxLabelWidth + : 0 + this._windowTitle.clutter_text.natural_width_set = useFixedWidth + + this._windowTitle.set_style( + useFixedWidth ? '' : 'max-width: ' + maxLabelWidth + 'px;' + ) + } + } + + _updateWindowTitle() { + if (this._windowTitle.text != this.window.title) { + this._windowTitle.text = ( + this.window.title ? this.window.title : this.app.get_name() + ) + .replace(/\r?\n|\r/g, '') + .trim() + + if (this._focusedDots) { + this._displayProperIndicator() + } + } + } + + _setIconStyle(isFocused) { + let inlineStyle = 'margin: 0;' + + if (this._dotsContainer.get_style() != inlineStyle) { + this._dotsContainer.set_style(inlineStyle) + } + } + + _checkIfFocusedApp() { + return tracker.focus_app == this.app + } + + _checkIfMonitorHasFocus() { + return ( + global.display.focus_window && + (!SETTINGS.get_boolean('multi-monitors') || // only check same monitor index if multi window is enabled. + !SETTINGS.get_boolean('isolate-monitors') || + global.display.focus_window.get_monitor() === + this.dtpPanel.monitor.index) + ) + } + + _setAppIconPadding() { + const padding = getIconPadding(this.dtpPanel) + const margin = Taskbar.APPICON_MARGIN + let vertical = this.dtpPanel.checkIfVertical() + + this.set_style( + `padding: ${vertical ? margin : 0}px ${vertical ? 0 : margin}px;`, + ) + this._iconContainer.set_style('padding: ' + padding + 'px;') + } + + popupMenu() { + this._removeMenuTimeout() + this.fake_release() + + if (!this._menu) { + this._menu = new TaskbarSecondaryMenu(this, this.dtpPanel.geom.position) + this._menu.setApp(this.app) + + this._signalsHandler.add( + [ + this._menu, + 'open-state-changed', + (menu, isPoppedUp) => { + if (!isPoppedUp) this._onMenuPoppedDown() + else this._previewMenu.close(true) + }, + ], + [Main.overview, 'hiding', () => this._menu.close()], + ) + + // We want to keep the item hovered while the menu is up + this._menu.blockSourceEvents = true + + Main.uiGroup.add_child(this._menu.actor) + this._menuManager.addMenu(this._menu) + } + this._menu.updateQuitText() + + this.emit('menu-state-changed', true) + + this.set_hover(true) + this._menu.open(BoxPointer.PopupAnimation.FULL) + this._menuManager.ignoreRelease() + this.emit('sync-tooltip') + + return false + } + + _onFocusAppChanged() { + this._displayProperIndicator() + } + + _onOverviewWindowDragEnd() { + this._timeoutsHandler.add([ + T4, + 0, + () => { + if (SETTINGS.get_boolean('isolate-workspaces')) this._updateWindows() + + this._displayProperIndicator() + }, + ]) + } + + _onSwitchWorkspace() { + if (this._isGroupApps) { + this._timeoutsHandler.add([T5, 0, () => this._displayProperIndicator()]) + } else { + this._displayProperIndicator() + } + } + + _displayProperIndicator() { + let isFocused = this._isFocusedWindow() + let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index) + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM + + this._setIconStyle(isFocused) + + if (!this._isGroupApps) { + if ( + this.window && + isFocused + ) { + let align = + Clutter.ActorAlign[ + position == DOT_POSITION.TOP || position == DOT_POSITION.LEFT + ? 'START' + : 'END' + ] + + this._focusedDots.set_size(0, 0) + this._focusedDots[isHorizontalDots ? 'height' : 'width'] = + this._getRunningIndicatorSize() + + this._focusedDots.y_align = this._focusedDots.x_align = + Clutter.ActorAlign.FILL + this._focusedDots[(isHorizontalDots ? 'y' : 'x') + '_align'] = align + this._focusedDots.background_color = + this._getRunningIndicatorColor(isFocused) + this._focusedDots.show() + } else if (this._focusedDots.visible) { + this._focusedDots.hide() + } + } else { + let sizeProp = isHorizontalDots ? 'width' : 'height' + let newFocusedDotsSize = 0 + let newFocusedDotsOpacity = 0 + let newUnfocusedDotsSize = 0 + let newUnfocusedDotsOpacity = 0 + + isFocused = this._checkIfFocusedApp() && this._checkIfMonitorHasFocus() + + this._timeoutsHandler.add([ + T6, + 0, + () => { + if (isFocused) this.add_style_class_name('focused') + else this.remove_style_class_name('focused') + }, + ]) + + if (this._focusedIsWide) { + newFocusedDotsSize = + isFocused && this._nWindows > 0 ? this._containerSize : 0 + newFocusedDotsOpacity = 255 + } else { + newFocusedDotsSize = this._containerSize + newFocusedDotsOpacity = isFocused && this._nWindows > 0 ? 255 : 0 + } + + if (this._unfocusedIsWide) { + newUnfocusedDotsSize = + !isFocused && this._nWindows > 0 ? this._containerSize : 0 + newUnfocusedDotsOpacity = 255 + } else { + newUnfocusedDotsSize = this._containerSize + newUnfocusedDotsOpacity = !isFocused && this._nWindows > 0 ? 255 : 0 + } + + // Only animate if... + // going from a wide style to a narrow style indicator or vice-versa + // OR going from an open app to a closed app or vice versa + let animate = + this._focusedIsWide != this._unfocusedIsWide || + this._focusedDots[sizeProp] != newUnfocusedDotsSize || + this._unfocusedDots[sizeProp] != newFocusedDotsSize + let duration = animate ? Taskbar.DASH_ANIMATION_TIME : 0.001 + + this._animateDotDisplay( + this._focusedDots, + newFocusedDotsSize, + this._unfocusedDots, + newUnfocusedDotsOpacity, + sizeProp, + duration, + ) + this._animateDotDisplay( + this._unfocusedDots, + newUnfocusedDotsSize, + this._focusedDots, + newFocusedDotsOpacity, + sizeProp, + duration, + ) + } + } + + _animateDotDisplay( + dots, + newSize, + otherDots, + newOtherOpacity, + sizeProp, + duration, + ) { + Utils.stopAnimations(dots) + + let tweenOpts = { + time: duration, + transition: 'easeInOutCubic', + onComplete: () => { + if (newOtherOpacity > 0) otherDots.opacity = newOtherOpacity + }, + } + + if (newOtherOpacity == 0) otherDots.opacity = newOtherOpacity + + tweenOpts[sizeProp] = newSize + + Utils.animate(dots, tweenOpts) + } + + _isFocusedWindow() { + let focusedWindow = global.display.focus_window + + while (focusedWindow) { + if (focusedWindow == this.window) { + return true + } + + focusedWindow = focusedWindow.get_transient_for() + } + + return false + } + + _isWideDotStyle(dotStyle) { + return ( + dotStyle == DOT_STYLE.SEGMENTED || + dotStyle == DOT_STYLE.CILIORA || + dotStyle == DOT_STYLE.METRO || + dotStyle == DOT_STYLE.SOLID + ) + } + + _isThemeProvidingIndicator() { + // This is an attempt to determine if the theme is providing their own + // running indicator by way of a border image on the icon, for example in + // the theme Ciliora + return ( + this.icon.get_stage() && this.icon.get_theme_node().get_border_image() + ) + } + + activate(button, modifiers, handleAsGrouped) { + let event = Clutter.get_current_event() + + modifiers = event ? event.get_state() : modifiers || 0 + + // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.) + modifiers = + modifiers & + (Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK) + + let ctrlPressed = modifiers & Clutter.ModifierType.CONTROL_MASK + + if (ctrlPressed) { + // CTRL-click or hotkey with ctrl + return this._launchNewInstance(true) + } + + // We check what type of click we have and if the modifier SHIFT is + // being used. We then define what buttonAction should be for this + // event. + let buttonAction = 0 + let doubleClick + + if (button && button == 2) { + if (modifiers & Clutter.ModifierType.SHIFT_MASK) + buttonAction = SETTINGS.get_string('shift-middle-click-action') + else buttonAction = SETTINGS.get_string('middle-click-action') + } + // fixed issue #1676 by checking for button 0 or 1 to also handle touchscreen + // input, probably not the proper fix as i'm not aware button 0 should exist + // but from using this fix for months it seems to not create any issues + else if (button === 0 || button === 1) { + let now = global.get_current_time() + + doubleClick = now - this.lastClick < DOUBLE_CLICK_DELAY_MS + this.lastClick = now + + if (modifiers & Clutter.ModifierType.SHIFT_MASK) + buttonAction = SETTINGS.get_string('shift-click-action') + else buttonAction = SETTINGS.get_string('click-action') + } + + let closePreview = () => + this._previewMenu.close(true) + let appCount = this.getAppIconInterestingWindows().length + let previewedAppIcon = this._previewMenu.getCurrentAppIcon() + + if (this.window || buttonAction != 'TOGGLE-SHOWPREVIEW') closePreview() + + // We check if the app is running, and that the # of windows is > 0 in + // case we use workspace isolation, + let appIsRunning = + this.app.state == Shell.AppState.RUNNING && appCount > 0 + + // We customize the action only when the application is already running + if (appIsRunning && !this.isLauncher) { + if (this.window && !handleAsGrouped) { + //ungrouped applications behaviors + switch (buttonAction) { + case 'LAUNCH': + this._launchNewInstance() + break + + case 'QUIT': + this.window.delete(global.get_current_time()) + break + + default: + if ( + !Main.overview._shown && + (buttonAction == 'MINIMIZE' || + buttonAction == 'TOGGLE-SHOWPREVIEW' || + buttonAction == 'TOGGLE-CYCLE' || + buttonAction == 'TOGGLE-SPREAD' || + buttonAction == 'CYCLE-MIN') && + (this._isFocusedWindow() || + (buttonAction == 'MINIMIZE' && + (button == 2 || + modifiers & Clutter.ModifierType.SHIFT_MASK))) + ) { + this.window.minimize() + } else { + Main.activateWindow(this.window) + } + } + } else { + //grouped application behaviors + let monitor = this.dtpPanel.monitor + let appHasFocus = + this._checkIfFocusedApp() && this._checkIfMonitorHasFocus() + + switch (buttonAction) { + case 'RAISE': + activateAllWindows(this.app, monitor) + break + + case 'LAUNCH': + this._launchNewInstance() + break + + case 'MINIMIZE': + // In overview just activate the app, unless the acion is explicitely + // requested with a keyboard modifier + if (!Main.overview._shown || modifiers) { + // If we have button=2 or a modifier, allow minimization even if + // the app is not focused + if ( + appHasFocus || + button == 2 || + modifiers & Clutter.ModifierType.SHIFT_MASK + ) { + // minimize all windows on double click and always in the case of primary click without + // additional modifiers + let all_windows = (button == 1 && !modifiers) || doubleClick + minimizeWindow(this.app, all_windows, monitor) + } else activateAllWindows(this.app, monitor) + } else this.app.activate() + break + + case 'CYCLE': + if (!Main.overview._shown) { + if (appHasFocus) + cycleThroughWindows(this.app, false, false, monitor) + else { + activateFirstWindow(this.app, monitor) + } + } else this.app.activate() + break + case 'CYCLE-MIN': + if (!Main.overview._shown) { + if ( + appHasFocus || + (recentlyClickedApp == this.app && + recentlyClickedAppWindows[ + recentlyClickedAppIndex % recentlyClickedAppWindows.length + ] == 'MINIMIZE') + ) + cycleThroughWindows(this.app, false, true, monitor) + else { + activateFirstWindow(this.app, monitor) + } + } else this.app.activate() + break + case 'TOGGLE-SHOWPREVIEW': + if (!Main.overview._shown) { + if (appCount == 1) { + closePreview() + + if (appHasFocus) minimizeWindow(this.app, false, monitor) + else activateFirstWindow(this.app, monitor) + } else { + if (doubleClick) { + // minimize all windows if double clicked + closePreview() + minimizeWindow(this.app, true, monitor) + } else if (previewedAppIcon != this) { + this._previewMenu.open(this) + } + + this.emit('sync-tooltip') + } + } else this.app.activate() + break + case 'TOGGLE-CYCLE': + if (!Main.overview._shown) { + if (appCount == 1) { + if (appHasFocus) minimizeWindow(this.app, false, monitor) + else activateFirstWindow(this.app, monitor) + } else { + cycleThroughWindows(this.app, false, false, monitor) + } + } else this.app.activate() + break + case 'QUIT': + closeAllWindows(this.app, monitor) + break + case 'TOGGLE-SPREAD': + if (appCount == 1) { + if (appHasFocus && !Main.overview._shown) + minimizeWindow(this.app, false, monitor) + else activateFirstWindow(this.app, monitor) + } else + // return so the overview stays open if it already is + return this.dtpPanel.panelManager.showFocusedAppInOverview( + this.app, + ) + } + } + } else { + this._launchNewInstance() + } + + global.display.emit('grab-op-begin', null, null) + Main.overview.hide() + } + + _launchNewInstance(ctrlPressed) { + if ( + (ctrlPressed || this.app.state == Shell.AppState.RUNNING) && + this.app.can_open_new_window() + ) { + this.animateLaunch() + this.app.open_new_window(-1) + } else { + let windows = this.window ? [this.window] : this.app.get_windows() + + if (windows.length) { + Main.activateWindow(windows[0]) + } else { + this.animateLaunch() + this.app.activate() + } + } + } + + _updateWindows() { + let windows = [this.window] + + if (!this.window) { + windows = this.getAppIconInterestingWindows() + + this._nWindows = windows.length + + for (let i = 1; i <= MAX_INDICATORS; i++) { + let className = 'running' + i + if (i != this._nWindows) this.remove_style_class_name(className) + else this.add_style_class_name(className) + } + } + + this._previewMenu.update(this, windows) + } + + _getRunningIndicatorCount() { + return Math.min(this._nWindows, MAX_INDICATORS) + } + + _getRunningIndicatorSize() { + return DOT_SIZE * Utils.getScaleFactor() + } + + _getRunningIndicatorColor(isFocused) { + let color + const fallbackColor = new Utils.ColorUtils.Color({ + red: 82, + green: 148, + blue: 226, + alpha: 255, + }) + + // Re-use the style - background color, and border width and color - + // of the default dot + let themeNode = this._dot.get_theme_node() + color = themeNode.get_background_color() + + // theme didn't provide one, use a default + if (color.alpha == 0) color = fallbackColor + + return color + } + + _drawRunningIndicator(area, type, isFocused) { + let n = this._getRunningIndicatorCount() + + if (!n) { + return + } + + let position = PanelSettings.getPanelPosition(SETTINGS, this.dtpPanel.monitor.index) + let isHorizontalDots = + position == DOT_POSITION.TOP || position == DOT_POSITION.BOTTOM + let bodyColor = this._getRunningIndicatorColor(isFocused) + let [areaWidth, areaHeight] = area.get_surface_size() + let cr = area.get_context() + let size = this._getRunningIndicatorSize() + + let areaSize = areaWidth + let startX = 0 + let startY = 0 + + if (isHorizontalDots) { + if (position == DOT_POSITION.BOTTOM) { + startY = areaHeight - size + } + } else { + areaSize = areaHeight + + if (position == DOT_POSITION.RIGHT) { + startX = areaWidth - size + } + } + + if (type == DOT_STYLE.SOLID || type == DOT_STYLE.METRO) { + if (type == DOT_STYLE.SOLID || n <= 1) { + cr.translate(startX, startY) + cr.setSourceColor(bodyColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + [0, 0].concat( + isHorizontalDots ? [areaSize, size] : [size, areaSize], + ), + ) + cr.fill() + } else { + let blackenedLength = (1 / 48) * areaSize // need to scale with the SVG for the stacked highlight + let darkenedLength = isFocused + ? (2 / 48) * areaSize + : (10 / 48) * areaSize + let blackenedColor = new Utils.ColorUtils.Color({ + red: bodyColor.red * 0.3, + green: bodyColor.green * 0.3, + blue: bodyColor.blue * 0.3, + alpha: bodyColor.alpha, + }) + let darkenedColor = new Utils.ColorUtils.Color({ + red: bodyColor.red * 0.7, + green: bodyColor.green * 0.7, + blue: bodyColor.blue * 0.7, + alpha: bodyColor.alpha, + }) + let solidDarkLength = areaSize - darkenedLength + let solidLength = solidDarkLength - blackenedLength + + cr.translate(startX, startY) + + cr.setSourceColor(bodyColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + [0, 0].concat( + isHorizontalDots ? [solidLength, size] : [size, solidLength], + ), + ) + cr.fill() + cr.setSourceColor(blackenedColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + isHorizontalDots + ? [solidLength, 0, 1, size] + : [0, solidLength, size, 1], + ) + cr.fill() + cr.setSourceColor(darkenedColor) + cr.newSubPath() + cr.rectangle.apply( + cr, + isHorizontalDots + ? [solidDarkLength, 0, darkenedLength, size] + : [0, solidDarkLength, size, darkenedLength], + ) + cr.fill() + } + } else { + let spacing = Math.ceil(areaSize / 18) // separation between the indicators + let length + let radius + let dist + let indicatorSize + let translate + let preDraw = () => {} + let draw + let drawDash = (i, dashLength) => { + dist = i * dashLength + i * spacing + cr.rectangle.apply( + cr, + isHorizontalDots + ? [dist, 0, dashLength, size] + : [0, dist, size, dashLength], + ) + } + + switch (type) { + // Using the code from Ciliora Round style in pull request https://github.com/home-sweet-gnome/dash-to-panel/pull/1720 + case DOT_STYLE.CILIORA: + spacing = size + radius = size / 2 + length = areaSize - size * (n - 1) - spacing * (n - 1) + translate = () => cr.translate(startX, startY) + preDraw = () => { + cr.newSubPath() + cr.arc.apply( + cr, + (isHorizontalDots ? [radius, radius] : [radius, radius]).concat([radius, 0, 2 * Math.PI]) + ) + cr.arc.apply( + cr, + (isHorizontalDots ? [length - radius, radius] : [radius, length - radius]).concat([radius, 0, 2 * Math.PI]) + ) + cr.rectangle.apply( + cr, + (isHorizontalDots ? [radius, 0, length - size, size] : [0, radius, size, length - size]) + ) + } + draw = (i) => { + dist = length + i * spacing + (i - 1) * size + radius + cr.arc.apply( + cr, + (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([radius, 0, 2 * Math.PI]), + ) + } + break + case DOT_STYLE.DOTS: { + radius = size / 2 + + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * size - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) + } + draw = (i) => { + dist = (2 * i + 1) * radius + i * spacing + cr.arc.apply( + cr, + (isHorizontalDots ? [dist, radius] : [radius, dist]).concat([ + radius, + 0, + 2 * Math.PI, + ]), + ) + } + break + } + case DOT_STYLE.SQUARES: + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * size - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) + } + draw = (i) => { + dist = i * size + i * spacing + cr.rectangle.apply( + cr, + (isHorizontalDots ? [dist, 0] : [0, dist]).concat([size, size]), + ) + } + break + case DOT_STYLE.DASHES: + length = Math.floor(areaSize / 4) - spacing + translate = () => { + indicatorSize = Math.floor( + (areaSize - n * length - (n - 1) * spacing) / 2, + ) + cr.translate.apply( + cr, + isHorizontalDots + ? [indicatorSize, startY] + : [startX, indicatorSize], + ) + } + draw = (i) => drawDash(i, length) + break + case DOT_STYLE.SEGMENTED: + length = Math.ceil((areaSize - (n - 1) * spacing) / n) + translate = () => cr.translate(startX, startY) + draw = (i) => drawDash(i, length) + break + } + + translate() + + cr.setSourceColor(bodyColor) + preDraw() + for (let i = 0; i < n; i++) { + cr.newSubPath() + draw(i) + } + cr.fill() + } + + cr.$dispose() + } + + _handleNotifications() { + if (!this._nWindows && !this.window) return + + let monitor = this.dtpPanel.panelManager.notificationsMonitor + let state = monitor.getState(this.app) + let count = 0 + + if (!state) return + + if (SETTINGS.get_boolean('progress-show-count')) { + this.iconAnimator[`${state.urgent ? 'add' : 'remove'}Animation`]( + this.icon._iconBin, + 'dance', + ) + + if (state.total) { + count = state.total > 9 ? '9+' : state.total + this.dtpPanel.intellihide.revealAndHold(Hold.NOTIFY) + } else this.dtpPanel.intellihide.release(Hold.NOTIFY) + } + + this._notificationsCount = count + + this._maybeUpdateNumberOverlay() + } + + _maybeUpdateNumberOverlay() { + let visible = this._numberOverlayBin.visible + let shouldBeVisible = + (this._hotkeysOverlayActiveMode && + this._numberHotkeysOverlayLabel > -1) || + this._notificationsCount + + let showNotifications = + this._notificationsCount && + this._hotkeysOverlayActiveMode !== 'TEMPORARILY' + let label = showNotifications + ? this._notificationsCount + : this._numberHotkeysOverlayLabel + + this._numberOverlayLabel[ + `${showNotifications ? 'add' : 'remove'}_style_class_name` + ]('notification-badge') + + if (showNotifications) { + this._numberOverlayLabel.set_x_align(Clutter.ActorAlign.END) + this._numberOverlayLabel.set_y_align(Clutter.ActorAlign.START) + this._numberOverlayLabel.set_x_expand(true) + this._numberOverlayLabel.set_y_expand(true) + } else { + this._numberOverlayLabel.set_x_align(Clutter.ActorAlign.START) + this._numberOverlayLabel.set_y_align(Clutter.ActorAlign.START) + this._numberOverlayLabel.set_x_expand(false) + this._numberOverlayLabel.set_y_expand(false) + } + + if (shouldBeVisible && label !== this._numberOverlayLabel.get_text()) { + this._numberOverlayLabel.set_text(label.toString()) + this._updateNumberOverlay() + } + + if (visible && !shouldBeVisible) this._numberOverlayBin.hide() + else if (!visible && shouldBeVisible) this._numberOverlayBin.show() + } + + _numberOverlay() { + // Add label for a numeric visual aid (hotkeys or notification) + this._numberOverlayLabel = new St.Label({ style_class: 'badge' }) + this._numberOverlayBin = new St.Bin({ + child: this._numberOverlayLabel, + y: 2, + }) + this._numberOverlayLabel.add_style_class_name('number-overlay') + this._numberHotkeysOverlayLabel = -1 + this._numberOverlayBin.hide() + + this._dtpIconContainer.add_child(this._numberOverlayBin) + } + + _updateNumberOverlay() { + // We apply an overall scale factor that might come from a HiDPI monitor. + // Clutter dimensions are in physical pixels, but CSS measures are in logical + // pixels, so make sure to consider the scale. + // Set the font size to something smaller than the whole icon so it is + // still visible. The border radius is large to make the shape circular + let panelSize = this.dtpPanel.geom.iconSize + let minFontSize = panelSize >= 32 ? 12 : 10 + let fontSize = Math.round( + Math.max(minFontSize, 0.2 * panelSize) / Utils.getScaleFactor(), + ) + let size = Math.round(fontSize * 1.3) + let style = ` + font-size: ${fontSize}px; + height: ${size}px; + ` + this._numberOverlayLabel.set_style(style) + } + + setHotkeysNumberOverlayLabel(number) { + this._numberHotkeysOverlayLabel = number + } + + toggleHotkeysNumberOverlay(activateMode) { + this._hotkeysOverlayActiveMode = + this._numberHotkeysOverlayLabel > -1 && activateMode + + this._maybeUpdateNumberOverlay() + } + + handleDragOver(source) { + if (source == Main.xdndHandler) { + this._previewMenu.close(true) + + if (!this._nWindows && !this.window) + return DND.DragMotionResult.MOVE_DROP + + if (this._nWindows == 1 || this.window) { + this.window + ? Main.activateWindow(this.window) + : activateFirstWindow(this.app, this.monitor) + } else + this.dtpPanel.panelManager.showFocusedAppInOverview(this.app, true) + + return DND.DragMotionResult.MOVE_DROP + } + + return DND.DragMotionResult.CONTINUE + } + + getAppIconInterestingWindows(isolateMonitors) { + return getInterestingWindows( + this.app, + this.dtpPanel.monitor, + isolateMonitors, + ) + } + }, +) +TaskbarAppIcon.prototype.scaleAndFade = + TaskbarAppIcon.prototype.undoScaleAndFade = () => {} + +export function minimizeWindow(app, param, monitor) { + // Param true make all app windows minimize + let windows = getInterestingWindows(app, monitor) + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + for (let i = 0; i < windows.length; i++) { + let w = windows[i] + if ( + w.get_workspace() == current_workspace && + w.showing_on_its_workspace() + ) { + w.minimize() + // Just minimize one window. By specification it should be the + // focused window on the current workspace. + if (!param) break + } + } +} + +/* + * By default only non minimized windows are activated. + * This activates all windows in the current workspace. + */ +export function activateAllWindows(app, monitor) { + // First activate first window so workspace is switched if needed, + // then activate all other app windows in the current workspace. + let windows = getInterestingWindows(app, monitor) + let w = windows[0] + Main.activateWindow(w) + let activeWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace_index() + + if (windows.length <= 0) return + + for (let i = windows.length - 1; i >= 0; i--) { + if (windows[i].get_workspace().index() == activeWorkspace) { + Main.activateWindow(windows[i]) + } + } +} + +export function activateFirstWindow(app, monitor) { + let windows = getInterestingWindows(app, monitor) + Main.activateWindow(windows[0]) +} + +export function cycleThroughWindows(app, reversed, shouldMinimize, monitor) { + // Store for a little amount of time last clicked app and its windows + // since the order changes upon window interaction + let MEMORY_TIME = 3000 + + let app_windows = getInterestingWindows(app, monitor) + + if (shouldMinimize) app_windows.push('MINIMIZE') + + if (recentlyClickedAppLoopId > 0) GLib.Source.remove(recentlyClickedAppLoopId) + + recentlyClickedAppLoopId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + MEMORY_TIME, + resetRecentlyClickedApp, + ) + + // If there isn't already a list of windows for the current app, + // or the stored list is outdated, use the current windows list. + if ( + !recentlyClickedApp || + recentlyClickedApp.get_id() != app.get_id() || + recentlyClickedAppWindows.length != app_windows.length || + recentlyClickedAppMonitorIndex != monitor.index + ) { + recentlyClickedApp = app + recentlyClickedAppWindows = app_windows + recentlyClickedAppIndex = 0 + recentlyClickedAppMonitorIndex = monitor.index + } + + if (reversed) { + recentlyClickedAppIndex-- + if (recentlyClickedAppIndex < 0) + recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1 + } else { + recentlyClickedAppIndex++ + } + let index = recentlyClickedAppIndex % recentlyClickedAppWindows.length + + if (recentlyClickedAppWindows[index] === 'MINIMIZE') + minimizeWindow(app, true, monitor) + else Main.activateWindow(recentlyClickedAppWindows[index]) +} + +export function resetRecentlyClickedApp() { + if (recentlyClickedAppLoopId > 0) GLib.Source.remove(recentlyClickedAppLoopId) + + recentlyClickedAppLoopId = 0 + recentlyClickedApp = null + recentlyClickedAppWindows = null + recentlyClickedAppIndex = 0 + recentlyClickedAppMonitorIndex = null + + return GLib.SOURCE_REMOVE +} + +export function closeAllWindows(app, monitor) { + let windows = getInterestingWindows(app, monitor) + for (let i = 0; i < windows.length; i++) + windows[i].delete(global.get_current_time()) +} + +// Filter out unnecessary windows, for instance +// nautilus desktop window. +export function getInterestingWindows(app, monitor, isolateMonitors) { + let windows = (app ? app.get_windows() : Utils.getAllMetaWindows()).filter( + (w) => !w.skip_taskbar, + ) + + // When using workspace or monitor isolation, we filter out windows + // that are not in the current workspace or on the same monitor as the appicon + if (SETTINGS.get_boolean('isolate-workspaces')) + windows = windows.filter(function (w) { + return ( + w.get_workspace() && w.get_workspace() == Utils.getCurrentWorkspace() + ) + }) + + if ( + monitor && + SETTINGS.get_boolean('multi-monitors') && + (isolateMonitors || SETTINGS.get_boolean('isolate-monitors')) + ) { + windows = windows.filter(function (w) { + return w.get_monitor() == monitor.index + }) + } + + return windows +} + +export function cssHexTocssRgba(cssHex, opacity) { + let bigint = parseInt(cssHex.slice(1), 16) + let r = (bigint >> 16) & 255 + let g = (bigint >> 8) & 255 + let b = bigint & 255 + + return 'rgba(' + [r, g, b].join(',') + ',' + opacity + ')' +} + +export function getIconPadding(dtpPanel) { + let panelSize = dtpPanel.geom.innerSize + let padding = Taskbar.APPICON_PADDING + let availSize = panelSize - Taskbar.MIN_ICON_SIZE - (panelSize % 2) + + if (padding * 2 > availSize) { + padding = availSize * 0.5 + } + + return padding +} + +/** + * Extend AppMenu (AppIconMenu for pre gnome 41) + * + * - hide 'App Details' according to setting + * - show windows header only if show-window-previews is disabled + * - Add close windows option based on quitfromdash extension + * (https://github.com/deuill/shell-extension-quitfromdash) + */ + +export class TaskbarSecondaryMenu extends AppMenu.AppMenu { + constructor(source, side) { + super(source, side) + // constructor parameter does nos work for some reason + this._enableFavorites = true + this._showSingleWindows = true + + // replace quit item + delete this._quitItem + this._quitItem = this.addAction(_('Quit'), () => this._quitFromTaskbar()) + } + + updateQuitText() { + let count = this.sourceActor.window + ? 1 + : getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) + .length + + if (count > 0) { + let quitFromTaskbarMenuText = '' + if (count == 1) quitFromTaskbarMenuText = _('Quit') + else + quitFromTaskbarMenuText = ngettext( + 'Quit %d Window', + 'Quit %d Windows', + count, + ).format(count) + + this._quitItem.label.set_text(quitFromTaskbarMenuText) + } + } + + _quitFromTaskbar() { + let time = global.get_current_time() + let windows = this.sourceActor.window // ungrouped applications + ? [this.sourceActor.window] + : getInterestingWindows(this._app, this.sourceActor.dtpPanel.monitor) + + if (windows.length == this._app.get_windows().length) + this._app.request_quit() + + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + windows.forEach((w) => !!w.get_compositor_private() && w.delete(time++)) + + return GLib.SOURCE_REMOVE + }) + } +} + +/** + * This function is used for extendDashItemContainer + */ +export function ItemShowLabel() { + if (!this._labelText) return + + this.label.set_text(this._labelText) + this.label.opacity = 0 + this.label.show() + + let [stageX, stageY] = this.get_transformed_position() + let node = this.label.get_theme_node() + + let itemWidth = this.allocation.x2 - this.allocation.x1 + let itemHeight = this.allocation.y2 - this.allocation.y1 + + let labelWidth = this.label.get_width() + let labelHeight = this.label.get_height() + + let position = this._dtpPanel.getPosition() + let labelOffset = node.get_length('-x-offset') + + let xOffset = Math.floor((itemWidth - labelWidth) / 2) + let x = stageX + xOffset + let y = stageY + (itemHeight - labelHeight) * 0.5 + + switch (position) { + case St.Side.TOP: + y = stageY + labelOffset + itemHeight + break + case St.Side.BOTTOM: + y = stageY - labelHeight - labelOffset + break + case St.Side.LEFT: + x = stageX + labelOffset + itemWidth + break + case St.Side.RIGHT: + x = stageX - labelWidth - labelOffset + break + } + + // keep the label inside the screen border + // Only needed for the x coordinate. + + // Leave a few pixel gap + let gap = LABEL_GAP + let monitor = Main.layoutManager.findMonitorForActor(this) + if (x - monitor.x < gap) x += monitor.x - x + labelOffset + else if (x + labelWidth > monitor.x + monitor.width - gap) + x -= x + labelWidth - (monitor.x + monitor.width) + gap + + this.label.set_position(Math.round(x), Math.round(y)) + + let duration = Dash.DASH_ITEM_LABEL_SHOW_TIME + + if (duration > 1) { + duration /= 1000 + } + + Utils.animate(this.label, { + opacity: 255, + time: duration, + transition: 'easeOutQuad', + }) +} + +/** + * A wrapper class around the ShowAppsIcon class. + * + * - Pass settings to the constructor + * - set label position based on dash orientation (Note, I am reusing most machinery of the appIcon class) + * - implement a popupMenu based on the AppIcon code (Note, I am reusing most machinery of the appIcon class) + * + * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. + * thus use this pattern where the real showAppsIcon object is encaptulated, and a reference to it will be properly wired upon + * use of this class in place of the original showAppsButton. + * + */ +export const ShowAppsIconWrapper = class extends EventEmitter { + constructor(dtpPanel) { + super() + + this.realShowAppsIcon = new Dash.ShowAppsIcon() + + /* the variable equivalent to toggleButton has a different name in the appIcon class + (actor): duplicate reference to easily reuse appIcon methods */ + this.actor = this.realShowAppsIcon.toggleButton + this.realShowAppsIcon.show(false) + + // Re-use appIcon methods + this._removeMenuTimeout = AppDisplay.AppIcon.prototype._removeMenuTimeout + this._setPopupTimeout = AppDisplay.AppIcon.prototype._setPopupTimeout + this._onKeyboardPopupMenu = + AppDisplay.AppIcon.prototype._onKeyboardPopupMenu + + // No action on clicked (showing of the appsview is controlled elsewhere) + this._onClicked = () => this._removeMenuTimeout() + + this.actor.connect('leave-event', this._onLeaveEvent.bind(this)) + this.actor.connect('button-press-event', this._onButtonPress.bind(this)) + this.actor.connect('touch-event', this._onTouchEvent.bind(this)) + this.actor.connect('clicked', this._onClicked.bind(this)) + this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this)) + + this._menu = null + this._menuManager = new PopupMenu.PopupMenuManager(this.actor) + this._menuTimeoutId = 0 + + this.realShowAppsIcon._dtpPanel = dtpPanel + Taskbar.extendDashItemContainer(this.realShowAppsIcon) + + this.realShowAppsIcon.icon.createIcon = function (size) { + this._iconActor = new St.Icon({ + icon_name: 'view-app-grid-symbolic', + icon_size: size, + style_class: 'show-apps-icon', + track_hover: true, + }) + + return this._iconActor + } + + this.setShowAppsPadding() + } + + _onButtonPress(_actor, event) { + let button = event.get_button() + if (button == 1) { + this._setPopupTimeout() + } else if (button == 3) { + this.popupMenu() + return Clutter.EVENT_STOP + } + return Clutter.EVENT_PROPAGATE + } + + _onLeaveEvent() { + this.actor.fake_release() + this._removeMenuTimeout() + } + + _onTouchEvent(actor, event) { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) this._setPopupTimeout() + + return Clutter.EVENT_PROPAGATE + } + + _onMenuPoppedDown() { + this._menu.sourceActor = this.actor + this.actor.sync_hover() + this.emit('menu-state-changed', false) + } + + setShowAppsPadding() { + let padding = getIconPadding(this.realShowAppsIcon._dtpPanel) + + this.actor.set_style( + 'padding:' + padding + 'px;', + ) + } + + createMenu() { + if (!this._menu) { + this._menu = new MyShowAppsIconMenu( + this.realShowAppsIcon, + this.realShowAppsIcon._dtpPanel, + ) + this._menu.connect('open-state-changed', (menu, isPoppedUp) => { + if (!isPoppedUp) this._onMenuPoppedDown() + }) + let id = Main.overview.connect('hiding', () => { + this._menu.close() + }) + this._menu.actor.connect('destroy', () => { + Main.overview.disconnect(id) + }) + + // We want to keep the item hovered while the menu is up + this._menu.blockSourceEvents = true + + Main.uiGroup.add_child(this._menu.actor) + this._menuManager.addMenu(this._menu) + } + } + + popupMenu(sourceActor = null) { + this._removeMenuTimeout() + this.actor.fake_release() + this.createMenu() + + this._menu.updateItems( + sourceActor == null ? this.realShowAppsIcon : sourceActor, + ) + + this.actor.set_hover(true) + this._menu.open(BoxPointer.PopupAnimation.FULL) + this._menuManager.ignoreRelease() + this.emit('sync-tooltip') + + return false + } + + shouldShowTooltip() { + return ( + SETTINGS.get_boolean('show-tooltip') && + this.actor.hover && + (!this._menu || !this._menu.isOpen) + ) + } + + destroy() { + this.realShowAppsIcon.destroy() + } +} + +/** + * A menu for the showAppsIcon + */ +export const MyShowAppsIconMenu = class extends PopupMenu.PopupMenu { + constructor(actor, dtpPanel) { + super(actor, 0, dtpPanel.getPosition()) + + this._dtpPanel = dtpPanel + + this.updateItems(actor) + } + + updateItems(sourceActor) { + this.sourceActor = sourceActor + + this.removeAll() + + this._appendItem({ + title: _('System Monitor'), + cmd: ['gnome-system-monitor'], + }) + + this._appendItem({ + title: _('Files'), + cmd: ['nautilus'], + }) + + this._appendItem({ + title: _('Settings'), + cmd: ['gnome-control-center'], + }) + + this._appendItem({ + title: _('Zorin Appearance'), + cmd: ['zorin-appearance'], + }) + + this._appendList( + SETTINGS.get_strv('panel-context-menu-commands'), + SETTINGS.get_strv('panel-context-menu-titles'), + ) + + this._appendSeparator() + + let lockTaskbarMenuItem = this._appendMenuItem( + SETTINGS.get_boolean('taskbar-locked') + ? _('Unlock taskbar') + : _('Lock taskbar'), + ) + lockTaskbarMenuItem.connect('activate', () => { + SETTINGS.set_boolean( + 'taskbar-locked', + !SETTINGS.get_boolean('taskbar-locked'), + ) + }) + + let settingsMenuItem = this._appendMenuItem(_('Taskbar Settings')) + settingsMenuItem.connect('activate', () => DTP_EXTENSION.openPreferences()) + + if (this.sourceActor == Main.layoutManager.dummyCursor) { + this._appendSeparator() + let item = this._appendMenuItem( + this._dtpPanel._restoreWindowList + ? _('Restore Windows') + : _('Show Desktop'), + ) + item.connect( + 'activate', + this._dtpPanel._onShowDesktopButtonPress.bind(this._dtpPanel), + ) + } + } + + // Only add menu entries for commands that exist in path + _appendItem(info) { + if (GLib.find_program_in_path(info.cmd[0])) { + let item = this._appendMenuItem(_(info.title)) + + item.connect('activate', function () { + Util.spawn(info.cmd) + }) + return item + } + + return null + } + + _appendList(commandList, titleList) { + if (commandList.length != titleList.length) { + return + } + + for (let entry = 0; entry < commandList.length; entry++) { + this._appendItem({ + title: titleList[entry], + cmd: commandList[entry].split(' '), + }) + } + } + + _appendSeparator() { + let separator = new PopupMenu.PopupSeparatorMenuItem() + this.addMenuItem(separator) + } + + _appendMenuItem(labelText) { + // FIXME: app-well-menu-item style + let item = new PopupMenu.PopupMenuItem(labelText) + this.addMenuItem(item) + return item + } +} + +export const getIconContainerStyle = function (isVertical) { + let style = 'padding: ' + + if (SETTINGS.get_boolean('group-apps')) { + style += isVertical ? '0;' : '0 ' + DEFAULT_PADDING_SIZE + 'px;' + } else { + style += (isVertical ? '' : '0 ') + DEFAULT_PADDING_SIZE + 'px;' + } + + return style +} diff --git a/src/desktopIconsIntegration.js b/src/desktopIconsIntegration.js new file mode 100644 index 0000000..669fb6d --- /dev/null +++ b/src/desktopIconsIntegration.js @@ -0,0 +1,171 @@ +/* + * The code in this file is distributed under a "1-clause BSD license", + * which makes it compatible with GPLv2 and GPLv3 too, and others. + * + * License text: + * + * Copyright (C) 2021 Sergio Costas (rastersoft@gmail.com) + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/******************************************************************************* + * Integration class + * + * This class must be added to other extensions in order to integrate + * them with Desktop Icons NG. It allows an extension to notify how much margin + * it uses in each side of each monitor. + * + * DON'T SEND PATCHES TO THIS FILE TO THE EXTENSION MAINTAINER. SEND THEM TO + * DESKTOP ICONS NG MAINTAINER: https://gitlab.com/rastersoft/desktop-icons-ng + * + * In the *enable()* function, create a *DesktopIconsUsableAreaClass()* + * object with + * + * new DesktopIconsIntegration.DesktopIconsUsableAreaClass(object); + * + * Now, in the *disable()* function just call to the *destroy()* method before + * nullifying the pointer. You must create a new object in enable() the next + * time the extension is enabled. + * + * In your code, every time you change the margins, you should call first to + * *resetMargins()* method to clear the current margins, and then call to + * *setMargins(...)* method as many times as you need to set the margins in each + * monitor. You don't need to call it for all the monitors, only for those where + * you are painting something. If you don't set values for a monitor, they will + * be considered zero. + * + * The margins values are relative to the monitor border. + * + *******************************************************************************/ + +import GLib from 'gi://GLib' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js' +import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js' + +const IDENTIFIER_UUID = '130cbc66-235c-4bd6-8571-98d2d8bba5e2' + +export class DesktopIconsUsableAreaClass { + _checkIfExtensionIsEnabled(extension) { + return ( + extension?.state === ExtensionUtils.ExtensionState.ENABLED || + extension?.state === ExtensionUtils.ExtensionState.ACTIVE + ) + } + + constructor() { + const Me = Extension.lookupByURL(import.meta.url) + this._UUID = Me.uuid + this._extensionManager = Main.extensionManager + this._timedMarginsID = 0 + this._margins = {} + this._emID = this._extensionManager.connect( + 'extension-state-changed', + (_obj, extension) => { + if (!extension) return + + // If an extension is being enabled and lacks the DesktopIconsUsableArea object, we can avoid launching a refresh + if (this._checkIfExtensionIsEnabled(extension)) { + this._sendMarginsToExtension(extension) + return + } + // if the extension is being disabled, we must do a full refresh, because if there were other extensions originally + // loaded after that extension, those extensions will be disabled and enabled again without notification + this._changedMargins() + }, + ) + } + + /** + * Sets or updates the top, bottom, left and right margins for a + * monitor. Values are measured from the monitor border (and NOT from + * the workspace border). + * + * @param {int} monitor Monitor number to which set the margins. + * A negative value means "the primary monitor". + * @param {int} top Top margin in pixels + * @param {int} bottom Bottom margin in pixels + * @param {int} left Left margin in pixels + * @param {int} right Right margin in pixels + */ + setMargins(monitor, top, bottom, left, right) { + this._margins[monitor] = { + top: top, + bottom: bottom, + left: left, + right: right, + } + this._changedMargins() + } + + /** + * Clears the current margins. Must be called before configuring the monitors + * margins with setMargins(). + */ + resetMargins() { + this._margins = {} + this._changedMargins() + } + + /** + * Disconnects all the signals and removes the margins. + */ + destroy() { + if (this._emID) { + this._extensionManager.disconnect(this._emID) + this._emID = 0 + } + if (this._timedMarginsID) { + GLib.source_remove(this._timedMarginsID) + this._timedMarginsID = 0 + } + this._margins = null + this._changedMargins() + } + + _changedMargins() { + if (this._timedMarginsID) { + GLib.source_remove(this._timedMarginsID) + } + this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => { + this._sendMarginsToAll() + this._timedMarginsID = 0 + return GLib.SOURCE_REMOVE + }) + } + + _sendMarginsToAll() { + this._extensionManager + .getUuids() + .forEach((uuid) => + this._sendMarginsToExtension(this._extensionManager.lookup(uuid)), + ) + } + + _sendMarginsToExtension(extension) { + // check that the extension is an extension that has the logic to accept + // working margins + if (!this._checkIfExtensionIsEnabled(extension)) return + + const usableArea = extension?.stateObj?.DesktopIconsUsableArea + if (usableArea?.uuid === IDENTIFIER_UUID) + usableArea.setMarginsForExtension(this._UUID, this._margins) + } +} diff --git a/src/extension.js b/src/extension.js new file mode 100644 index 0000000..18c5351 --- /dev/null +++ b/src/extension.js @@ -0,0 +1,175 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import Shell from 'gi://Shell' + +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' +import { + Extension, + gettext as _, +} from 'resource:///org/gnome/shell/extensions/extension.js' +import * as PanelSettings from './panelSettings.js' + +import * as PanelManager from './panelManager.js' +import * as AppIcons from './appIcons.js' +import * as Utils from './utils.js' + +const ZORIN_DASH_UUID = 'zorin-dash@zorinos.com' +export const ZORIN_TILING_SHELL_UUID = 'zorin-tiling-shell@zorinos.com' + +let panelManager +let startupCompleteHandler +let zorinDashDelayId = 0 + +export let DTP_EXTENSION = null +export let SETTINGS = null +export let TILINGSETTINGS = null +export let DESKTOPSETTINGS = null +export let TERMINALSETTINGS = null +export let NOTIFICATIONSSETTINGS = null +export let PERSISTENTSTORAGE = null +export let EXTENSION_PATH = null +export let tracker = null + +export default class ZorinTaskbarExtension extends Extension { + constructor(metadata) { + super(metadata) + + this._realHasOverview = Main.sessionMode.hasOverview + + //create an object that persists until gnome-shell is restarted, even if the extension is disabled + PERSISTENTSTORAGE = {} + } + + async enable() { + DTP_EXTENSION = this + SETTINGS = this.getSettings('org.gnome.shell.extensions.zorin-taskbar') + try { + TILINGSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.shell.extensions.zorin-tiling-shell', + }) + } catch (e) { + console.log(e) + } + DESKTOPSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }) + TERMINALSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.desktop.default-applications.terminal', + }) + NOTIFICATIONSSETTINGS = new Gio.Settings({ + schema_id: 'org.gnome.desktop.notifications', + }) + EXTENSION_PATH = this.path + + tracker = Shell.WindowTracker.get_default() + + //create a global object that can emit signals and conveniently expose functionalities to other extensions + global.zorinTaskbar = new EventEmitter() + + await PanelSettings.init(SETTINGS) + + // To remove later, try to map settings using monitor indexes to monitor ids + PanelSettings.adjustMonitorSettings(SETTINGS) + + Main.layoutManager.startInOverview = false + + if (Main.layoutManager._startingUp) { + Main.sessionMode.hasOverview = false + startupCompleteHandler = Main.layoutManager.connect( + 'startup-complete', + () => (Main.sessionMode.hasOverview = this._realHasOverview), + ) + } + + this.enableGlobalStyles() + + let completeEnable = () => { + panelManager = new PanelManager.PanelManager() + panelManager.enable() + zorinDashDelayId = 0 + + return GLib.SOURCE_REMOVE + } + + // disable Zorin Dash if present + if (Main.extensionManager._extensionOrder.indexOf(ZORIN_DASH_UUID) >= 0) { + let disabled = global.settings.get_strv('disabled-extensions') + + if (disabled.indexOf(ZORIN_DASH_UUID) < 0) { + disabled.push(ZORIN_DASH_UUID) + global.settings.set_strv('disabled-extensions', disabled) + + // wait a bit so Zorin Dash can disable itself and restore the showappsbutton + zorinDashDelayId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 200, + completeEnable, + ) + } + } else completeEnable() + } + + disable() { + if (zorinDashDelayId) GLib.Source.remove(zorinDashDelayId) + + panelManager.disable() + + DTP_EXTENSION = null + SETTINGS = null + DESKTOPSETTINGS = null + TERMINALSETTINGS = null + panelManager = null + + delete global.zorinTaskbar + + this.disableGlobalStyles() + + AppIcons.resetRecentlyClickedApp() + + if (startupCompleteHandler) { + Main.layoutManager.disconnect(startupCompleteHandler) + startupCompleteHandler = null + } + + Main.sessionMode.hasOverview = this._realHasOverview + } + + resetGlobalStyles() { + this.disableGlobalStyles() + this.enableGlobalStyles() + } + + enableGlobalStyles() { + let globalBorderRadius = SETTINGS.get_int('global-border-radius') + + if (globalBorderRadius) + Main.layoutManager.uiGroup.add_style_class_name( + `br${globalBorderRadius * 5}`, + ) + } + + disableGlobalStyles() { + ;['br5', 'br10', 'br15', 'br20', 'br25'].forEach((c) => + Main.layoutManager.uiGroup.remove_style_class_name(c), + ) + } +} diff --git a/src/intellihide.js b/src/intellihide.js new file mode 100644 index 0000000..2c2a08b --- /dev/null +++ b/src/intellihide.js @@ -0,0 +1,529 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + */ + +import Clutter from 'gi://Clutter' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' + +import * as GrabHelper from 'resource:///org/gnome/shell/ui/grabHelper.js' +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js' +import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js' + +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import { SETTINGS, NOTIFICATIONSSETTINGS } from './extension.js' + +const INTELLIHIDE_PRESSURE_THRESHOLD = 100 +const INTELLIHIDE_PRESSURE_TIME = 1000 +const INTELLIHIDE_ANIMATION_TIME = 200 +const INTELLIHIDE_CLOSE_DELAY = 400 +const INTELLIHIDE_ENABLE_START_DELAY = 2000 + +//timeout intervals +const CHECK_POINTER_MS = 200 +const CHECK_GRAB_MS = 400 +const POST_ANIMATE_MS = 50 +const MIN_UPDATE_MS = 250 + +//timeout names +const T1 = 'checkGrabTimeout' +const T2 = 'limitUpdateTimeout' +const T3 = 'postAnimateTimeout' +const T4 = 'enableStartTimeout' + +const SIDE_CONTROLS_ANIMATION_TIME = + OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / + (OverviewControls.SIDE_CONTROLS_ANIMATION_TIME > 1 ? 1000 : 1) + +export const Hold = { + NONE: 0, + TEMPORARY: 1, + PERMANENT: 2, + NOTIFY: 4, +} + +export const Intellihide = class { + constructor(dtpPanel) { + this._dtpPanel = dtpPanel + this._panelBox = dtpPanel.panelBox + this._panelManager = dtpPanel.panelManager + this._proximityManager = this._panelManager.proximityManager + this._holdStatus = Hold.NONE + + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + this._intellihideChangedId = SETTINGS.connect('changed::intellihide', () => + this._changeEnabledStatus(), + ) + this._intellihideOnlySecondaryChangedId = SETTINGS.connect( + 'changed::intellihide-only-secondary', + () => this._changeEnabledStatus(), + ) + + this.enabled = false + this._changeEnabledStatus() + } + + enable() { + this.enabled = true + this._monitor = this._dtpPanel.monitor + this._animationDestination = -1 + this._pendingUpdate = false + this._hoveredOut = false + this._windowOverlap = false + this._translationProp = + 'translation_' + (this._dtpPanel.checkIfVertical() ? 'x' : 'y') + + this._panelBox.translation_y = 0 + this._panelBox.translation_x = 0 + + this._setTrackPanel(true) + this._bindGeneralSignals() + + if (SETTINGS.get_boolean('intellihide-hide-from-windows')) { + this._proximityWatchId = this._proximityManager.createWatch( + this._panelBox.get_parent(), + this._dtpPanel.monitor.index, + Proximity.Mode[SETTINGS.get_string('intellihide-behaviour')], + 0, + 0, + (overlap) => { + this._windowOverlap = overlap + this._queueUpdatePanelPosition() + }, + ) + } + + this._setRevealMechanism() + + let lastState = SETTINGS.get_int('intellihide-persisted-state') + + if (lastState > -1) { + this._holdStatus = lastState + + if (lastState == Hold.NONE && Main.layoutManager._startingUp) + this._signalsHandler.add([ + this._panelBox, + 'notify::mapped', + () => this._hidePanel(true), + ]) + else this._queueUpdatePanelPosition() + } else + // -1 means that the option to persist hold isn't activated, so normal start + this._timeoutsHandler.add([ + T4, + INTELLIHIDE_ENABLE_START_DELAY, + () => this._queueUpdatePanelPosition(), + ]) + } + + disable(reset) { + this.enabled = false + + if (this._proximityWatchId) { + this._proximityManager.removeWatch(this._proximityWatchId) + } + + this._setTrackPanel(false) + + this._signalsHandler.destroy() + this._timeoutsHandler.destroy() + + this._removeRevealMechanism() + + this._revealPanel(!reset) + } + + destroy() { + SETTINGS.disconnect(this._intellihideChangedId) + SETTINGS.disconnect(this._intellihideOnlySecondaryChangedId) + + if (this.enabled) { + this.disable() + } + } + + toggle() { + this[this._holdStatus & Hold.PERMANENT ? 'release' : 'revealAndHold']( + Hold.PERMANENT, + ) + } + + revealAndHold(holdStatus) { + if ( + !this.enabled || + (holdStatus == Hold.NOTIFY && + (!SETTINGS.get_boolean('intellihide-show-on-notification') || + !NOTIFICATIONSSETTINGS.get_boolean('show-banners'))) + ) + return + + if (!this._holdStatus) this._revealPanel() + + this._holdStatus |= holdStatus + + this._maybePersistHoldStatus() + } + + release(holdStatus) { + if (!this.enabled) return + + if (this._holdStatus & holdStatus) this._holdStatus -= holdStatus + + if (!this._holdStatus) { + this._maybePersistHoldStatus() + this._queueUpdatePanelPosition() + } + } + + reset() { + this.disable(true) + this.enable() + } + + _changeEnabledStatus() { + let intellihide = SETTINGS.get_boolean('intellihide') + let onlySecondary = SETTINGS.get_boolean('intellihide-only-secondary') + let enabled = intellihide && !(this._dtpPanel.isPrimary && onlySecondary) + + if (this.enabled !== enabled) { + this[enabled ? 'enable' : 'disable']() + } + } + + _maybePersistHoldStatus() { + if (SETTINGS.get_int('intellihide-persisted-state') > -1) + SETTINGS.set_int( + 'intellihide-persisted-state', + this._holdStatus & Hold.PERMANENT ? Hold.PERMANENT : Hold.NONE, + ) + } + + _bindGeneralSignals() { + this._signalsHandler.add( + [ + this._dtpPanel.taskbar, + ['menu-closed', 'end-drag'], + () => { + this._panelBox.sync_hover() + this._onHoverChanged() + }, + ], + [ + SETTINGS, + [ + 'changed::intellihide-use-pressure', + 'changed::intellihide-hide-from-windows', + 'changed::intellihide-behaviour', + ], + () => this.reset(), + ], + [this._panelBox, 'notify::hover', () => this._onHoverChanged()], + [ + this._dtpPanel.taskbar.previewMenu, + 'open-state-changed', + () => this._queueUpdatePanelPosition(), + ], + [ + Main.overview, + ['showing', 'hiding'], + () => this._queueUpdatePanelPosition(), + ], + ) + + if (Meta.is_wayland_compositor()) { + this._signalsHandler.add([ + this._panelBox, + 'notify::visible', + () => Utils.setDisplayUnredirect(!this._panelBox.visible), + ]) + } + } + + _onHoverChanged() { + this._hoveredOut = !this._panelBox.hover + this._queueUpdatePanelPosition() + } + + _setTrackPanel(enable) { + let actorData = Utils.getTrackedActorData(this._panelBox) + + actorData.affectsStruts = !enable + actorData.trackFullscreen = !enable + + Main.layoutManager.panelBox.reactive = enable + Main.layoutManager.panelBox.track_hover = enable + + this._panelBox.track_hover = enable + this._panelBox.reactive = enable + this._panelBox.visible = enable ? enable : this._panelBox.visible + + Main.layoutManager._queueUpdateRegions() + } + + _setRevealMechanism() { + let barriers = Meta.BackendCapabilities.BARRIERS + + if ( + (global.backend.capabilities & barriers) === barriers && + SETTINGS.get_boolean('intellihide-use-pressure') + ) { + this._edgeBarrier = this._createBarrier() + this._pressureBarrier = new Layout.PressureBarrier( + INTELLIHIDE_PRESSURE_THRESHOLD, + INTELLIHIDE_PRESSURE_TIME, + Shell.ActionMode.NORMAL, + ) + this._pressureBarrier.addBarrier(this._edgeBarrier) + this._signalsHandler.add([ + this._pressureBarrier, + 'trigger', + () => this._queueUpdatePanelPosition(true), + ]) + } else { + this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch( + CHECK_POINTER_MS, + (x, y) => this._checkMousePointer(x, y), + ) + } + } + + _removeRevealMechanism() { + if (this._pointerWatch) { + PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch) + } + + if (this._pressureBarrier) { + this._pressureBarrier.destroy() + this._edgeBarrier.destroy() + + this._pressureBarrier = 0 + } + } + + _createBarrier() { + let position = this._dtpPanel.geom.position + let opts = { backend: global.backend } + + if (this._dtpPanel.checkIfVertical()) { + opts.y1 = this._monitor.y + opts.y2 = this._monitor.y + this._monitor.height + opts.x1 = opts.x2 = this._monitor.x + } else { + opts.x1 = this._monitor.x + opts.x2 = this._monitor.x + this._monitor.width + opts.y1 = opts.y2 = this._monitor.y + } + + if (position == St.Side.TOP) { + opts.directions = Meta.BarrierDirection.POSITIVE_Y + } else if (position == St.Side.BOTTOM) { + opts.y1 = opts.y2 = opts.y1 + this._monitor.height + opts.directions = Meta.BarrierDirection.NEGATIVE_Y + } else if (position == St.Side.LEFT) { + opts.directions = Meta.BarrierDirection.POSITIVE_X + } else { + opts.x1 = opts.x2 = opts.x1 + this._monitor.width + opts.directions = Meta.BarrierDirection.NEGATIVE_X + } + + return new Meta.Barrier(opts) + } + + _checkMousePointer(x, y) { + let position = this._dtpPanel.geom.position + + if ( + !this._panelBox.hover && + !Main.overview.visible && + ((position == St.Side.TOP && y <= this._monitor.y + 1) || + (position == St.Side.BOTTOM && + y >= this._monitor.y + this._monitor.height - 1) || + (position == St.Side.LEFT && x <= this._monitor.x + 1) || + (position == St.Side.RIGHT && + x >= this._monitor.x + this._monitor.width - 1)) && + x >= this._monitor.x && + x < this._monitor.x + this._monitor.width && + y >= this._monitor.y && + y < this._monitor.y + this._monitor.height + ) { + this._queueUpdatePanelPosition(true) + } + } + + _queueUpdatePanelPosition(fromRevealMechanism) { + if ( + !fromRevealMechanism && + this._timeoutsHandler.getId(T2) && + !Main.overview.visible + ) { + //unless this is a mouse interaction or entering/leaving the overview, limit the number + //of updates, but remember to update again when the limit timeout is reached + this._pendingUpdate = true + } else if (!this._holdStatus) { + this._checkIfShouldBeVisible(fromRevealMechanism) + ? this._revealPanel() + : this._hidePanel() + this._timeoutsHandler.add([ + T2, + MIN_UPDATE_MS, + () => this._endLimitUpdate(), + ]) + } + } + + _endLimitUpdate() { + if (this._pendingUpdate) { + this._pendingUpdate = false + this._queueUpdatePanelPosition() + } + } + + _checkIfShouldBeVisible(fromRevealMechanism) { + if ( + Main.overview.visibleTarget || + this._dtpPanel.taskbar.previewMenu.opened || + this._dtpPanel.taskbar._dragMonitor || + this._panelBox.get_hover() || + (this._dtpPanel.geom.position == St.Side.TOP && + Main.layoutManager.panelBox.get_hover()) || + this._checkIfGrab() + ) { + return true + } + + if (fromRevealMechanism) { + let mouseBtnIsPressed = + global.get_pointer()[2] & Clutter.ModifierType.BUTTON1_MASK + + //the user is trying to reveal the panel + if (this._monitor.inFullscreen && !mouseBtnIsPressed) { + return SETTINGS.get_boolean('intellihide-show-in-fullscreen') + } + + return !mouseBtnIsPressed + } + + if (!SETTINGS.get_boolean('intellihide-hide-from-windows')) { + return this._panelBox.hover + } + + return !this._windowOverlap + } + + _checkIfGrab() { + let isGrab + + if (GrabHelper._grabHelperStack) + // gnome-shell < 42 + isGrab = GrabHelper._grabHelperStack.some( + (gh) => gh._owner == this._dtpPanel.panel, + ) + else if (global.stage.get_grab_actor) { + // gnome-shell >= 42 + let grabActor = global.stage.get_grab_actor() + let sourceActor = grabActor?._sourceActor || grabActor + + isGrab = + sourceActor && + (sourceActor == Main.layoutManager.dummyCursor || + this._dtpPanel.statusArea.quickSettings?.menu.actor.contains( + sourceActor, + ) || + this._dtpPanel.panel.contains(sourceActor)) + } + + if (isGrab) + //there currently is a grab on a child of the panel, check again soon to catch its release + this._timeoutsHandler.add([ + T1, + CHECK_GRAB_MS, + () => this._queueUpdatePanelPosition(), + ]) + + return isGrab + } + + _revealPanel(immediate) { + if (!this._panelBox.visible) { + this._panelBox.visible = true + this._dtpPanel.taskbar._shownInitially = false + } + + this._animatePanel(0, immediate) + } + + _hidePanel(immediate) { + let position = this._dtpPanel.geom.position + let size = + this._panelBox[ + position == St.Side.LEFT || position == St.Side.RIGHT + ? 'width' + : 'height' + ] + let coefficient = + position == St.Side.TOP || position == St.Side.LEFT ? -1 : 1 + + this._animatePanel(size * coefficient, immediate) + } + + _animatePanel(destination, immediate) { + if (destination === this._animationDestination) return + + Utils.stopAnimations(this._panelBox) + this._animationDestination = destination + + if (immediate) { + this._panelBox[this._translationProp] = destination + this._panelBox.visible = !destination + } else if (destination !== this._panelBox[this._translationProp]) { + let tweenOpts = { + //when entering/leaving the overview, use its animation time instead of the one from the settings + time: Main.overview.visible + ? SIDE_CONTROLS_ANIMATION_TIME + : INTELLIHIDE_ANIMATION_TIME * 0.001, + //only delay the animation when hiding the panel after the user hovered out + delay: + destination != 0 && this._hoveredOut + ? INTELLIHIDE_CLOSE_DELAY * 0.001 + : 0, + transition: 'easeOutQuad', + onComplete: () => { + this._panelBox.visible = !destination + Main.layoutManager._queueUpdateRegions() + this._timeoutsHandler.add([ + T3, + POST_ANIMATE_MS, + () => this._queueUpdatePanelPosition(), + ]) + }, + } + + tweenOpts[this._translationProp] = destination + Utils.animate(this._panelBox, tweenOpts) + } + + this._hoveredOut = false + } +} diff --git a/src/notificationsMonitor.js b/src/notificationsMonitor.js new file mode 100644 index 0000000..a673299 --- /dev/null +++ b/src/notificationsMonitor.js @@ -0,0 +1,216 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + */ + +import Gio from 'gi://Gio' + +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' + +import { tracker } from './extension.js' +import * as Utils from './utils.js' + +const knownIdMappings = { + 'org.gnome.Evolution': [/^org\.gnome\.[eE]volution([.-].+)?$/g], +} + +export const NotificationsMonitor = class extends EventEmitter { + constructor() { + super() + + this._state = {} + this._signalsHandler = new Utils.GlobalSignalsHandler() + + // pretty much useless, but might as well keep it for now + this._launcherEntryId = Gio.DBus.session.signal_subscribe( + null, // sender + 'com.canonical.Unity.LauncherEntry', // iface + 'Update', // member + null, // path + null, // arg0 + Gio.DBusSignalFlags.NONE, + ( + connection, + senderName, + objectPath, + interfaceName, + signalName, + parameters, + ) => this._handleLauncherUpdate(senderName, parameters), + ) + + this._signalsHandler.add([ + tracker, + 'notify::focus-app', + () => { + let appId = tracker.focus_app?.id + + // reset notifications from message tray on app focus + if (tracker.focus_app && this._state[appId]) + this._updateState(tracker.focus_app.id, this._getDefaultState(), true) + }, + ]) + this._acquireUnityDBus() + + this._checkNotifications() + } + + destroy() { + if (this._launcherEntryId) + Gio.DBus.session.signal_unsubscribe(this._launcherEntryId) + + this._releaseUnityDBus() + this._signalsHandler.destroy() + } + + _updateState(appId, state, ignoreMapping) { + // depending of the notification source, some app id end + // with ".desktop" and some don't ¯\_(ツ)_/¯ + appId = appId.replace('.desktop', '') + + // some app have different source app id, deamon and such, + // but it maps to a desktop app so match those here + if (!ignoreMapping && !knownIdMappings[appId]) + appId = + Object.keys(knownIdMappings).find((k) => + knownIdMappings[k].some((regex) => appId.match(regex)), + ) || appId + + appId = `${appId}.desktop` + this._state[appId] = this._state[appId] || this._getDefaultState() + + if (this._mergeState(appId, state)) this.emit(`update-${appId}`) + } + + _getDefaultState() { + return { + count: 0, // Unity + trayCount: 0, // MessageTray + trayUrgent: false, // MessageTray + urgent: false, // Unity add MessageTray combined + total: 0, // Unity add MessageTray combined + } + } + + getState(app) { + return this._state[app.id] + } + + _mergeState(appId, state) { + let currenState = JSON.stringify(this._state[appId]) + + this._state[appId] = Object.assign(this._state[appId], state) + + if (tracker.focus_app?.id == appId) { + this._state[appId].count = 0 + this._state[appId].trayCount = 0 + } + + this._state[appId].urgent = + state.urgent || + (this._state[appId].trayUrgent && this._state[appId].trayCount) || + false + + this._state[appId].total = + ((this._state[appId]['count-visible'] || 0) && + (this._state[appId].count || 0)) + (this._state[appId].trayCount || 0) + + return currenState != JSON.stringify(this._state[appId]) + } + + _acquireUnityDBus() { + if (!this._unityBusId) { + this._unityBusId = Gio.DBus.session.own_name( + 'com.canonical.Unity', + Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, + null, + null, + ) + } + } + + _releaseUnityDBus() { + if (this._unityBusId) { + Gio.DBus.session.unown_name(this._unityBusId) + this._unityBusId = 0 + } + } + + _handleLauncherUpdate(senderName, parameters) { + if (!senderName || !parameters) return + + let [appUri, properties] = parameters.deep_unpack() + let appId = appUri.replace(/(^\w+:|^)\/\//, '') + let updates = {} + + // https://wiki.ubuntu.com/Unity/LauncherAPI#Low_level_DBus_API:_com.canonical.Unity.LauncherEntry + for (let property in properties) + updates[property] = properties[property].unpack() + + this._updateState(appId, updates) + } + + _checkNotifications() { + let getSourceId = (source) => + source?._appId || + source?.app?.id || + (source?.policy instanceof MessageTray.NotificationApplicationPolicy && + source.policy.id) + let addSource = (tray, source) => { + let appId = getSourceId(source) + let updateTray = () => { + this._updateState(appId, { + trayCount: source.count, // always source.unseenCount might be less annoying + trayUrgent: !!source.notifications.find((n) => { + return ( + n.urgency > MessageTray.Urgency.NORMAL || + source.constructor.name == 'WindowAttentionSource' // private type from gnome-shell + ) + }), + }) + } + + if (!appId) return + + this._signalsHandler.addWithLabel(appId, [ + source, + 'notify::count', + updateTray, + ]) + + updateTray() + } + + this._signalsHandler.add( + [Main.messageTray, 'source-added', addSource], + [ + Main.messageTray, + 'source-removed', + (tray, source) => { + let appId = getSourceId(source) + + if (appId) this._signalsHandler.removeWithLabel(appId) + }, + ], + ) + + Main.messageTray.getSources().forEach((s) => addSource(null, s)) + } +} diff --git a/src/overview.js b/src/overview.js new file mode 100644 index 0000000..d7376f6 --- /dev/null +++ b/src/overview.js @@ -0,0 +1,518 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import * as Intellihide from './intellihide.js' +import * as Utils from './utils.js' + +import Clutter from 'gi://Clutter' +import Gio from 'gi://Gio' +import Shell from 'gi://Shell' +import St from 'gi://St' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as WindowManager from 'resource:///org/gnome/shell/ui/windowManager.js' +import { WindowPreview } from 'resource:///org/gnome/shell/ui/windowPreview.js' +import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js' +import { SETTINGS } from './extension.js' + +const GS_SWITCH_HOTKEYS_KEY = 'switch-to-application-' +const GS_OPEN_HOTKEYS_KEY = 'open-new-window-application-' +const OVERLAY_TIMEOUT = 750 +const SHORTCUT_TIMEOUT = 2000 + +// When the dash is shown, workspace window preview bottom labels go over it (default +// gnome-shell behavior), but when the extension hides the dash, leave some space +// so those labels don't go over a bottom panel +const LABEL_MARGIN = 60 + +//timeout names +const T1 = 'swipeEndTimeout' +const T2 = 'numberOverlayTimeout' + +export const Overview = class { + constructor(panelManager) { + this._injectionManager = new InjectionManager() + this._numHotkeys = 10 + this._panelManager = panelManager + } + + enable(primaryPanel) { + this._panel = primaryPanel + this.taskbar = primaryPanel.taskbar + + this._injectionsHandler = new Utils.InjectionsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + this._optionalWorkspaceIsolation() + this._optionalHotKeys() + this._optionalNumberOverlay() + + this.toggleDash() + this._adaptAlloc() + + this._signalsHandler.add([ + SETTINGS, + ['changed::panel-sizes'], + () => this.toggleDash(), + ]) + } + + disable() { + this._signalsHandler.destroy() + this._injectionsHandler.destroy() + this._timeoutsHandler.destroy() + this._injectionManager.clear() + + this.toggleDash(true) + + // Remove key bindings + this._disableHotKeys() + this._disableExtraShortcut() + } + + toggleDash(visible) { + if (visible === undefined) { + visible = false + } + + let visibilityFunc = visible ? 'show' : 'hide' + let height = visible ? -1 : LABEL_MARGIN * Utils.getScaleFactor() + let overviewControls = Main.overview._overview._controls + + overviewControls.dash[visibilityFunc]() + overviewControls.dash.set_height(height) + } + + _adaptAlloc() { + let overviewControls = Main.overview._overview._controls + + this._injectionManager.overrideMethod( + Object.getPrototypeOf(overviewControls), + 'vfunc_allocate', + (originalAllocate) => (box) => { + let focusedPanel = this._panel.panelManager.focusedMonitorPanel + + if (focusedPanel) { + let position = focusedPanel.geom.position + let isBottom = position == St.Side.BOTTOM + + if (focusedPanel.intellihide?.enabled) { + // Panel intellihide is enabled (struts aren't taken into account on overview allocation), + // dynamically modify the overview box to follow the reveal/hide animation + let { transitioning, finalState, progress } = + overviewControls._stateAdjustment.getStateTransitionParams() + let size = + focusedPanel.geom[focusedPanel.checkIfVertical() ? 'w' : 'h'] * + (transitioning + ? Math.abs((finalState != 0 ? 0 : 1) - progress) + : 1) + + if (isBottom || position == St.Side.RIGHT) + box[focusedPanel.fixedCoord.c2] -= size + else box[focusedPanel.fixedCoord.c1] += size + } else if (isBottom) + // The default overview allocation takes into account external + // struts, everywhere but the bottom where the dash is usually fixed anyway. + // If there is a bottom panel under the dash location, give it some space here + box.y2 -= focusedPanel.geom.outerSize + } + + originalAllocate.call(overviewControls, box) + }, + ) + } + + /** + * Isolate overview to open new windows for inactive apps + */ + _optionalWorkspaceIsolation() { + let label = 'optionalWorkspaceIsolation' + + let enable = () => { + this._injectionsHandler.removeWithLabel(label) + + this._injectionsHandler.addWithLabel(label, [ + Shell.App.prototype, + 'activate', + IsolatedOverview, + ]) + + this._signalsHandler.removeWithLabel(label) + + this._signalsHandler.addWithLabel(label, [ + global.window_manager, + 'switch-workspace', + () => + this._panel.panelManager.allPanels.forEach((p) => + p.taskbar.handleIsolatedWorkspaceSwitch(), + ), + ]) + } + + let disable = () => { + this._signalsHandler.removeWithLabel(label) + this._injectionsHandler.removeWithLabel(label) + } + + function IsolatedOverview() { + // These lines take care of Nautilus for icons on Desktop + let activeWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = this.get_windows().filter( + (w) => w.get_workspace().index() == activeWorkspace.index(), + ) + + if ( + windows.length > 0 && + (!(windows.length == 1 && windows[0].skip_taskbar) || + this.is_on_workspace(activeWorkspace)) + ) + return Main.activateWindow(windows[0]) + + return this.open_new_window(-1) + } + + this._signalsHandler.add([ + SETTINGS, + 'changed::isolate-workspaces', + () => { + this._panel.panelManager.allPanels.forEach((p) => + p.taskbar.resetAppIcons(), + ) + + if (SETTINGS.get_boolean('isolate-workspaces')) enable() + else disable() + }, + ]) + + if (SETTINGS.get_boolean('isolate-workspaces')) enable() + } + + // Hotkeys + _activateApp(appIndex, modifiers) { + let seenApps = {} + let apps = [] + + this.taskbar._getAppIcons().forEach((appIcon) => { + if (!seenApps[appIcon.app] || this.taskbar.allowSplitApps) { + apps.push(appIcon) + } + + seenApps[appIcon.app] = (seenApps[appIcon.app] || 0) + 1 + }) + + this._showOverlay() + + if (appIndex < apps.length) { + let appIcon = apps[appIndex] + let seenAppCount = seenApps[appIcon.app] + let windowCount = + appIcon.window || appIcon._hotkeysCycle + ? seenAppCount + : appIcon._nWindows + + if ( + SETTINGS.get_boolean('shortcut-previews') && + windowCount > 1 && + !( + modifiers & + ~(Clutter.ModifierType.MOD1_MASK | Clutter.ModifierType.SUPER_MASK) + ) + ) { + //ignore the alt (MOD1_MASK) and super key (SUPER_MASK) + if ( + this._hotkeyPreviewCycleInfo && + this._hotkeyPreviewCycleInfo.appIcon != appIcon + ) { + this._endHotkeyPreviewCycle() + } + + if (!this._hotkeyPreviewCycleInfo) { + this._hotkeyPreviewCycleInfo = { + appIcon: appIcon, + currentWindow: appIcon.window, + keyFocusOutId: appIcon.connect('key-focus-out', () => + appIcon.grab_key_focus(), + ), + capturedEventId: global.stage.connect( + 'captured-event', + (actor, e) => { + if ( + e.type() == Clutter.EventType.KEY_RELEASE && + e.get_key_symbol() == (Clutter.KEY_Super_L || Clutter.Super_L) + ) { + this._endHotkeyPreviewCycle(true) + } + + return Clutter.EVENT_PROPAGATE + }, + ), + } + + appIcon._hotkeysCycle = appIcon.window + appIcon.window = null + appIcon._previewMenu.open(appIcon, true) + appIcon.grab_key_focus() + } + + appIcon._previewMenu.focusNext() + } else { + // Activate with button = 1, i.e. same as left click + let button = 1 + this._endHotkeyPreviewCycle() + appIcon.activate(button, modifiers, !this.taskbar.allowSplitApps) + } + } + } + + _endHotkeyPreviewCycle(focusWindow) { + if (this._hotkeyPreviewCycleInfo) { + global.stage.disconnect(this._hotkeyPreviewCycleInfo.capturedEventId) + this._hotkeyPreviewCycleInfo.appIcon.disconnect( + this._hotkeyPreviewCycleInfo.keyFocusOutId, + ) + + if (focusWindow) { + this._hotkeyPreviewCycleInfo.appIcon._previewMenu.activateFocused() + } else this._hotkeyPreviewCycleInfo.appIcon._previewMenu.close() + + this._hotkeyPreviewCycleInfo.appIcon.window = + this._hotkeyPreviewCycleInfo.currentWindow + delete this._hotkeyPreviewCycleInfo.appIcon._hotkeysCycle + this._hotkeyPreviewCycleInfo = 0 + } + } + + _optionalHotKeys() { + this._hotKeysEnabled = false + if (SETTINGS.get_boolean('hot-keys')) this._enableHotKeys() + + this._signalsHandler.add([ + SETTINGS, + 'changed::hot-keys', + () => { + if (SETTINGS.get_boolean('hot-keys')) this._enableHotKeys() + else this._disableHotKeys() + }, + ]) + } + + _resetHotkeys() { + this._disableHotKeys() + this._enableHotKeys() + } + + _enableHotKeys() { + if (this._hotKeysEnabled) return + + // Setup keyboard bindings for taskbar elements + let shortcutNumKeys = SETTINGS.get_string('shortcut-num-keys') + let bothNumKeys = shortcutNumKeys == 'BOTH' + let numRowKeys = shortcutNumKeys == 'NUM_ROW' + let keys = [] + let prefixModifiers = Clutter.ModifierType.SUPER_MASK + + //3.32 introduced app hotkeys, disable them to prevent conflicts + if (Main.wm._switchToApplication) { + for (let i = 1; i < 10; ++i) { + Utils.removeKeybinding(GS_SWITCH_HOTKEYS_KEY + i) + + if (bothNumKeys || numRowKeys) + Utils.removeKeybinding(GS_OPEN_HOTKEYS_KEY + i) + } + } + + if (SETTINGS.get_string('hotkey-prefix-text') == 'SuperAlt') + prefixModifiers |= Clutter.ModifierType.MOD1_MASK + + if (bothNumKeys || numRowKeys) { + keys.push('app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-') // Regular numbers + } + + if (bothNumKeys || shortcutNumKeys == 'NUM_KEYPAD') { + keys.push('app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-') // Key-pad numbers + } + + keys.forEach(function (key) { + let modifiers = prefixModifiers + + // for some reason, in gnome-shell >= 40 Clutter.get_current_event() is now empty + // for keyboard events. Create here the modifiers that are needed in appicon.activate + modifiers |= + key.indexOf('-shift-') >= 0 ? Clutter.ModifierType.SHIFT_MASK : 0 + modifiers |= + key.indexOf('-ctrl-') >= 0 ? Clutter.ModifierType.CONTROL_MASK : 0 + + for (let i = 0; i < this._numHotkeys; i++) { + let appNum = i + + Utils.addKeybinding(key + (i + 1), SETTINGS, () => + this._activateApp(appNum, modifiers), + ) + } + }, this) + + this._hotKeysEnabled = true + + if (SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') + this._toggleHotkeysNumberOverlay(true) + } + + _disableHotKeys() { + if (!this._hotKeysEnabled) return + + let shortcutNumKeys = SETTINGS.get_string('shortcut-num-keys') + let keys = [ + 'app-hotkey-', + 'app-shift-hotkey-', + 'app-ctrl-hotkey-', // Regular numbers + 'app-hotkey-kp-', + 'app-shift-hotkey-kp-', + 'app-ctrl-hotkey-kp-', // Key-pad numbers + ] + + keys.forEach(function (key) { + for (let i = 0; i < this._numHotkeys; i++) { + Utils.removeKeybinding(key + (i + 1)) + } + }, this) + + if (Main.wm._switchToApplication) { + let gsSettings = new Gio.Settings({ + schema_id: WindowManager.SHELL_KEYBINDINGS_SCHEMA, + }) + + for (let i = 1; i < 10; ++i) { + Utils.addKeybinding( + GS_SWITCH_HOTKEYS_KEY + i, + gsSettings, + Main.wm._switchToApplication.bind(Main.wm), + ) + + if (shortcutNumKeys == 'BOTH' || shortcutNumKeys == 'NUM_ROW') + Utils.addKeybinding( + GS_OPEN_HOTKEYS_KEY + i, + gsSettings, + Main.wm._openNewApplicationWindow.bind(Main.wm), + ) + } + } + + this._hotKeysEnabled = false + + this._toggleHotkeysNumberOverlay(false) + } + + _optionalNumberOverlay() { + // Enable extra shortcut + if (SETTINGS.get_boolean('hot-keys')) this._enableExtraShortcut() + + this._signalsHandler.add( + [SETTINGS, 'changed::hot-keys', this._checkHotkeysOptions.bind(this)], + [ + SETTINGS, + [ + 'changed::hotkeys-overlay-combo', + ], + () => { + if ( + SETTINGS.get_boolean('hot-keys') && + SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS' + ) + this._toggleHotkeysNumberOverlay(true) + else this._toggleHotkeysNumberOverlay(false, true) + }, + ], + [SETTINGS, 'changed::shortcut-num-keys', () => this._resetHotkeys()], + ) + } + + _checkHotkeysOptions() { + if (SETTINGS.get_boolean('hot-keys')) this._enableExtraShortcut() + else this._disableExtraShortcut() + } + + _enableExtraShortcut() { + Utils.addKeybinding('shortcut', SETTINGS, () => this._showOverlay(true)) + } + + _disableExtraShortcut() { + Utils.removeKeybinding('shortcut') + } + + _showOverlay(overlayFromShortcut) { + //wait for intellihide timeout initialization + if (!this._panel.intellihide) { + return + } + + // Restart the counting if the shortcut is pressed again + let hotkey_option = SETTINGS.get_string('hotkeys-overlay-combo') + let temporarily = hotkey_option === 'TEMPORARILY' + let timeout = overlayFromShortcut ? SHORTCUT_TIMEOUT : OVERLAY_TIMEOUT + + if (hotkey_option === 'NEVER' || (!timeout && temporarily)) return + + if (temporarily || overlayFromShortcut) + this._toggleHotkeysNumberOverlay(true) + + this._panel.intellihide.revealAndHold(Intellihide.Hold.TEMPORARY) + + // Hide the overlay/dock after the timeout + this._timeoutsHandler.add([ + T2, + timeout, + () => { + if (hotkey_option != 'ALWAYS') { + this._toggleHotkeysNumberOverlay(false) + } + + this._panel.intellihide.release(Intellihide.Hold.TEMPORARY) + }, + ]) + } + + _toggleHotkeysNumberOverlay(show, reset) { + // this.taskbar is the primary taskbar + this.taskbar.toggleHotkeysNumberOverlay(show) + + if (reset) { + // on secondary panels, show the overlay on icons matching the ones + // found on the primary panel (see Taksbar.hotkeyAppNumbers) + this._panelManager.allPanels.forEach((p) => { + if (p.isPrimary) return + + p.taskbar.toggleHotkeysNumberOverlay(show) + }) + } + } + + _onSwipeBegin() { + this._swiping = true + return true + } + + _onSwipeEnd() { + this._timeoutsHandler.add([T1, 0, () => (this._swiping = false)]) + return true + } +} diff --git a/src/panel.js b/src/panel.js new file mode 100644 index 0000000..15f5591 --- /dev/null +++ b/src/panel.js @@ -0,0 +1,1544 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * + * Code to re-anchor the panel was taken from Thoma5 BottomPanel: + * https://github.com/Thoma5/gnome-shell-extension-bottompanel + * + * Pattern for moving clock based on Frippery Move Clock by R M Yorston + * http://frippery.org/extensions/ + * + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import Clutter from 'gi://Clutter' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Graphene from 'gi://Graphene' + +import * as AppIcons from './appIcons.js' +import * as Utils from './utils.js' +import * as Taskbar from './taskbar.js' +import * as TaskbarItemContainer from './taskbar.js' +import * as Pos from './panelPositions.js' +import * as PanelSettings from './panelSettings.js' +import * as PanelStyle from './panelStyle.js' + +import * as Config from 'resource:///org/gnome/shell/misc/config.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import * as CtrlAltTab from 'resource:///org/gnome/shell/ui/ctrlAltTab.js' +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js' +import St from 'gi://St' +import Meta from 'gi://Meta' +import Pango from 'gi://Pango' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import * as DateMenu from 'resource:///org/gnome/shell/ui/dateMenu.js' +import * as Volume from 'resource:///org/gnome/shell/ui/status/volume.js' + +import * as Intellihide from './intellihide.js' +import * as Transparency from './transparency.js' +import { + SETTINGS, + TILINGSETTINGS, + DESKTOPSETTINGS, + PERSISTENTSTORAGE, + EXTENSION_PATH, + tracker, + ZORIN_TILING_SHELL_UUID, +} from './extension.js' +import { + gettext as _, + InjectionManager, +} from 'resource:///org/gnome/shell/extensions/extension.js' + +export const panelBoxes = ['_leftBox', '_centerBox', '_rightBox'] + +//timeout names +const T4 = 'showDesktopTimeout' +const T5 = 'trackerFocusAppTimeout' +const T6 = 'waitPanelBoxAllocation' + +const SHOW_SHOWDESKTOP_DELAY = 1000 +const SHOW_SHOWDESKTOP_TIME = 200 +const MIN_PANEL_SIZE = 22 + +export const Panel = GObject.registerClass( + {}, + class Panel extends St.Widget { + _init(panelManager, monitor, panelBox, isStandalone) { + super._init({ + style_class: 'zorintaskbarPanel', + layout_manager: new Clutter.BinLayout(), + }) + + this._timeoutsHandler = new Utils.TimeoutsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._injectionManager = new InjectionManager() + + this.panelManager = panelManager + this.panelStyle = new PanelStyle.PanelStyle() + + this.monitor = monitor + this.panelBox = panelBox + + // when the original gnome-shell top panel is kept, all panels are "standalone", + // so in this case use isPrimary to get the panel on the primary dtp monitor, which + // might be different from the system's primary monitor. + this.isStandalone = isStandalone + this.isPrimary = + !isStandalone || + (SETTINGS.get_boolean('stockgs-keep-top-panel') && + monitor == panelManager.dtpPrimaryMonitor) + + this._sessionStyle = null + this._unmappedButtons = [] + this._elementGroups = [] + + let systemMenuInfo = Utils.getSystemMenuInfo() + + if (isStandalone) { + this.panel = new SecondaryPanel({ name: 'panel', reactive: true }) + this.statusArea = this.panel.statusArea = {} + + //next 3 functions are needed by other extensions to add elements to the secondary panel + this.panel.addToStatusArea = function (role, indicator, position, box) { + return Main.panel.addToStatusArea.call( + this, + role, + indicator, + position, + box, + ) + } + + this.panel._addToPanelBox = function (role, indicator, position, box) { + Main.panel._addToPanelBox.call(this, role, indicator, position, box) + } + + this.panel._onMenuSet = function (indicator) { + Main.panel._onMenuSet.call(this, indicator) + } + + this._leftBox = this.panel._leftBox = Utils.createBoxLayout({ + name: 'panelLeft', + }) + this._centerBox = this.panel._centerBox = Utils.createBoxLayout({ + name: 'panelCenter', + }) + this._rightBox = this.panel._rightBox = Utils.createBoxLayout({ + name: 'panelRight', + }) + + this.menuManager = this.panel.menuManager = + new PopupMenu.PopupMenuManager(this.panel) + + this._setPanelMenu( + systemMenuInfo.name, + systemMenuInfo.constructor, + this.panel, + ) + this._setPanelMenu('dateMenu', DateMenu.DateMenuButton, this.panel) + this._setPanelMenu( + 'activities', + Main.panel.statusArea.activities.constructor, + this.panel, + ) + + this.panel.add_child(this._leftBox) + this.panel.add_child(this._centerBox) + this.panel.add_child(this._rightBox) + } else { + this.panel = Main.panel + this.statusArea = Main.panel.statusArea + this.menuManager = Main.panel.menuManager + + panelBoxes.forEach((p) => (this[p] = Main.panel[p])) + ;['activities', systemMenuInfo.name, 'dateMenu'].forEach((b) => { + let container = this.statusArea[b].container + let parent = container.get_parent() + let siblings = parent.get_children() + let index = siblings.indexOf(container) + + container._dtpOriginalParent = parent + container._dtpOriginalIndex = + index && index == siblings.length - 1 ? -1 : index + parent ? parent.remove_child(container) : null + this.panel.add_child(container) + }) + } + + this.geom = this.getGeometry() + + // Create a wrapper around the real showAppsIcon in order to add a popupMenu. Most of + // its behavior is handled by the taskbar, but its positioning is done at the panel level + this.showAppsIconWrapper = new AppIcons.ShowAppsIconWrapper(this) + this.panel.add_child(this.showAppsIconWrapper.realShowAppsIcon) + + this.panel._delegate = this + + this.add_child(this.panel) + + if (Main.panel._onButtonPress || Main.panel._tryDragWindow) { + this._signalsHandler.add([ + this.panel, + ['button-press-event', 'touch-event'], + this._onButtonPress.bind(this), + ]) + } + + if (Main.panel._onKeyPress) { + this._signalsHandler.add([ + this.panel, + 'key-press-event', + Main.panel._onKeyPress.bind(this), + ]) + } + + Main.ctrlAltTabManager.addGroup( + this, + _('Top Bar') + ' ' + monitor.index, + 'focus-top-bar-symbolic', + { sortGroup: CtrlAltTab.SortGroup.TOP }, + ) + } + + enable() { + let { name: systemMenuName } = Utils.getSystemMenuInfo() + + if ( + this.statusArea[systemMenuName] && + this.statusArea[systemMenuName]._volumeOutput + ) { + Utils.getIndicators( + this.statusArea[systemMenuName]._volumeOutput, + )._dtpIgnoreScroll = 1 + } + + this._setPanelBoxStyle() + this._maybeSetDockCss() + this._setPanelPosition() + + if (!this.isStandalone) { + this._injectionManager.overrideMethod( + Object.getPrototypeOf(this.panel), + 'vfunc_allocate', + () => (box) => this._mainPanelAllocate(box), + ) + + // remove the extra space before the clock when the message-indicator is displayed + if (DateMenu.IndicatorPad) { + this._injectionManager.overrideMethod( + DateMenu.IndicatorPad.prototype, + 'vfunc_get_preferred_width', + () => () => [0, 0], + ) + this._injectionManager.overrideMethod( + DateMenu.IndicatorPad.prototype, + 'vfunc_get_preferred_height', + () => () => [0, 0], + ) + } + } + + if (!DateMenu.IndicatorPad && this.statusArea.dateMenu) { + //3.36 switched to a size constraint applied on an anonymous child + let indicatorPad = this.statusArea.dateMenu + .get_first_child() + .get_first_child() + + this._dateMenuIndicatorPadContraints = indicatorPad.get_constraints() + indicatorPad.clear_constraints() + } + + this.menuManager._oldChangeMenu = this.menuManager._changeMenu + this.menuManager._changeMenu = (menu) => { + if (!SETTINGS.get_boolean('stockgs-panelbtn-click-only')) { + this.menuManager._oldChangeMenu(menu) + } + } + + this.dynamicTransparency = new Transparency.DynamicTransparency(this) + + this.taskbar = new Taskbar.Taskbar(this) + + this.panel.add_child(this.taskbar.actor) + + this._setShowDesktopButton(true) + + this._setAllocationMap() + + this.panel.add_style_class_name( + 'zorintaskbarMainPanel ' + this.getOrientation(), + ) + + this.intellihide = new Intellihide.Intellihide(this) + + this._signalsHandler.add( + // this is to catch changes to the theme or window scale factor + [ + Utils.getStageTheme(), + 'changed', + () => (this._resetGeometry(), this._setShowDesktopButtonStyle()), + ], + [ + // sync hover after a popupmenu is closed + this.taskbar, + 'menu-closed', + () => this.panel.sync_hover(), + ], + [Main.overview, ['showing', 'hiding'], () => this._adjustForOverview()], + [ + Main.overview, + 'hidden', + () => { + if (this.isPrimary) { + //reset the primary monitor when exiting the overview + this.panelManager.setFocusedMonitor(this.monitor) + } + }, + ], + [ + this.statusArea.activities, + 'captured-event', + (actor, e) => { + if ( + e.type() == Clutter.EventType.BUTTON_PRESS || + e.type() == Clutter.EventType.TOUCH_BEGIN + ) { + //temporarily use as primary the monitor on which the activities btn was clicked + this.panelManager.setFocusedMonitor(this.monitor) + } + }, + ], + [ + this._centerBox, + 'child-added', + () => this._onBoxActorAdded(this._centerBox), + ], + [ + this._rightBox, + 'child-added', + () => this._onBoxActorAdded(this._rightBox), + ], + [Main.layoutManager, 'startup-complete', () => this._resetGeometry()], + ) + + this._bindSettingsChanges() + + this.panelStyle.enable(this) + + if (this.checkIfVertical()) { + this._signalsHandler.add([ + this.panelBox, + 'notify::visible', + () => { + if (this.panelBox.visible) { + this._refreshVerticalAlloc() + } + }, + ]) + + if (this.statusArea.dateMenu) { + this._formatVerticalClock() + + this._signalsHandler.add([ + this.statusArea.dateMenu._clock, + 'notify::clock', + () => this._formatVerticalClock(), + ]) + } + } + + // Since we are usually visible but not usually changing, make sure + // most repaint requests don't actually require us to repaint anything. + // This saves significant CPU when repainting the screen. + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS) + + if (!Main.layoutManager._startingUp) + GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._resetGeometry() + return GLib.SOURCE_REMOVE + }) + } + + disable() { + this.panelStyle.disable() + + this._timeoutsHandler.destroy() + this._signalsHandler.destroy() + + this.panel.remove_child(this.taskbar.actor) + + if (this.intellihide) { + this.intellihide.destroy() + } + + this.dynamicTransparency.destroy() + + this.taskbar.destroy() + this.showAppsIconWrapper.destroy() + + this._setPanelBoxStyle(true) + this._maybeSetDockCss(true) + + this.menuManager._changeMenu = this.menuManager._oldChangeMenu + + this._unmappedButtons.forEach((a) => this._disconnectVisibleId(a)) + + if (this.statusArea.dateMenu) { + this.statusArea.dateMenu._clockDisplay.text = + this.statusArea.dateMenu._clock.clock + this.statusArea.dateMenu._clockDisplay.clutter_text.set_width(-1) + + if (this._dateMenuIndicatorPadContraints) { + let indicatorPad = this.statusArea.dateMenu + .get_first_child() + .get_first_child() + + this._dateMenuIndicatorPadContraints.forEach((c) => + indicatorPad.add_constraint(c), + ) + } + } + + this._setVertical(this.panel, false) + this._setVertical(this._centerBox, false) + this._setVertical(this._rightBox, false) + + let { name: systemMenuName } = Utils.getSystemMenuInfo() + + if (!this.isStandalone) { + ;['vertical', 'horizontal', 'zorintaskbarMainPanel'].forEach((c) => + this.panel.remove_style_class_name(c), + ) + + if (!Main.sessionMode.isLocked) { + ;['activities', systemMenuName, 'dateMenu'].forEach((b) => { + let container = this.statusArea[b].container + let originalParent = container._dtpOriginalParent + + this.panel.remove_child(container) + + if (originalParent) { + originalParent.visible = true + + originalParent.insert_child_at_index( + container, + Math.min( + container._dtpOriginalIndex, + originalParent.get_children().length - 1, + ), + ) + } + + delete container._dtpOriginalParent + delete container._dtpOriginalIndex + }) + } + + this._setShowDesktopButton(false) + + delete Utils.getIndicators( + this.statusArea[systemMenuName]._volumeOutput, + )._dtpIgnoreScroll + + this._injectionManager.clear() + + this.panel._delegate = this.panel + } else { + this._removePanelMenu('dateMenu') + this._removePanelMenu(systemMenuName) + this._removePanelMenu('activities') + } + + Main.ctrlAltTabManager.removeGroup(this) + } + + handleDragOver(source) { + if ( + source == Main.xdndHandler && + Main.overview.shouldToggleByCornerOrButton() + ) { + this.panelManager.showFocusedAppInOverview(null, true) + Main.overview.show() + } + + return DND.DragMotionResult.CONTINUE + } + + getPosition() { + let position = PanelSettings.getPanelPosition( + SETTINGS, + this.monitor.index, + ) + + if (position == Pos.TOP) { + return St.Side.TOP + } else if (position == Pos.RIGHT) { + return St.Side.RIGHT + } else if (position == Pos.BOTTOM) { + return St.Side.BOTTOM + } + + return St.Side.LEFT + } + + checkIfVertical() { + let position = this.getPosition() + + return position == St.Side.LEFT || position == St.Side.RIGHT + } + + getOrientation() { + return this.checkIfVertical() ? 'vertical' : 'horizontal' + } + + updateElementPositions() { + let panelPositions = PanelSettings.getPanelElementPositions( + SETTINGS, + this.monitor.index, + ) + + this._updateGroupedElements(panelPositions) + + this.panel.hide() + this.panel.show() + } + + _updateGroupedElements(panelPositions) { + let previousPosition = 0 + let previousCenteredPosition = 0 + let currentGroup = -1 + + this._elementGroups = [] + + panelPositions.forEach((pos) => { + let allocationMap = this.allocationMap[pos.element] + + if (allocationMap.actor) { + allocationMap.actor.visible = pos.visible + + if (!pos.visible) return + + // if the panel length is dynamic, get all visible + // elements as a single group + let currentPosition = this.geom.dynamic || pos.position + let isCentered = Pos.checkIfCentered(currentPosition) + + if ( + currentPosition == Pos.STACKED_TL && + previousPosition == Pos.STACKED_BR + ) { + currentPosition = Pos.STACKED_BR + } + + if ( + !previousPosition || + (previousPosition == Pos.STACKED_TL && + currentPosition != Pos.STACKED_TL) || + (previousPosition != Pos.STACKED_BR && + currentPosition == Pos.STACKED_BR) || + (isCentered && + previousPosition != currentPosition && + previousPosition != Pos.STACKED_BR) + ) { + this._elementGroups[++currentGroup] = { + elements: [], + index: this._elementGroups.length, + expandableIndex: -1, + } + previousCenteredPosition = 0 + } + + if (pos.element == Pos.TASKBAR) { + this._elementGroups[currentGroup].expandableIndex = + this._elementGroups[currentGroup].elements.length + } + + if (isCentered && !this._elementGroups[currentGroup].isCentered) { + this._elementGroups[currentGroup].isCentered = 1 + previousCenteredPosition = currentPosition + } + + this._elementGroups[currentGroup].position = + previousCenteredPosition || currentPosition + this._elementGroups[currentGroup].elements.push(allocationMap) + + allocationMap.position = currentPosition + previousPosition = currentPosition + } + }) + } + + _bindSettingsChanges() { + let isVertical = this.checkIfVertical() + + this._signalsHandler.add( + [ + SETTINGS, + [ + 'changed::panel-margin', + 'changed::panel-sizes', + 'changed::group-apps', + ], + (settings, settingChanged) => { + PanelSettings.clearCache(settingChanged) + this._resetGeometry() + }, + ], + [ + SETTINGS, + [ + 'changed::showdesktop-button-width', + 'changed::show-showdesktop-icon', + ], + () => this._setShowDesktopButtonStyle(), + ], + [ + DESKTOPSETTINGS, + 'changed::clock-format', + () => { + this._clockFormat = null + + if (isVertical) { + this._formatVerticalClock() + } + }, + ], + ) + + if (isVertical) { + this._signalsHandler.add([ + SETTINGS, + 'changed::group-apps-label-max-width', + () => this._resetGeometry(), + ]) + } + + if (TILINGSETTINGS != null) { + this._signalsHandler.add([ + TILINGSETTINGS, + 'changed::outer-gaps', + () => this._resetGeometry(), + ]) + } + } + + _setPanelMenu(propName, constr, container) { + if (!this.statusArea[propName]) { + this.statusArea[propName] = this._getPanelMenu(propName, constr) + this.menuManager.addMenu(this.statusArea[propName].menu) + container.insert_child_at_index(this.statusArea[propName].container, 0) + } + } + + _removePanelMenu(propName) { + if (this.statusArea[propName]) { + let parent = this.statusArea[propName].container.get_parent() + + if (parent) { + parent.remove_child(this.statusArea[propName].container) + } + + //calling this.statusArea[propName].destroy(); is buggy for now, gnome-shell never + //destroys those panel menus... + //since we can't destroy the menu (hence properly disconnect its signals), let's + //store it so the next time a panel needs one of its kind, we can reuse it instead + //of creating a new one + let panelMenu = this.statusArea[propName] + + this.menuManager.removeMenu(panelMenu.menu) + PERSISTENTSTORAGE[propName].push(panelMenu) + this.statusArea[propName] = null + } + } + + _getPanelMenu(propName, constr) { + PERSISTENTSTORAGE[propName] = PERSISTENTSTORAGE[propName] || [] + + if (!PERSISTENTSTORAGE[propName].length) { + PERSISTENTSTORAGE[propName].push(new constr()) + } + + return PERSISTENTSTORAGE[propName].pop() + } + + _adjustForOverview() { + let isFocusedMonitor = this.panelManager.checkIfFocusedMonitor( + this.monitor, + ) + let isOverview = !!Main.overview.visibleTarget + let isOverviewFocusedMonitor = isOverview && isFocusedMonitor + let isShown = !isOverview || isOverviewFocusedMonitor + let actorData = Utils.getTrackedActorData(this.panelBox) + + // prevent the "chrome" to update the panelbox visibility while in overview + actorData.trackFullscreen = !isOverview + + this.panelBox[isShown ? 'show' : 'hide']() + } + + _resetGeometry() { + this._setPanelBoxStyle() + this.geom = this.getGeometry() + this._maybeSetDockCss() + this._setPanelPosition() + this.taskbar.resetAppIcons(true) + this.dynamicTransparency.updateExternalStyle() + + if (this.checkIfVertical()) { + this.showAppsIconWrapper.realShowAppsIcon.toggleButton.set_width( + this.geom.innerSize, + ) + this._refreshVerticalAlloc() + } + } + + getGeometry() { + let isVertical = this.checkIfVertical() + let scaleFactor = Utils.getScaleFactor() + let panelBoxTheme = this.panelBox.get_theme_node() + let sideMargins = + panelBoxTheme.get_padding(St.Side.LEFT) + + panelBoxTheme.get_padding(St.Side.RIGHT) + let topBottomMargins = + panelBoxTheme.get_padding(St.Side.TOP) + + panelBoxTheme.get_padding(St.Side.BOTTOM) + let position = this.getPosition() + let panelLength = PanelSettings.getPanelLength( + SETTINGS, + this.monitor.index, + ) + let anchor = PanelSettings.getPanelAnchor(SETTINGS, this.monitor.index) + let dynamic = panelLength == -1 ? Pos.anchorToPosition[anchor] : 0 + let dockMode = false + let length = (dynamic ? 100 : panelLength) / 100 + let gsTopPanelHeight = 0 + let x = 0 + let y = 0 + let w = 0 + let h = 0 + let fixedPadding = 0 + let varPadding = 0 + let topOffset = 0 + let iconSize = 0 + let innerSize = 0 + let outerSize = 0 + let panelSize = PanelSettings.getPanelSize(SETTINGS, this.monitor.index) + + iconSize = innerSize = outerSize = panelSize * scaleFactor + + if ( + SETTINGS.get_boolean('stockgs-keep-top-panel') && + Main.layoutManager.primaryMonitor == this.monitor + ) { + gsTopPanelHeight = Main.layoutManager.panelBox.height + topOffset = position == St.Side.TOP ? gsTopPanelHeight : 0 + } + + if (isVertical) { + if (!SETTINGS.get_boolean('group-apps')) { + // add window title width and side padding of _dtpIconContainer when vertical + innerSize = outerSize += + SETTINGS.get_int('group-apps-label-max-width') + + (AppIcons.DEFAULT_PADDING_SIZE * 2) / scaleFactor + } + + this.sizeFunc = 'get_preferred_height' + this.fixedCoord = { c1: 'x1', c2: 'x2' } + this.varCoord = { c1: 'y1', c2: 'y2' } + + w = innerSize + h = this.monitor.height * length - topBottomMargins - gsTopPanelHeight + dockMode = !!dynamic || topBottomMargins > 0 || h < this.monitor.height + outerSize += sideMargins + } else { + this.sizeFunc = 'get_preferred_width' + this.fixedCoord = { c1: 'y1', c2: 'y2' } + this.varCoord = { c1: 'x1', c2: 'x2' } + + w = this.monitor.width * length - sideMargins + h = innerSize + dockMode = !!dynamic || sideMargins > 0 || w < this.monitor.width + outerSize += topBottomMargins - topOffset + } + + if (position == St.Side.TOP) { + x = this.monitor.x + y = this.monitor.y + } else if (position == St.Side.LEFT) { + x = this.monitor.x + y = this.monitor.y + gsTopPanelHeight + } else if (position == St.Side.RIGHT) { + x = this.monitor.x + this.monitor.width - w - sideMargins + y = this.monitor.y + gsTopPanelHeight + } else { + //BOTTOM + x = this.monitor.x + y = this.monitor.y + this.monitor.height - h - topBottomMargins + } + + if (length < 1) { + // fixed size, less than 100%, so adjust start coordinate + if (!isVertical && anchor == Pos.MIDDLE) + x += (this.monitor.width - w - sideMargins) * 0.5 + else if (isVertical && anchor == Pos.MIDDLE) + y += (this.monitor.height - h - topBottomMargins) * 0.5 + else if (!isVertical && anchor == Pos.END) + x += this.monitor.width - w - sideMargins + else if (isVertical && anchor == Pos.END) + y += this.monitor.height - h - topBottomMargins + } + + innerSize -= fixedPadding * 2 + iconSize -= fixedPadding * 2 + + return { + x, + y, + w, + h, + iconSize, // selected panel thickness in settings + innerSize, // excludes padding and margins + outerSize, // includes padding and margins + fixedPadding, + varPadding, + topOffset, // only if gnome-shell top panel is present and position is TOP + position, + dynamic, + dockMode, + } + } + + _setAllocationMap() { + this.allocationMap = {} + let setMap = (name, actor) => + (this.allocationMap[name] = { + actor: actor, + box: new Clutter.ActorBox(), + }) + + setMap(Pos.SHOW_APPS_BTN, this.showAppsIconWrapper.realShowAppsIcon) + setMap( + Pos.ACTIVITIES_BTN, + this.statusArea.activities ? this.statusArea.activities.container : 0, + ) + setMap(Pos.LEFT_BOX, this._leftBox) + setMap(Pos.TASKBAR, this.taskbar.actor) + setMap(Pos.CENTER_BOX, this._centerBox) + setMap(Pos.DATE_MENU, this.statusArea.dateMenu.container) + setMap( + Pos.SYSTEM_MENU, + this.statusArea[Utils.getSystemMenuInfo().name].container, + ) + setMap(Pos.RIGHT_BOX, this._rightBox) + setMap(Pos.DESKTOP_BTN, this._showDesktopButton) + } + + _mainPanelAllocate(box) { + this.panel.set_allocation(box) + } + + vfunc_allocate(box) { + let fixed = 0 + let centeredMonitorGroup + let varSize = box[this.varCoord.c2] - box[this.varCoord.c1] + let fixedSize = box[this.fixedCoord.c2] - box[this.fixedCoord.c1] + let panelAlloc = new Clutter.ActorBox() + let assignGroupSize = (group, update) => { + group.size = 0 + group.tlOffset = 0 + group.brOffset = 0 + + group.elements.forEach((element) => { + if (!update) { + element.box[this.fixedCoord.c1] = + panelAlloc[this.fixedCoord.c1] + this.geom.fixedPadding + element.box[this.fixedCoord.c2] = + panelAlloc[this.fixedCoord.c2] - this.geom.fixedPadding + element.natSize = element.actor[this.sizeFunc](-1)[1] + } + + if (!group.isCentered || Pos.checkIfCentered(element.position)) { + group.size += element.natSize + } else if (element.position == Pos.STACKED_TL) { + group.tlOffset += element.natSize + } else { + // Pos.STACKED_BR + group.brOffset += element.natSize + } + }) + + if (group.isCentered) { + group.size += Math.max(group.tlOffset, group.brOffset) * 2 + group.tlOffset = Math.max(group.tlOffset - group.brOffset, 0) + } + } + let allocateGroup = (group, tlLimit, brLimit) => { + let startPosition = tlLimit + let currentPosition = 0 + + if (group.expandableIndex >= 0) { + let availableSize = brLimit - tlLimit + let expandable = group.elements[group.expandableIndex] + let i = 0 + let l = this._elementGroups.length + let tlSize = 0 + let brSize = 0 + + if ( + centeredMonitorGroup && + (centeredMonitorGroup != group || + expandable.position != Pos.CENTERED_MONITOR) + ) { + if ( + centeredMonitorGroup.index < group.index || + (centeredMonitorGroup == group && + expandable.position == Pos.STACKED_TL) + ) { + i = centeredMonitorGroup.index + } else { + l = centeredMonitorGroup.index + } + } + + for (; i < l; ++i) { + let refGroup = this._elementGroups[i] + + if ( + i < group.index && + (!refGroup.fixed || refGroup[this.varCoord.c2] > tlLimit) + ) { + tlSize += refGroup.size + } else if ( + i > group.index && + (!refGroup.fixed || refGroup[this.varCoord.c1] < brLimit) + ) { + brSize += refGroup.size + } + } + + if (group.isCentered) { + availableSize -= Math.max(tlSize, brSize) * 2 + } else { + availableSize -= tlSize + brSize + } + + if (availableSize < group.size) { + expandable.natSize -= + (group.size - availableSize) * + (group.isCentered && !Pos.checkIfCentered(expandable.position) + ? 0.5 + : 1) + assignGroupSize(group, true) + } + } + + if (group.isCentered) { + startPosition = tlLimit + (brLimit - tlLimit - group.size) * 0.5 + } else if (group.position == Pos.STACKED_BR) { + startPosition = brLimit - group.size + } + + currentPosition = group.tlOffset + startPosition + + group.elements.forEach((element) => { + element.box[this.varCoord.c1] = Math.round(currentPosition) + element.box[this.varCoord.c2] = Math.round( + (currentPosition += element.natSize), + ) + + element.actor.allocate(element.box) + }) + + group[this.varCoord.c1] = startPosition + group[this.varCoord.c2] = currentPosition + group.fixed = 1 + ++fixed + } + + panelAlloc[this.varCoord.c2] = varSize + panelAlloc[this.fixedCoord.c2] = fixedSize + + this._elementGroups.forEach((group) => { + group.fixed = 0 + + assignGroupSize(group) + + if (group.position == Pos.CENTERED_MONITOR) { + centeredMonitorGroup = group + } + }) + + if (this.geom.dynamic && this._elementGroups.length == 1) { + let dynamicGroup = this._elementGroups[0] // only one group if dynamic + let tl = box[this.varCoord.c1] + let br = box[this.varCoord.c2] + let groupSize = dynamicGroup.size + this.geom.varPadding * 2 + + if (this.geom.dynamic == Pos.STACKED_TL) { + br = Math.min(br, tl + groupSize) + } else if (this.geom.dynamic == Pos.STACKED_BR) { + tl = Math.max(tl, br - groupSize) + } else { + // CENTERED_MONITOR + let half = Math.max(0, Math.floor((br - tl - groupSize) * 0.5)) + + tl += half + br -= half + } + + box[this.varCoord.c1] = tl + box[this.varCoord.c2] = br + + panelAlloc[this.varCoord.c2] = Math.min(groupSize, br - tl) + } + + this.set_allocation(box) + this.panel.allocate(panelAlloc) + + // apply padding to panel's children, after panel allocation + panelAlloc[this.varCoord.c1] += this.geom.varPadding + panelAlloc[this.varCoord.c2] -= this.geom.varPadding + + if (centeredMonitorGroup) { + allocateGroup( + centeredMonitorGroup, + panelAlloc[this.varCoord.c1], + panelAlloc[this.varCoord.c2], + ) + } + + let iterations = 0 //failsafe + while (fixed < this._elementGroups.length && ++iterations < 10) { + for (let i = 0, l = this._elementGroups.length; i < l; ++i) { + let group = this._elementGroups[i] + + if (group.fixed) { + continue + } + + let prevGroup = this._elementGroups[i - 1] + let nextGroup = this._elementGroups[i + 1] + let prevLimit = + prevGroup && prevGroup.fixed + ? prevGroup[this.varCoord.c2] + : centeredMonitorGroup && group.index > centeredMonitorGroup.index + ? centeredMonitorGroup[this.varCoord.c2] + : panelAlloc[this.varCoord.c1] + let nextLimit = + nextGroup && nextGroup.fixed + ? nextGroup[this.varCoord.c1] + : centeredMonitorGroup && group.index < centeredMonitorGroup.index + ? centeredMonitorGroup[this.varCoord.c1] + : panelAlloc[this.varCoord.c2] + + if (group.position == Pos.STACKED_TL) { + allocateGroup(group, panelAlloc[this.varCoord.c1], nextLimit) + } else if (group.position == Pos.STACKED_BR) { + allocateGroup(group, prevLimit, panelAlloc[this.varCoord.c2]) + } else if ( + (!prevGroup || prevGroup.fixed) && + (!nextGroup || nextGroup.fixed) + ) { + // CENTERED + allocateGroup(group, prevLimit, nextLimit) + } + } + } + } + + _setPanelBoxStyle(disable) { + let style = '' + + if (!disable) { + let topBottomMargins = SETTINGS.get_int('panel-margin') + let sideMargins = topBottomMargins + + style = `padding: ${this.geom.topOffset + topBottomMargins}px ${sideMargins}px ${topBottomMargins}px;` + + if ( + TILINGSETTINGS != null && + Main.extensionManager._extensionOrder.includes(ZORIN_TILING_SHELL_UUID) && + TILINGSETTINGS.get_uint('outer-gaps') > 0 + ) { + let position = this.getPosition() + let margin_offset = TILINGSETTINGS.get_uint('outer-gaps') + let value = 0 + + if (position == St.Side.TOP) { + value = topBottomMargins - margin_offset + style += `padding-bottom: ${value > 0 ? value : 0}px;` + } else if (position == St.Side.BOTTOM) { + value = this.geom.topOffset + topBottomMargins - margin_offset + style += `padding-top: ${value > 0 ? value : 0}px;` + } else if (position == St.Side.LEFT) { + value = sideMargins - margin_offset + style += `padding-right: ${value > 0 ? value : 0}px;` + } else if (position == St.Side.RIGHT) { + value = sideMargins - margin_offset + style += `padding-left: ${value > 0 ? value : 0}px;` + } + } + } + + this.panelBox.set_style(style) + } + + _maybeSetDockCss(disable) { + this.remove_style_class_name('dock') + + if (!disable && this.geom.dockMode) this.add_style_class_name('dock') + } + + _setPanelPosition() { + let clipContainer = this.panelBox.get_parent() + + this.set_size(this.geom.w, this.geom.h) + clipContainer.set_position(this.geom.x, this.geom.y) + + this._setVertical(this.panel, this.checkIfVertical()) + + // center the system menu popup relative to its panel button + if (this.statusArea.quickSettings?.menu) { + this.statusArea.quickSettings.menu._arrowSide = this.geom.position + this.statusArea.quickSettings.menu._arrowAlignment = 0.5 + } + + // styles for theming + Object.keys(St.Side).forEach((p) => { + let cssName = 'zorintaskbar' + p.charAt(0) + p.slice(1).toLowerCase() + + this.panel[ + (St.Side[p] == this.geom.position ? 'add' : 'remove') + + '_style_class_name' + ](cssName) + }) + + this._setPanelClip(clipContainer) + + Main.layoutManager._updateHotCorners() + Main.layoutManager._updatePanelBarrier(this) + } + + _setPanelClip(clipContainer) { + clipContainer = clipContainer || this.panelBox.get_parent() + this._timeoutsHandler.add([ + T6, + 0, + () => + Utils.setClip( + clipContainer, + clipContainer.x, + clipContainer.y, + this.panelBox.width, + this.panelBox.height, + 0, + this.geom.topOffset, + ), + ]) + } + + _onButtonPress(actor, event) { + let type = event.type() + let isPress = type == Clutter.EventType.BUTTON_PRESS + let button = isPress ? event.get_button() : -1 + let [stageX, stageY] = event.get_coords() + + if ( + button == 3 && + global.stage.get_actor_at_pos( + Clutter.PickMode.REACTIVE, + stageX, + stageY, + ) == this.panel + ) { + //right click on an empty part of the panel, temporarily borrow and display the showapps context menu + Main.layoutManager.setDummyCursorGeometry(stageX, stageY, 0, 0) + + this.showAppsIconWrapper.createMenu() + this.showAppsIconWrapper.popupMenu(Main.layoutManager.dummyCursor) + + return Clutter.EVENT_STOP + } else { + const targetActor = global.stage.get_event_actor(event) + + if ( + Main.modalCount > 0 || + targetActor != actor || + (!isPress && type != Clutter.EventType.TOUCH_BEGIN) || + (isPress && button != 1) + ) { + return Clutter.EVENT_PROPAGATE + } + } + + let params = this.checkIfVertical() + ? [stageY, 'y', 'height'] + : [stageX, 'x', 'width'] + let dragWindow = this._getDraggableWindowForPosition.apply( + this, + params.concat(['maximized_' + this.getOrientation() + 'ly']), + ) + + if (!dragWindow) return Clutter.EVENT_PROPAGATE + + dragWindow.begin_grab_op( + Meta.GrabOp.MOVING, + event.get_device(), + event.get_event_sequence(), + event.get_time(), + new Graphene.Point({ x: stageX, y: stageY }), + ) + + return Clutter.EVENT_STOP + } + + _getDraggableWindowForPosition( + stageCoord, + coord, + dimension, + maximizedProp, + ) { + let workspace = Utils.getCurrentWorkspace() + let allWindowsByStacking = global.display + .sort_windows_by_stacking(workspace.list_windows()) + .reverse() + + return Utils.find(allWindowsByStacking, (metaWindow) => { + let rect = metaWindow.get_frame_rect() + + return ( + metaWindow.get_monitor() == this.monitor.index && + metaWindow.showing_on_its_workspace() && + metaWindow.get_window_type() != Meta.WindowType.DESKTOP && + metaWindow[maximizedProp] && + stageCoord > rect[coord] && + stageCoord < rect[coord] + rect[dimension] + ) + }) + } + + _onBoxActorAdded(box) { + if (this.checkIfVertical()) { + this._setVertical(box, true) + } + } + + _refreshVerticalAlloc() { + this._setVertical(this._centerBox, true) + this._setVertical(this._rightBox, true) + this._formatVerticalClock() + } + + _setVertical(actor, isVertical) { + let _set = (actor, isVertical) => { + if ( + !actor || + actor instanceof Dash.DashItemContainer || + actor instanceof TaskbarItemContainer.TaskbarItemContainer + ) { + return + } + + if (actor instanceof St.BoxLayout) { + Utils.setBoxLayoutVertical(actor, isVertical) + } else if ( + actor != this.statusArea.appMenu && + ((actor._delegate || actor) instanceof PanelMenu.ButtonBox || + actor == this.statusArea.quickSettings) + ) { + let child = actor.get_first_child() + + if (isVertical && !actor.visible && !actor._dtpVisibleId) { + this._unmappedButtons.push(actor) + actor._dtpVisibleId = actor.connect('notify::visible', () => { + this._disconnectVisibleId(actor) + this._refreshVerticalAlloc() + }) + actor._dtpDestroyId = actor.connect('destroy', () => + this._disconnectVisibleId(actor), + ) + } + + if (child) { + let [, natWidth] = actor.get_preferred_width(-1) + + child.x_align = Clutter.ActorAlign[isVertical ? 'CENTER' : 'START'] + actor.set_width(isVertical ? this.geom.innerSize : -1) + isVertical = isVertical && natWidth > this.geom.innerSize + actor[(isVertical ? 'add' : 'remove') + '_style_class_name']( + 'vertical', + ) + } + } + + actor.get_children().forEach((c) => _set(c, isVertical)) + } + + _set(actor, false) + + if (isVertical) _set(actor, isVertical) + } + + _disconnectVisibleId(actor) { + actor.disconnect(actor._dtpVisibleId) + actor.disconnect(actor._dtpDestroyId) + + delete actor._dtpVisibleId + delete actor._dtpDestroyId + + this._unmappedButtons.splice(this._unmappedButtons.indexOf(actor), 1) + } + + _formatVerticalClock() { + // https://github.com/GNOME/gnome-desktop/blob/master/libgnome-desktop/gnome-wall-clock.c#L310 + if (this.statusArea.dateMenu) { + let datetime = this.statusArea.dateMenu._clock.clock + let datetimeParts = datetime.split(' ') + let time = datetimeParts[1] + let clockText = this.statusArea.dateMenu._clockDisplay.clutter_text + let setClockText = (text, useTimeSeparator) => { + let stacks = text instanceof Array + let separator = `\n ${useTimeSeparator ? '‧‧' : '—'} \n` + + clockText.set_text((stacks ? text.join(separator) : text).trim()) + clockText.set_use_markup(stacks) + clockText.get_allocation_box() + + return !clockText.get_layout().is_ellipsized() + } + + if (clockText.ellipsize == Pango.EllipsizeMode.NONE) { + //on gnome-shell 3.36.4, the clockdisplay isn't ellipsize anymore, so set it back + clockText.ellipsize = Pango.EllipsizeMode.END + } + + clockText.natural_width = this.geom.innerSize + + if (!time) { + datetimeParts = datetime.split(' ') + time = datetimeParts.pop() + datetimeParts = [datetimeParts.join(' '), time] + } + + if ( + !setClockText(datetime) && + !setClockText(datetimeParts) && + !setClockText(time) + ) { + let timeParts = time.split('∶') + + if (!this._clockFormat) { + this._clockFormat = DESKTOPSETTINGS.get_string('clock-format') + } + + if (this._clockFormat == '12h') { + timeParts.push.apply(timeParts, timeParts.pop().split(' ')) + } + + setClockText(timeParts, true) + } + } + } + + _setShowDesktopButton(add) { + if (add) { + if (this._showDesktopButton) return + + this._showDesktopButton = new St.Bin({ + style_class: 'panel-button', + reactive: true, + can_focus: true, + // x_fill: true, + // y_fill: true, + track_hover: true, + }) + + this._showDesktopButton.icon = new St.Icon({ + gicon: Gio.icon_new_for_string( + `${EXTENSION_PATH}/img/show-desktop-symbolic.svg` + ), + style_class: 'system-status-icon' + }) + + this._setShowDesktopButtonStyle() + + this._showDesktopButton.connect('touch-event', (actor, event) => { + if (event.type() == Clutter.EventType.TOUCH_BEGIN) { + this._onShowDesktopButtonPress() + } + }) + this._showDesktopButton.connect('button-press-event', () => + this._onShowDesktopButtonPress(), + ) + this._showDesktopButton.connect('enter-event', () => { + if (SETTINGS.get_boolean('show-showdesktop-hover')) { + this._timeoutsHandler.add([ + T4, + SHOW_SHOWDESKTOP_DELAY, + () => { + this._hiddenDesktopWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + this._toggleWorkspaceWindows(true, this._hiddenDesktopWorkspace) + }, + ]) + } + }) + + this._showDesktopButton.connect('leave-event', () => { + if (SETTINGS.get_boolean('show-showdesktop-hover')) { + if (this._timeoutsHandler.getId(T4)) { + this._timeoutsHandler.remove(T4) + } else if (this._hiddenDesktopWorkspace) { + this._toggleWorkspaceWindows(false, this._hiddenDesktopWorkspace) + } + } + }) + + this.panel.add_child(this._showDesktopButton) + } else { + if (!this._showDesktopButton) return + + this.panel.remove_child(this._showDesktopButton) + this._showDesktopButton.destroy() + this._showDesktopButton = null + } + } + + _setShowDesktopButtonStyle() { + let rgb = this._getBackgroundBrightness() + ? 'rgba(55, 55, 55, .2)' + : 'rgba(200, 200, 200, .2)' + + for (let i = 0; i < this._showDesktopButton.get_children().length; i++) { + if (this._showDesktopButton.get_children()[i] == this._showDesktopButton.icon) { + this._showDesktopButton.remove_child(this._showDesktopButton.icon) + } + } + + if (this._showDesktopButton) { + if (SETTINGS.get_boolean('show-showdesktop-icon')) { + this._showDesktopButton.add_child(this._showDesktopButton.icon) + + let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px;' + let isVertical = this.checkIfVertical() + let buttonPadding = isVertical ? buttonSize + ' 0;' : '0 ' + buttonSize + ';' + + this._showDesktopButton.set_style('padding: ' + buttonPadding + ';') + } else { + let buttonSize = SETTINGS.get_int('showdesktop-button-width') + 'px;' + let isVertical = this.checkIfVertical() + + let style = 'border: 0 solid ' + rgb + '; padding: 0;' + style += isVertical + ? 'border-top-width:1px;height:' + buttonSize + : 'border-left-width:1px;width:' + buttonSize + + this._showDesktopButton.set_style(style) + this._showDesktopButton[(isVertical ? 'x' : 'y') + '_expand'] = true + } + } + } + + // _getBackgroundBrightness: return true if panel has a bright background color + _getBackgroundBrightness() { + return Utils.checkIfColorIsBright( + this.dynamicTransparency.backgroundColorRgb, + ) + } + + _toggleWorkspaceWindows(hide, workspace) { + let time = SHOW_SHOWDESKTOP_TIME * 0.001 + + workspace.list_windows().forEach((w) => { + if (!w.minimized && !w.customJS_ding && w.window_type != Meta.WindowType.DESKTOP) { + let tweenOpts = { + opacity: hide ? 0 : 255, + time: time, + transition: 'easeOutQuad', + } + + Utils.animateWindowOpacity(w.get_compositor_private(), tweenOpts) + } + }) + } + + _onShowDesktopButtonPress() { + let label = 'trackerFocusApp' + + this._signalsHandler.removeWithLabel(label) + this._timeoutsHandler.remove(T5) + + if (this._restoreWindowList && this._restoreWindowList.length) { + this._timeoutsHandler.remove(T4) + + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = current_workspace.list_windows() + this._restoreWindowList.forEach(function (w) { + if (windows.indexOf(w) > -1) Main.activateWindow(w) + }) + this._restoreWindowList = null + } else { + let current_workspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + let windows = current_workspace.list_windows().filter(function (w) { + return w.showing_on_its_workspace() && !w.skip_taskbar + }) + windows = global.display.sort_windows_by_stacking(windows) + + windows.forEach(function (w) { + w.minimize() + }) + + this._restoreWindowList = windows + + this._timeoutsHandler.add([ + T5, + 20, + () => + this._signalsHandler.addWithLabel(label, [ + tracker, + 'notify::focus-app', + () => (this._restoreWindowList = null), + ]), + ]) + } + + Main.overview.hide() + } + }, +) + +export const SecondaryPanel = GObject.registerClass( + {}, + class SecondaryPanel extends St.Widget { + _init(params) { + super._init(params) + } + + vfunc_allocate(box) { + this.set_allocation(box) + } + }, +) diff --git a/src/panelManager.js b/src/panelManager.js new file mode 100644 index 0000000..5b2e9a2 --- /dev/null +++ b/src/panelManager.js @@ -0,0 +1,1083 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * + * Code to re-anchor the panel was taken from Thoma5 BottomPanel: + * https://github.com/Thoma5/gnome-shell-extension-bottompanel + * + * Pattern for moving clock based on Frippery Move Clock by R M Yorston + * http://frippery.org/extensions/ + * + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import * as Overview from './overview.js' +import * as Panel from './panel.js' +import * as PanelSettings from './panelSettings.js' +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import * as DesktopIconsIntegration from './desktopIconsIntegration.js' +import { DTP_EXTENSION, SETTINGS, tracker } from './extension.js' + +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Clutter from 'gi://Clutter' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' + +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js' +import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js' +import { NotificationsMonitor } from './notificationsMonitor.js' +import { Workspace } from 'resource:///org/gnome/shell/ui/workspace.js' +import * as Layout from 'resource:///org/gnome/shell/ui/layout.js' +import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js' +import { + SecondaryMonitorDisplay, + WorkspacesView, +} from 'resource:///org/gnome/shell/ui/workspacesView.js' + +export const PanelManager = class { + constructor() { + this.overview = new Overview.Overview(this) + this._injectionManager = new InjectionManager() + } + + enable(reset) { + this.allPanels = [] + this.dtpPrimaryMonitor = Main.layoutManager.primaryMonitor // The real primary monitor should always have the main panel + this.proximityManager = new Proximity.ProximityManager() + + if (this.dtpPrimaryMonitor) { + this.primaryPanel = this._createPanel( + this.dtpPrimaryMonitor, + SETTINGS.get_boolean('stockgs-keep-top-panel'), + ) + this.allPanels.push(this.primaryPanel) + this.overview.enable(this.primaryPanel) + + this.setFocusedMonitor(this.dtpPrimaryMonitor) + } + + if (SETTINGS.get_boolean('multi-monitors')) { + Main.layoutManager.monitors + .filter((m) => m != this.dtpPrimaryMonitor) + .forEach((m) => { + this.allPanels.push(this._createPanel(m, true)) + }) + } + + global.zorinTaskbar.panels = this.allPanels + global.zorinTaskbar.emit('panels-created') + + this._setDesktopIconsMargins() + + this._updatePanelElementPositions() + + if (reset) return + + this.notificationsMonitor = new NotificationsMonitor() + + this._desktopIconsUsableArea = + new DesktopIconsIntegration.DesktopIconsUsableAreaClass() + + this._oldUpdatePanelBarrier = Main.layoutManager._updatePanelBarrier + Main.layoutManager._updatePanelBarrier = (panel) => { + let panelUpdates = panel ? [panel] : this.allPanels + + panelUpdates.forEach((p) => + newUpdatePanelBarrier.call(Main.layoutManager, p), + ) + } + Main.layoutManager._updatePanelBarrier() + + this._oldUpdateHotCorners = Main.layoutManager._updateHotCorners + Main.layoutManager._updateHotCorners = newUpdateHotCorners.bind( + Main.layoutManager, + ) + Main.layoutManager._updateHotCorners() + + Main.layoutManager.findIndexForActor = (actor) => + '_dtpIndex' in actor + ? actor._dtpIndex + : Layout.LayoutManager.prototype.findIndexForActor.call( + Main.layoutManager, + actor, + ) + + if (Main.layoutManager._interfaceSettings) { + this._enableHotCornersId = Main.layoutManager._interfaceSettings.connect( + 'changed::enable-hot-corners', + () => Main.layoutManager._updateHotCorners(), + ) + } + + this._oldUpdateWorkspacesViews = + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = + this._newUpdateWorkspacesViews.bind( + Main.overview._overview._controls._workspacesDisplay, + ) + + this._oldSetPrimaryWorkspaceVisible = + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = + this._newSetPrimaryWorkspaceVisible.bind( + Main.overview._overview._controls._workspacesDisplay, + ) + + let panelManager = this + this._injectionManager.overrideMethod( + BoxPointer.BoxPointer.prototype, + 'vfunc_get_preferred_height', + () => + function (forWidth) { + let alloc = { min_size: 0, natural_size: 0 } + + ;[alloc.min_size, alloc.natural_size] = + this.vfunc_get_preferred_height(forWidth) + + return panelManager._getBoxPointerPreferredHeight(this, alloc) + }, + ) + + this._injectionManager.overrideMethod( + Object.getPrototypeOf( + // WorkspaceDot in activities button + Main.panel.statusArea.activities.get_first_child().get_first_child(), + ), + 'vfunc_get_preferred_width', + (get_preferred_width) => + function (forHeight) { + return Utils.getBoxLayoutVertical(this.get_parent()) + ? [0, forHeight] + : get_preferred_width.call(this, forHeight) + }, + ) + + LookingGlass.LookingGlass.prototype._oldResize = + LookingGlass.LookingGlass.prototype._resize + LookingGlass.LookingGlass.prototype._resize = _newLookingGlassResize + + LookingGlass.LookingGlass.prototype._oldOpen = + LookingGlass.LookingGlass.prototype.open + LookingGlass.LookingGlass.prototype.open = _newLookingGlassOpen + + Main.messageTray._bannerBin.ease = (params) => { + if (params.y === 0) { + let panelOnPrimary = this.allPanels.find( + (p) => p.monitor == Main.layoutManager.primaryMonitor, + ) + + if ( + panelOnPrimary && + panelOnPrimary.intellihide?.enabled && + panelOnPrimary.geom.position == St.Side.TOP && + panelOnPrimary.panelBox.visible + ) + params.y += panelOnPrimary.geom.outerSize + } + + Object.getPrototypeOf(Main.messageTray._bannerBin).ease.call( + Main.messageTray._bannerBin, + params, + ) + } + + this._signalsHandler = new Utils.GlobalSignalsHandler() + + //listen settings + this._signalsHandler.add( + [ + SETTINGS, + 'changed::global-border-radius', + () => DTP_EXTENSION.resetGlobalStyles(), + ], + [ + SETTINGS, + [ + 'changed::multi-monitors', + 'changed::isolate-monitors', + 'changed::panel-positions', + 'changed::panel-lengths', + 'changed::panel-anchors', + 'changed::stockgs-keep-top-panel', + ], + (settings, settingChanged) => { + PanelSettings.clearCache(settingChanged) + this._reset() + }, + ], + [ + SETTINGS, + 'changed::panel-element-positions', + () => { + PanelSettings.clearCache('panel-element-positions') + this._updatePanelElementPositions() + }, + ], + [ + SETTINGS, + 'changed::intellihide-key-toggle-text', + () => this._setKeyBindings(true), + ], + [ + SETTINGS, + 'changed::panel-sizes', + () => { + GLib.idle_add(GLib.PRIORITY_LOW, () => { + this._setDesktopIconsMargins() + return GLib.SOURCE_REMOVE + }) + }, + ], + [ + Utils.DisplayWrapper.getMonitorManager(), + 'monitors-changed', + async () => { + if (Main.layoutManager.primaryMonitor) { + await PanelSettings.setMonitorsInfo(SETTINGS) + this._reset() + } + }, + ], + ) + + Panel.panelBoxes.forEach((c) => + this._signalsHandler.add([ + Main.panel[c], + 'child-added', + (parent, child) => { + this.primaryPanel && + child instanceof St.Bin && + this._adjustPanelMenuButton( + this._getPanelMenuButton(child.get_first_child()), + this.primaryPanel.monitor, + this.primaryPanel.getPosition(), + ) + }, + ]), + ) + + this._setKeyBindings(true) + + // keep GS overview.js from blowing away custom panel styles + if (!SETTINGS.get_boolean('stockgs-keep-top-panel')) + Object.defineProperty(Main.panel, 'style', { + configurable: true, + set() {}, + }) + } + + disable(reset) { + this.primaryPanel && this.overview.disable() + this.proximityManager.destroy() + + this.allPanels.forEach((p) => { + p.taskbar.iconAnimator.pause() + + this._findPanelMenuButtons(p.panelBox).forEach((pmb) => { + if (pmb.menu._boxPointer._dtpGetPreferredHeightId) { + pmb.menu._boxPointer._container.disconnect( + pmb.menu._boxPointer._dtpGetPreferredHeightId, + ) + } + + pmb.menu._boxPointer.sourceActor = pmb.menu._boxPointer._dtpSourceActor + delete pmb.menu._boxPointer._dtpSourceActor + pmb.menu._boxPointer._userArrowSide = St.Side.TOP + }) + + this._removePanelBarriers(p) + + p.disable() + + let clipContainer = p.panelBox.get_parent() + + Main.layoutManager._untrackActor(p.panelBox) + Main.layoutManager.removeChrome(clipContainer) + + if (p.isStandalone) { + p.panelBox.destroy() + } else { + p.panelBox.remove_child(p) + p.remove_child(p.panel) + p.panelBox.add_child(p.panel) + + p.panelBox.set_position(clipContainer.x, clipContainer.y) + + clipContainer.remove_child(p.panelBox) + Main.layoutManager.addChrome(p.panelBox, { + affectsStruts: true, + trackFullscreen: true, + }) + } + }) + + if (Main.layoutManager.primaryMonitor) { + Main.layoutManager.panelBox.set_position( + Main.layoutManager.primaryMonitor.x, + Main.layoutManager.primaryMonitor.y, + ) + Main.layoutManager.panelBox.set_size( + Main.layoutManager.primaryMonitor.width, + -1, + ) + } + + if (reset) return + + this._injectionManager.clear() + + this._setKeyBindings(false) + + this.notificationsMonitor.destroy() + + this._signalsHandler.destroy() + + Main.layoutManager._updateHotCorners = this._oldUpdateHotCorners + Main.layoutManager._updateHotCorners() + + delete Main.layoutManager.findIndexForActor + + if (this._enableHotCornersId) { + Main.layoutManager._interfaceSettings.disconnect(this._enableHotCornersId) + } + + Main.layoutManager._updatePanelBarrier = this._oldUpdatePanelBarrier + Main.layoutManager._updatePanelBarrier() + + Main.overview._overview._controls._workspacesDisplay._updateWorkspacesViews = + this._oldUpdateWorkspacesViews + Main.overview._overview._controls._workspacesDisplay.setPrimaryWorkspaceVisible = + this._oldSetPrimaryWorkspaceVisible + + LookingGlass.LookingGlass.prototype._resize = + LookingGlass.LookingGlass.prototype._oldResize + delete LookingGlass.LookingGlass.prototype._oldResize + + LookingGlass.LookingGlass.prototype.open = + LookingGlass.LookingGlass.prototype._oldOpen + delete LookingGlass.LookingGlass.prototype._oldOpen + + delete Main.messageTray._bannerBin.ease + + delete Main.panel.style + this._desktopIconsUsableArea.destroy() + this._desktopIconsUsableArea = null + } + + _setDesktopIconsMargins() { + this._desktopIconsUsableArea?.resetMargins() + this.allPanels.forEach((p) => { + switch (p.geom.position) { + case St.Side.TOP: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + p.geom.outerSize, + 0, + 0, + 0, + ) + break + case St.Side.BOTTOM: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + p.geom.outerSize, + 0, + 0, + ) + break + case St.Side.LEFT: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + 0, + p.geom.outerSize, + 0, + ) + break + case St.Side.RIGHT: + this._desktopIconsUsableArea?.setMargins( + p.monitor.index, + 0, + 0, + 0, + p.geom.outerSize, + ) + break + } + }) + } + + setFocusedMonitor(monitor) { + this.focusedMonitorPanel = this.allPanels.find((p) => p.monitor == monitor) + + if (!this.checkIfFocusedMonitor(monitor)) { + Main.overview._overview.clear_constraints() + Main.overview._overview.add_constraint( + new Layout.MonitorConstraint({ index: monitor.index }), + ) + + Main.overview._overview._controls._workspacesDisplay._primaryIndex = + monitor.index + + // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2395 + // The overview allocation used to calculate its workarea based on the monitor where the overview + // was displayed, but it got changed back to always use the primary monitor. So now, temporarily assign + // the primary monitor to dtp focused monitor while recalculating the overview workarea + Main.layoutManager.primaryMonitor = monitor + Main.overview._overview._controls.layout_manager._updateWorkAreaBox() + Main.layoutManager.primaryMonitor = + Main.layoutManager.monitors[Main.layoutManager.primaryIndex] + } + } + + showFocusedAppInOverview(app, keepOverviewOpen) { + if (app == this.focusedApp) { + if (!keepOverviewOpen && Main.overview._shown) Main.overview.hide() + + return + } + + let isolateWorkspaces = SETTINGS.get_boolean('isolate-workspaces') + let isolateMonitors = SETTINGS.get_boolean('isolate-monitors') + + this.focusedApp = app + + if (!this._signalsHandler.hasLabel('overview-spread')) { + let hasWorkspaces = Main.sessionMode.hasWorkspaces + let maybeDisableWorkspacesClick = () => { + if (!isolateWorkspaces) + Utils.getOverviewWorkspaces().forEach( + (w) => (w._container.get_actions()[0].enabled = false), + ) + } + let isIncludedWindow = (metaWindow) => + !this.focusedApp || + tracker.get_window_app(metaWindow) == this.focusedApp + + Main.sessionMode.hasWorkspaces = isolateWorkspaces + maybeDisableWorkspacesClick() + + Workspace.prototype._oldIsMyWindow = Workspace.prototype._isMyWindow + Workspace.prototype._isMyWindow = function (metaWindow) { + if (!metaWindow) return false + + return ( + isIncludedWindow(metaWindow) && + (this.metaWorkspace === null || + (!isolateWorkspaces && this.metaWorkspace.active) || + (isolateWorkspaces && + metaWindow.located_on_workspace(this.metaWorkspace))) && + (!isolateMonitors || metaWindow.get_monitor() === this.monitorIndex) + ) + } + + this.focusedWorkspace = !isolateWorkspaces + ? Utils.getCurrentWorkspace() + : null + + this._signalsHandler.addWithLabel( + 'overview-spread', + [Main.overview, 'showing', maybeDisableWorkspacesClick], + [ + Main.overview, + 'hidden', + () => { + Utils.getCurrentWorkspace() + .list_windows() + .forEach((w) => { + if ( + !w.minimized && + !w.customJS_ding && + global.display.focus_window != w && + tracker.get_window_app(w) != this.focusedApp + ) { + let window = w.get_compositor_private() + + ;(window.get_first_child() || window).opacity = 0 + + Utils.animateWindowOpacity(window, { + opacity: 255, + time: 0.25, + transition: 'easeOutQuad', + }) + } + }) + + this.focusedApp = null + this.focusedWorkspace = null + this._signalsHandler.removeWithLabel('overview-spread') + + Main.sessionMode.hasWorkspaces = hasWorkspaces + + Workspace.prototype._isMyWindow = Workspace.prototype._oldIsMyWindow + delete Workspace.prototype._oldIsMyWindow + }, + ], + ) + } + + if (Main.overview._shown) { + Utils.getOverviewWorkspaces().forEach((w) => { + let metaWindows = [] + let metaWorkspace = + w.metaWorkspace || + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + + w._container.layout_manager._windows.forEach((info, preview) => + preview.destroy(), + ) + + if (this.focusedWorkspace && this.focusedWorkspace == metaWorkspace) + metaWindows = Utils.getAllMetaWindows() + else if (!this.focusedWorkspace) + metaWindows = metaWorkspace.list_windows() + + metaWindows.forEach((mw) => w._doAddWindow(mw)) + }) + } else Main.overview.show() + } + + _newSetPrimaryWorkspaceVisible(visible) { + if (this._primaryVisible === visible) return + + this._primaryVisible = visible + + const primaryIndex = + Main.overview._overview._controls._workspacesDisplay._primaryIndex + const primaryWorkspace = this._workspacesViews[primaryIndex] + if (primaryWorkspace) primaryWorkspace.visible = visible + } + + _newUpdateWorkspacesViews() { + for (let i = 0; i < this._workspacesViews.length; i++) + this._workspacesViews[i].destroy() + + this._workspacesViews = [] + let monitors = Main.layoutManager.monitors + for (let i = 0; i < monitors.length; i++) { + let view + if (i === this._primaryIndex) { + view = new WorkspacesView( + i, + this._controls, + this._scrollAdjustment, + this._fitModeAdjustment, + this._overviewAdjustment, + ) + + view.visible = this._primaryVisible + this.bind_property( + 'opacity', + view, + 'opacity', + GObject.BindingFlags.SYNC_CREATE, + ) + this.add_child(view) + } else { + // No idea why atm, but we need the import at the top of this file and to use the + // full imports ns here, otherwise SecondaryMonitorDisplay can't be used ¯\_(ツ)_/¯ + view = new SecondaryMonitorDisplay( + i, + this._controls, + this._scrollAdjustment, + this._fitModeAdjustment, + this._overviewAdjustment, + ) + Main.layoutManager.overviewGroup.add_child(view) + } + + this._workspacesViews.push(view) + } + } + + checkIfFocusedMonitor(monitor) { + return ( + Main.overview._overview._controls._workspacesDisplay._primaryIndex == + monitor.index + ) + } + + _createPanel(monitor, isStandalone) { + let panelBox + let panel + let clipContainer = new Clutter.Actor() + + if (isStandalone) { + panelBox = new Utils.createBoxLayout({ name: 'panelBox' }) + } else { + panelBox = Main.layoutManager.panelBox + Main.layoutManager._untrackActor(panelBox) + panelBox.remove_child(Main.panel) + Main.layoutManager.removeChrome(panelBox) + } + + Main.layoutManager.addChrome(clipContainer, { affectsInputRegion: false }) + clipContainer.add_child(panelBox) + Main.layoutManager.trackChrome(panelBox, { + trackFullscreen: true, + affectsStruts: true, + affectsInputRegion: true, + }) + + panel = new Panel.Panel(this, monitor, panelBox, isStandalone) + panelBox.add_child(panel) + panel.enable() + + panelBox._dtpIndex = monitor.index + panelBox.set_position(0, 0) + + if (panel.checkIfVertical) panelBox.set_width(-1) + + this._findPanelMenuButtons(panelBox).forEach((pmb) => + this._adjustPanelMenuButton(pmb, monitor, panel.getPosition()), + ) + + panel.taskbar.iconAnimator.start() + + return panel + } + + _reset() { + this.disable(true) + this.allPanels = [] + this.enable(true) + } + + _updatePanelElementPositions() { + this.allPanels.forEach((p) => p.updateElementPositions()) + } + + _adjustPanelMenuButton(button, monitor, arrowSide) { + if (button) { + button.menu._boxPointer._dtpSourceActor = + button.menu._boxPointer.sourceActor + button.menu._boxPointer.sourceActor = button + button.menu._boxPointer._userArrowSide = arrowSide + button.menu._boxPointer._dtpInPanel = 1 + + if (!button.menu._boxPointer.vfunc_get_preferred_height) { + button.menu._boxPointer._dtpGetPreferredHeightId = + button.menu._boxPointer._container.connect( + 'get-preferred-height', + (actor, forWidth, alloc) => { + this._getBoxPointerPreferredHeight( + button.menu._boxPointer, + alloc, + monitor, + ) + }, + ) + } + } + } + + _getBoxPointerPreferredHeight(boxPointer, alloc, monitor) { + if ( + boxPointer._dtpInPanel && + boxPointer.sourceActor && + SETTINGS.get_boolean('intellihide') + ) { + monitor = + monitor || + Main.layoutManager.findMonitorForActor(boxPointer.sourceActor) + let panel = Utils.find( + global.zorinTaskbar.panels, + (p) => p.monitor == monitor, + ) + let excess = alloc.natural_size + panel.outerSize + 10 - monitor.height // 10 is arbitrary + + if (excess > 0) { + alloc.natural_size -= excess + } + } + + return [alloc.min_size, alloc.natural_size] + } + + _findPanelMenuButtons(container) { + let panelMenuButtons = [] + let panelMenuButton + + let find = (parent) => + parent.get_children().forEach((c) => { + if ((panelMenuButton = this._getPanelMenuButton(c))) { + panelMenuButtons.push(panelMenuButton) + } + + find(c) + }) + + find(container) + + return panelMenuButtons + } + + _removePanelBarriers(panel) { + if (panel.isStandalone && panel._rightPanelBarrier) { + panel._rightPanelBarrier.destroy() + } + + if (panel._leftPanelBarrier) { + panel._leftPanelBarrier.destroy() + delete panel._leftPanelBarrier + } + } + + _getPanelMenuButton(obj) { + return obj instanceof PanelMenu.Button && obj.menu?._boxPointer ? obj : 0 + } + + _setKeyBindings(enable) { + let keys = { + 'intellihide-key-toggle': () => + this.allPanels.forEach((p) => p.intellihide.toggle()), + } + + Object.keys(keys).forEach((k) => { + Utils.removeKeybinding(k) + + if (enable) { + Utils.addKeybinding(k, SETTINGS, keys[k], Shell.ActionMode.NORMAL) + } + }) + } +} + +// This class drives long-running icon animations, to keep them running in sync +// with each other. +export const IconAnimator = class { + constructor(actor) { + this._count = 0 + this._started = false + this._animations = { + dance: [], + } + this._timeline = new Clutter.Timeline({ + duration: 3000, + repeat_count: -1, + }) + + /* Just use the construction property when no need to support 3.36 */ + if (this._timeline.set_actor) this._timeline.set_actor(actor) + + this._timeline.connect('new-frame', () => { + const progress = this._timeline.get_progress() + const danceRotation = + progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0 + const dancers = this._animations.dance + for (let i = 0, iMax = dancers.length; i < iMax; i++) { + dancers[i].target.rotation_angle_z = danceRotation + } + }) + } + + destroy() { + this._timeline.stop() + this._timeline = null + for (let name in this._animations) { + const pairs = this._animations[name] + for (let i = 0, iMax = pairs.length; i < iMax; i++) { + const pair = pairs[i] + pair.target.disconnect(pair.targetDestroyId) + } + } + this._animations = null + } + + pause() { + if (this._started && this._count > 0) { + this._timeline.stop() + } + this._started = false + } + + start() { + if (!this._started && this._count > 0) { + this._timeline.start() + } + this._started = true + } + + addAnimation(target, name) { + // before adding a new animation, remove previous one to only have one running + let animationNames = Object.keys(this._animations) + + for (let i = 0; i < animationNames.length; ++i) { + let n = animationNames[i] + let currentAnimationPair = this._animations[n].find( + (p) => p.target == target, + ) + + if (currentAnimationPair) { + if (n == name) return // already have this animation running, nothing else to do + + this.removeAnimation(currentAnimationPair.target, n) + } + } + + const targetDestroyId = target.connect('destroy', () => + this.removeAnimation(target, name), + ) + this._animations[name].push({ + target: target, + targetDestroyId: targetDestroyId, + }) + if (this._started && this._count === 0) { + this._timeline.start() + } + this._count++ + } + + removeAnimation(target, name) { + const pairs = this._animations[name] + for (let i = 0, iMax = pairs.length; i < iMax; i++) { + const pair = pairs[i] + if (pair.target === target) { + target.disconnect(pair.targetDestroyId) + pairs.splice(i, 1) + this._count-- + if (this._started && this._count === 0) { + this._timeline.stop() + } + + if (name == 'dance') target.rotation_angle_z = 0 + + return + } + } + } +} + +function newUpdateHotCorners() { + // destroy old hot corners + this.hotCorners.forEach(function (corner) { + if (corner) corner.destroy() + }) + this.hotCorners = [] + + //global.settings is ubuntu specific setting to disable the hot corner (Tweak tool > Top Bar > Activities Overview Hot Corner) + //this._interfaceSettings is for the setting to disable the hot corner introduced in gnome-shell 3.34 + if ( + (global.settings.list_keys().indexOf('enable-hot-corners') >= 0 && + !global.settings.get_boolean('enable-hot-corners')) || + (this._interfaceSettings && + !this._interfaceSettings.get_boolean('enable-hot-corners')) + ) { + this.emit('hot-corners-changed') + return + } + + // build new hot corners + for (let i = 0; i < this.monitors.length; i++) { + let panel = Utils.find( + global.zorinTaskbar.panels, + (p) => p.monitor.index == i, + ) + let panelPosition = panel ? panel.getPosition() : St.Side.BOTTOM + let panelTopLeft = + panelPosition == St.Side.TOP || panelPosition == St.Side.LEFT + let monitor = this.monitors[i] + let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x + let cornerY = monitor.y + + let haveTopLeftCorner = true + + // If the panel is on the bottom, unless this is explicitly forced, don't add a topleft + // hot corner unless it is actually a top left panel. Otherwise, it stops the mouse + // as you are dragging across. In the future, maybe we will automatically move the + // hotcorner to the bottom when the panel is positioned at the bottom + if ( + i != this.primaryIndex || + !panelTopLeft + ) { + // Check if we have a top left (right for RTL) corner. + // I.e. if there is no monitor directly above or to the left(right) + let besideX = this._rtl ? monitor.x + 1 : cornerX - 1 + let besideY = cornerY + let aboveX = cornerX + let aboveY = cornerY - 1 + + for (let j = 0; j < this.monitors.length; j++) { + if (i == j) continue + let otherMonitor = this.monitors[j] + if ( + besideX >= otherMonitor.x && + besideX < otherMonitor.x + otherMonitor.width && + besideY >= otherMonitor.y && + besideY < otherMonitor.y + otherMonitor.height + ) { + haveTopLeftCorner = false + break + } + if ( + aboveX >= otherMonitor.x && + aboveX < otherMonitor.x + otherMonitor.width && + aboveY >= otherMonitor.y && + aboveY < otherMonitor.y + otherMonitor.height + ) { + haveTopLeftCorner = false + break + } + } + } + + if (haveTopLeftCorner) { + let corner = new Layout.HotCorner(this, monitor, cornerX, cornerY) + + corner.setBarrierSize = (size) => + Object.getPrototypeOf(corner).setBarrierSize.call( + corner, + Math.min(size, 32), + ) + corner.setBarrierSize(panel ? panel.geom.innerSize : 32) + this.hotCorners.push(corner) + } else { + this.hotCorners.push(null) + } + } + + this.emit('hot-corners-changed') +} + +function newUpdatePanelBarrier(panel) { + let barriers = { + _rightPanelBarrier: [panel.isStandalone ? panel : this], + _leftPanelBarrier: [panel], + } + + Object.keys(barriers).forEach((k) => { + let obj = barriers[k][0] + + if (obj[k]) { + obj[k].destroy() + obj[k] = null + } + }) + + if (!this.primaryMonitor || !panel.panelBox.height) { + return + } + + let barrierSize = Math.min(10, panel.panelBox.height) + let fixed1 = panel.monitor.y + let fixed2 = panel.monitor.y + barrierSize + + if (panel.checkIfVertical()) { + barriers._rightPanelBarrier.push( + panel.monitor.y + panel.monitor.height, + Meta.BarrierDirection.NEGATIVE_Y, + ) + barriers._leftPanelBarrier.push( + panel.monitor.y, + Meta.BarrierDirection.POSITIVE_Y, + ) + } else { + barriers._rightPanelBarrier.push( + panel.monitor.x + panel.monitor.width, + Meta.BarrierDirection.NEGATIVE_X, + ) + barriers._leftPanelBarrier.push( + panel.monitor.x, + Meta.BarrierDirection.POSITIVE_X, + ) + } + + switch (panel.getPosition()) { + //values are initialized as St.Side.TOP + case St.Side.BOTTOM: + fixed1 = panel.monitor.y + panel.monitor.height - barrierSize + fixed2 = panel.monitor.y + panel.monitor.height + break + case St.Side.LEFT: + fixed1 = panel.monitor.x + barrierSize + fixed2 = panel.monitor.x + break + case St.Side.RIGHT: + fixed1 = panel.monitor.x + panel.monitor.width - barrierSize + fixed2 = panel.monitor.x + panel.monitor.width + break + } + + //remove left barrier if it overlaps one of the hotcorners + for (let k in this.hotCorners) { + let hc = this.hotCorners[k] + + if ( + hc && + hc._monitor == panel.monitor && + (fixed1 == hc._x || fixed2 == hc._x || fixed1 == hc._y || fixed2 == hc._y) + ) { + delete barriers._leftPanelBarrier + break + } + } + + Object.keys(barriers).forEach((k) => { + let barrierOptions = { + backend: global.backend, + directions: barriers[k][2], + } + + barrierOptions[panel.varCoord.c1] = barrierOptions[panel.varCoord.c2] = + barriers[k][1] + barrierOptions[panel.fixedCoord.c1] = fixed1 + barrierOptions[panel.fixedCoord.c2] = fixed2 + + barriers[k][0][k] = new Meta.Barrier(barrierOptions) + }) +} + +function _newLookingGlassResize() { + let primaryMonitorPanel = Utils.find( + global.zorinTaskbar.panels, + (p) => p.monitor == Main.layoutManager.primaryMonitor, + ) + let topOffset = + primaryMonitorPanel.getPosition() == St.Side.TOP + ? primaryMonitorPanel.geom.outerSize + + (SETTINGS.get_boolean('stockgs-keep-top-panel') + ? Main.layoutManager.panelBox.height + : 0) + + 8 + : 32 + + this._oldResize() + + this._hiddenY = Main.layoutManager.primaryMonitor.y + topOffset - this.height + this._targetY = this._hiddenY + this.height + this.y = this._hiddenY + + this._objInspector.set_position( + this.x + Math.floor(this.width * 0.1), + this._targetY + Math.floor(this.height * 0.1), + ) +} + +function _newLookingGlassOpen() { + if (this._open) return + + this._resize() + this._oldOpen() +} diff --git a/src/panelPositions.js b/src/panelPositions.js new file mode 100644 index 0000000..d896acf --- /dev/null +++ b/src/panelPositions.js @@ -0,0 +1,70 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + */ + +export const SHOW_APPS_BTN = 'showAppsButton' +export const ACTIVITIES_BTN = 'activitiesButton' +export const TASKBAR = 'taskbar' +export const DATE_MENU = 'dateMenu' +export const SYSTEM_MENU = 'systemMenu' +export const LEFT_BOX = 'leftBox' +export const CENTER_BOX = 'centerBox' +export const RIGHT_BOX = 'rightBox' +export const DESKTOP_BTN = 'desktopButton' + +export const STACKED_TL = 'stackedTL' +export const STACKED_BR = 'stackedBR' +export const CENTERED = 'centered' +export const CENTERED_MONITOR = 'centerMonitor' + +export const TOP = 'TOP' +export const BOTTOM = 'BOTTOM' +export const LEFT = 'LEFT' +export const RIGHT = 'RIGHT' + +export const START = 'START' +export const MIDDLE = 'MIDDLE' +export const END = 'END' + +export const defaults = [ + { element: LEFT_BOX, visible: true, position: STACKED_TL }, + { element: SHOW_APPS_BTN, visible: false, position: STACKED_TL }, + { element: TASKBAR, visible: true, position: STACKED_TL }, + { element: CENTER_BOX, visible: true, position: STACKED_BR }, + { element: ACTIVITIES_BTN, visible: true, position: STACKED_BR }, + { element: RIGHT_BOX, visible: true, position: STACKED_BR }, + { element: SYSTEM_MENU, visible: true, position: STACKED_BR }, + { element: DATE_MENU, visible: true, position: STACKED_BR }, + { element: DESKTOP_BTN, visible: false, position: STACKED_BR }, +] + +export const anchorToPosition = { + [START]: STACKED_TL, + [MIDDLE]: CENTERED_MONITOR, + [END]: STACKED_BR, +} + +export const optionDialogFunctions = {} + +optionDialogFunctions[DATE_MENU] = '_showDateMenuOptions' +optionDialogFunctions[DESKTOP_BTN] = '_showDesktopButtonOptions' + +export function checkIfCentered(position) { + return position == CENTERED || position == CENTERED_MONITOR +} diff --git a/src/panelSettings.js b/src/panelSettings.js new file mode 100644 index 0000000..04fd5ca --- /dev/null +++ b/src/panelSettings.js @@ -0,0 +1,308 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import Gio from 'gi://Gio' + +import * as Pos from './panelPositions.js' + +const displayConfigWrapper = Gio.DBusProxy.makeProxyWrapper( + ` + + + + + + + + + + `, +) + +// the module variables here are different in the settings dialog (gjs process) +// and in gnome-shell (gnome-shell process) +let useCache = false +let cache = {} +let monitorIdToIndex = {} +let monitorIndexToId = {} + +export var displayConfigProxy = null +export var availableMonitors = [] + +export async function init(settings) { + useCache = true + + await setMonitorsInfo(settings) +} + +export function clearCache(setting) { + if (setting) { + cache[setting] = null + return + } + + cache = {} +} + +/** Return object representing a settings value that is stored as JSON. */ +export function getSettingsJson(settings, setting) { + try { + if (useCache && cache[setting]) return cache[setting] + + let res = JSON.parse(settings.get_string(setting)) + + cache[setting] = res + + return res + } catch (e) { + console.log('Error parsing positions: ' + e.message) + } +} +/** Write value object as JSON to setting in settings. */ +export function setSettingsJson(settings, setting, value) { + try { + const json = JSON.stringify(value) + settings.set_string(setting, json) + cache[setting] = value + } catch (e) { + console.log('Error serializing setting: ' + e.message) + } +} + +// Previously, the monitor index was used as an id to persist per monitor +// settings. Since these indexes are unreliable AF, switch to use the monitor +// serial as its id while keeping it backward compatible. +function getMonitorSetting(settings, settingName, monitorIndex, fallback) { + let monitorId = monitorIndexToId[monitorIndex] + + settings = getSettingsJson(settings, settingName) + + return ( + settings[monitorId] || + settings[monitorIndex] || + settings[availableMonitors[monitorIndex]?.id] || + fallback + ) +} + +function setMonitorSetting(settings, settingName, monitorIndex, value) { + let monitorId = monitorIndexToId[monitorIndex] + let usedId = monitorId || monitorIndex + + let currentSettings = getSettingsJson(settings, settingName) + + if (monitorId) delete currentSettings[monitorIndex] + + currentSettings[usedId] = value + setSettingsJson(settings, settingName, currentSettings) +} + +/** Returns size of panel on a specific monitor, in pixels. */ +export function getPanelSize(settings, monitorIndex) { + // Pull in deprecated setting if panel-sizes does not have setting for monitor. + return getMonitorSetting( + settings, + 'panel-sizes', + monitorIndex, + settings.get_int('panel-size') || 48, + ) +} + +export function setPanelSize(settings, monitorIndex, value) { + if (!(Number.isInteger(value) && value <= 128 && value >= 16)) { + console.log('Not setting invalid panel size: ' + value) + return + } + + setMonitorSetting(settings, 'panel-sizes', monitorIndex, value) +} + +/** + * Returns length of panel on a specific monitor, as a whole number percent, + * from settings. e.g. 100, or -1 for a dynamic panel length + */ +export function getPanelLength(settings, monitorIndex) { + return getMonitorSetting(settings, 'panel-lengths', monitorIndex, 100) +} + +export function setPanelLength(settings, monitorIndex, value) { + if ( + !(Number.isInteger(value) && ((value <= 100 && value >= 20) || value == -1)) + ) { + console.log('Not setting invalid panel length: ' + value, new Error().stack) + return + } + + setMonitorSetting(settings, 'panel-lengths', monitorIndex, value) +} + +/** Returns position of panel on a specific monitor. */ +export function getPanelPosition(settings, monitorIndex) { + return getMonitorSetting( + settings, + 'panel-positions', + monitorIndex, + settings.get_string('panel-position') || Pos.BOTTOM, + ) +} + +export function setPanelPosition(settings, monitorIndex, value) { + if ( + !( + value === Pos.TOP || + value === Pos.BOTTOM || + value === Pos.LEFT || + value === Pos.RIGHT + ) + ) { + console.log('Not setting invalid panel position: ' + value) + return + } + + setMonitorSetting(settings, 'panel-positions', monitorIndex, value) +} + +/** Returns anchor location of panel on a specific monitor. */ +export function getPanelAnchor(settings, monitorIndex) { + return getMonitorSetting(settings, 'panel-anchors', monitorIndex, Pos.MIDDLE) +} + +export function setPanelAnchor(settings, monitorIndex, value) { + if (!(value === Pos.START || value === Pos.MIDDLE || value === Pos.END)) { + console.log('Not setting invalid panel anchor: ' + value) + return + } + + setMonitorSetting(settings, 'panel-anchors', monitorIndex, value) +} + +export function getPanelElementPositions(settings, monitorIndex) { + return getMonitorSetting( + settings, + 'panel-element-positions', + monitorIndex, + Pos.defaults, + ) +} + +export function setPanelElementPositions(settings, monitorIndex, value) { + setMonitorSetting(settings, 'panel-element-positions', monitorIndex, value) +} + +export async function setMonitorsInfo(settings) { + return new Promise((resolve, reject) => { + try { + let monitorInfos = [] + let saveMonitorState = (proxy) => { + proxy.GetCurrentStateRemote((displayInfo, e) => { + if (e) return reject(`Error getting display state: ${e}`) + + let gsPrimaryIndex = 0 + let ids = {} + + //https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.gnome.Mutter.DisplayConfig.xml#L347 + displayInfo[2].forEach((logicalMonitor, i) => { + let [connector, vendor, product, serial] = logicalMonitor[5][0] + let id = i + let primary = logicalMonitor[4] + + // if by any chance 2 monitors have the same id, use the connector string + // instead, which should be unique but varies between x11 and wayland :( + // worst case scenario, resort to using the dumbass index + if (vendor && serial) id = `${vendor}-${serial}` + + if (ids[id]) id = connector && !ids[connector] ? connector : i + + if (primary) gsPrimaryIndex = i + + monitorInfos.push({ + id, + product, + primary, + }) + + monitorIdToIndex[id] = i + monitorIndexToId[i] = id + ids[id] = 1 + }) + + _saveMonitors(settings, monitorInfos, gsPrimaryIndex) + + resolve() + }) + } + + if (!displayConfigProxy) + displayConfigProxy = new displayConfigWrapper( + Gio.DBus.session, + 'org.gnome.Mutter.DisplayConfig', + '/org/gnome/Mutter/DisplayConfig', + (proxy, e) => { + if (e) return reject(`Error creating display proxy: ${e}`) + + saveMonitorState(proxy) + }, + ) + else saveMonitorState(displayConfigProxy) + } catch (e) { + reject(e) + } + }) +} + +function _saveMonitors(settings, monitorInfos, gsPrimaryIndex) { + /* Commented out as Zorin Taskbar always uses gnome-shell primary monitor + let keyPrimary = 'primary-monitor' + let dtpPrimaryMonitor = settings.get_string(keyPrimary) + + // convert previously saved index to monitor id + if (dtpPrimaryMonitor.match(/^\d{1,2}$/) && monitorInfos[dtpPrimaryMonitor]) + dtpPrimaryMonitor = monitorInfos[dtpPrimaryMonitor].id + + // default to gnome-shell primary monitor + if (!dtpPrimaryMonitor) + dtpPrimaryMonitor = monitorInfos[gsPrimaryIndex]?.id || 0 + + settings.set_string(keyPrimary, dtpPrimaryMonitor) + */ + + availableMonitors = Object.freeze(monitorInfos) +} + +// this is for backward compatibility, to remove in a few versions +export function adjustMonitorSettings(settings) { + let updateSettings = (settingName) => { + let monitorSettings = getSettingsJson(settings, settingName) + let updatedSettings = {} + + Object.keys(monitorSettings).forEach((key) => { + let initialKey = key + + if (key.match(/^\d{1,2}$/)) key = monitorIndexToId[key] || key + + updatedSettings[key] = monitorSettings[initialKey] + }) + + setSettingsJson(settings, settingName, updatedSettings) + } + + updateSettings('panel-sizes') + updateSettings('panel-lengths') + updateSettings('panel-positions') + updateSettings('panel-anchors') + updateSettings('panel-element-positions') +} diff --git a/src/panelStyle.js b/src/panelStyle.js new file mode 100644 index 0000000..9c543af --- /dev/null +++ b/src/panelStyle.js @@ -0,0 +1,168 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + * Ideas for recursing child actors and assigning inline styles + * are based on code from the StatusAreaHorizontalSpacing extension + * https://bitbucket.org/mathematicalcoffee/status-area-horizontal-spacing-gnome-shell-extension + * mathematical.coffee@gmail.com + */ + +import * as Utils from './utils.js' + +export const PanelStyle = class { + enable(panel) { + this.panel = panel + + this._applyStyles() + } + + disable() { + this._removeStyles() + } + + _applyStyles() { + this._rightBoxOperations = [] + + // center box has been moved next to the right box and will be treated the same + this._centerBoxOperations = this._rightBoxOperations + + this._leftBoxOperations = [] + + this._applyStylesRecursively() + + /* connect signal */ + this._rightBoxActorAddedID = this.panel._rightBox.connect( + 'child-added', + (container, actor) => { + if (this._rightBoxOperations.length && !this._ignoreAddedChild) + this._recursiveApply(actor, this._rightBoxOperations) + + this._ignoreAddedChild = 0 + }, + ) + this._centerBoxActorAddedID = this.panel._centerBox.connect( + 'child-added', + (container, actor) => { + if (this._centerBoxOperations.length && !this._ignoreAddedChild) + this._recursiveApply(actor, this._centerBoxOperations) + + this._ignoreAddedChild = 0 + }, + ) + this._leftBoxActorAddedID = this.panel._leftBox.connect( + 'child-added', + (container, actor) => { + if (this._leftBoxOperations.length) + this._recursiveApply(actor, this._leftBoxOperations) + }, + ) + } + + _removeStyles() { + /* disconnect signal */ + if (this._rightBoxActorAddedID) + this.panel._rightBox.disconnect(this._rightBoxActorAddedID) + if (this._centerBoxActorAddedID) + this.panel._centerBox.disconnect(this._centerBoxActorAddedID) + if (this._leftBoxActorAddedID) + this.panel._leftBox.disconnect(this._leftBoxActorAddedID) + + this._restoreOriginalStyle(this.panel._rightBox) + this._restoreOriginalStyle(this.panel._centerBox) + this._restoreOriginalStyle(this.panel._leftBox) + + this._applyStylesRecursively(true) + } + + _applyStylesRecursively(restore) { + /*recurse actors */ + if (this._rightBoxOperations.length) { + // add the system menu as we move it from the rightbox to the panel to position it independently + let children = this.panel._rightBox + .get_children() + .concat([ + this.panel.statusArea[Utils.getSystemMenuInfo().name].container, + ]) + for (let i in children) + this._recursiveApply(children[i], this._rightBoxOperations, restore) + } + + if (this._centerBoxOperations.length) { + // add the date menu as we move it from the centerbox to the panel to position it independently + let children = this.panel._centerBox + .get_children() + .concat([this.panel.statusArea.dateMenu.container]) + for (let i in children) + this._recursiveApply(children[i], this._centerBoxOperations, restore) + } + + if (this._leftBoxOperations.length) { + let children = this.panel._leftBox.get_children() + for (let i in children) + this._recursiveApply(children[i], this._leftBoxOperations, restore) + } + } + + _recursiveApply(actor, operations, restore) { + for (let i in operations) { + let o = operations[i] + if (o.compareFn(actor)) + if (restore) + o.restoreFn ? o.restoreFn(actor) : this._restoreOriginalStyle(actor) + else o.applyFn(actor, i) + } + + if (actor.get_children) { + let children = actor.get_children() + for (let i in children) { + this._recursiveApply(children[i], operations, restore) + } + } + } + + _restoreOriginalStyle(actor) { + if (actor._dtp_original_inline_style !== undefined) { + actor.set_style(actor._dtp_original_inline_style) + delete actor._dtp_original_inline_style + delete actor._dtp_style_overrides + } + + if (actor.has_style_class_name('panel-button')) { + this._refreshPanelButton(actor) + } + } + + _refreshPanelButton(actor) { + if (actor.visible) { + //force gnome 3.34+ to refresh (having problem with the -natural-hpadding) + let parent = actor.get_parent() + let children = parent.get_children() + let actorIndex = 0 + + if (children.length > 1) { + actorIndex = children.indexOf(actor) + } + + this._ignoreAddedChild = + [this.panel._centerBox, this.panel._rightBox].indexOf(parent) >= 0 + + parent.remove_child(actor) + parent.insert_child_at_index(actor, actorIndex) + } + } +} diff --git a/src/prefs.js b/src/prefs.js new file mode 100644 index 0000000..b260b98 --- /dev/null +++ b/src/prefs.js @@ -0,0 +1,1672 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import Adw from 'gi://Adw' +import GdkPixbuf from 'gi://GdkPixbuf' +import Gio from 'gi://Gio' +import GioUnix from 'gi://GioUnix' +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Gtk from 'gi://Gtk' +import Gdk from 'gi://Gdk' + +import * as PanelSettings from './panelSettings.js' +import * as Pos from './panelPositions.js' + +import { + ExtensionPreferences, + gettext as _, + ngettext, +} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js' + +const SCALE_UPDATE_TIMEOUT = 500 +const DEFAULT_PANEL_SIZES = [ 96, 64, 48, 32, 24 ] +const DEFAULT_MARGIN_SIZES = [32, 24, 16, 12, 8, 4, 0] +// Minimum length could be 0, but a higher value may help prevent confusion about where the panel went. +const LENGTH_MARKS = [100, 90, 80, 70, 60, 50, 40, 30, 20] + +const SCHEMA_PATH = '/org.gnome.shell.extensions.zorin-taskbar/' + +/** + * This function was copied from the activities-config extension + * https://github.com/nls1729/acme-code/tree/master/activities-config + * by Norman L. Smith. + */ +function cssHexString(css) { + let rrggbb = '#' + let start + for (let loop = 0; loop < 3; loop++) { + let end = 0 + let xx = '' + for (let loop = 0; loop < 2; loop++) { + while (true) { + let x = css.slice(end, end + 1) + if (x == '(' || x == ',' || x == ')') break + end++ + } + if (loop == 0) { + end++ + start = end + } + } + xx = parseInt(css.slice(start, end)).toString(16) + if (xx.length == 1) xx = '0' + xx + rrggbb += xx + css = css.slice(end) + } + return rrggbb +} + +function setShortcut(settings, shortcutName) { + let shortcut_text = settings.get_string(shortcutName + '-text') + let [success, key, mods] = Gtk.accelerator_parse(shortcut_text) + + if (success && Gtk.accelerator_valid(key, mods)) { + let shortcut = Gtk.accelerator_name(key, mods) + settings.set_strv(shortcutName, [shortcut]) + } else { + settings.set_strv(shortcutName, []) + } +} + +function checkHotkeyPrefix(settings) { + settings.delay() + + let hotkeyPrefix = settings.get_string('hotkey-prefix-text') + if (hotkeyPrefix == 'Super') hotkeyPrefix = '' + else if (hotkeyPrefix == 'SuperAlt') hotkeyPrefix = '' + let [, , mods] = Gtk.accelerator_parse(hotkeyPrefix) + let [, , shift_mods] = Gtk.accelerator_parse('' + hotkeyPrefix) + let [, , ctrl_mods] = Gtk.accelerator_parse('' + hotkeyPrefix) + + let numHotkeys = 10 + for (let i = 1; i <= numHotkeys; i++) { + let number = i + if (number == 10) number = 0 + let key = Gdk.keyval_from_name(number.toString()) + let key_kp = Gdk.keyval_from_name('KP_' + number.toString()) + if (Gtk.accelerator_valid(key, mods)) { + let shortcut = Gtk.accelerator_name(key, mods) + let shortcut_kp = Gtk.accelerator_name(key_kp, mods) + + // Setup shortcut strings + settings.set_strv('app-hotkey-' + i, [shortcut]) + settings.set_strv('app-hotkey-kp-' + i, [shortcut_kp]) + + // With + shortcut = Gtk.accelerator_name(key, shift_mods) + shortcut_kp = Gtk.accelerator_name(key_kp, shift_mods) + settings.set_strv('app-shift-hotkey-' + i, [shortcut]) + settings.set_strv('app-shift-hotkey-kp-' + i, [shortcut_kp]) + + // With + shortcut = Gtk.accelerator_name(key, ctrl_mods) + shortcut_kp = Gtk.accelerator_name(key_kp, ctrl_mods) + settings.set_strv('app-ctrl-hotkey-' + i, [shortcut]) + settings.set_strv('app-ctrl-hotkey-kp-' + i, [shortcut_kp]) + } else { + // Reset default settings for the relevant keys if the + // accelerators are invalid + let keys = [ + 'app-hotkey-' + i, + 'app-shift-hotkey-' + i, + 'app-ctrl-hotkey-' + i, // Regular numbers + 'app-hotkey-kp-' + i, + 'app-shift-hotkey-kp-' + i, + 'app-ctrl-hotkey-kp-' + i, + ] // Key-pad numbers + keys.forEach(function (val) { + settings.set_value(val, settings.get_default_value(val)) + }, this) + } + } + + settings.apply() +} + +function mergeObjects(main, bck) { + for (const prop in bck) { + if (!Object.hasOwn(main, prop) && Object.hasOwn(bck, prop)) { + main[prop] = bck[prop] + } + } + + return main +} + +const Preferences = class { + constructor(window, settings, gnomeInterfaceSettings, path) { + // this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.zorin-taskbar'); + this._rtl = Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL + this._builder = new Gtk.Builder() + this._builder.set_scope(new BuilderScope(this)) + this._settings = settings + this._gnomeInterfaceSettings = gnomeInterfaceSettings + this._path = path + + this._metadata = ExtensionPreferences.lookupByURL(import.meta.url).metadata + this._builder.set_translation_domain(this._metadata['gettext-domain']) + + window.set_search_enabled(true) + + // dialogs + this._builder.add_from_file(this._path + '/ui/BoxShowDesktopOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxDynamicOpacityOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxIntellihideOptions.ui') + this._builder.add_from_file( + this._path + '/ui/BoxShowDateMenuOptions.ui', + ) + this._builder.add_from_file(this._path + '/ui/BoxWindowPreviewOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxGroupAppsOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxMiddleClickOptions.ui') + this._builder.add_from_file(this._path + '/ui/BoxOverlayShortcut.ui') + + // pages + this._builder.add_from_file(this._path + '/ui/SettingsStyle.ui') + let pageStyle = this._builder.get_object('style') + window.add(pageStyle) + + this._builder.add_from_file(this._path + '/ui/SettingsPosition.ui') + let pagePosition = this._builder.get_object('position') + window.add(pagePosition) + + this._builder.add_from_file(this._path + '/ui/SettingsBehavior.ui') + let pageBehavior = this._builder.get_object('behavior') + window.add(pageBehavior) + + this._builder.add_from_file(this._path + '/ui/SettingsAction.ui') + let pageAction = this._builder.get_object('action') + window.add(pageAction) + + let listbox = this._builder.get_object('taskbar_display_listbox') + let provider = new Gtk.CssProvider() + provider.load_from_data('list { background-color: transparent; }', -1) + let context = listbox.get_style_context() + context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + // set the window as notebook, it is being used as parent for dialogs + this.notebook = window + + PanelSettings.setMonitorsInfo(settings).then(() => { + this._bindSettings() + + PanelSettings.displayConfigProxy.connectSignal( + 'MonitorsChanged', + async () => { + await PanelSettings.setMonitorsInfo(settings) + this._setMonitorsInfo() + }, + ) + + let maybeGoToPage = () => { + let targetPageName = settings.get_string('target-prefs-page') + + if (targetPageName) { + window.set_visible_page_name(targetPageName) + settings.set_string('target-prefs-page', '') + } + } + + settings.connect('changed::target-prefs-page', maybeGoToPage) + + maybeGoToPage() + }) + } + + /** + * Connect signals + */ + _connector(builder, object, signal, handler) { + object.connect(signal, this._SignalHandler[handler].bind(this)) + } + + _updateVerticalRelatedOptions() { + let position = this._getPanelPosition(this._currentMonitorIndex) + let isVertical = position == Pos.LEFT || position == Pos.RIGHT + let showDesktopWidthLabel = this._builder.get_object( + 'show_showdesktop_width_label', + ) + + showDesktopWidthLabel.set_title(_('Show Desktop button padding (px)')) + + this._displayPanelPositionsForMonitor(this._currentMonitorIndex) + this._setPanelLenghtWidgetSensitivity( + PanelSettings.getPanelLength(this._settings, this._currentMonitorIndex), + ) + } + + _getPanelPosition(monitorIndex) { + return PanelSettings.getPanelPosition(this._settings, monitorIndex) + } + + _setPanelPosition(position) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? Object.keys(this.monitors) + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelPosition(this._settings, monitorIndex, position) + }) + this._setAnchorLabels(this._currentMonitorIndex) + } + + _setPositionRadios(position) { + this._ignorePositionRadios = true + + switch (position) { + case Pos.BOTTOM: + this._builder.get_object('position_bottom_button').set_active(true) + break + case Pos.TOP: + this._builder.get_object('position_top_button').set_active(true) + break + case Pos.LEFT: + this._builder.get_object('position_left_button').set_active(true) + break + case Pos.RIGHT: + this._builder.get_object('position_right_button').set_active(true) + break + } + + this._ignorePositionRadios = false + } + + /** + * Set panel anchor combo labels according to whether the monitor's panel is vertical + * or horizontal, or if all monitors' panels are being configured and they are a mix + * of vertical and horizontal. + */ + _setAnchorLabels(currentMonitorIndex) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? Object.keys(this.monitors) + : [currentMonitorIndex] + const allVertical = monitorsToSetFor.every((i) => { + const position = PanelSettings.getPanelPosition(this._settings, i) + return position === Pos.LEFT || position === Pos.RIGHT + }) + const allHorizontal = monitorsToSetFor.every((i) => { + const position = PanelSettings.getPanelPosition(this._settings, i) + return position === Pos.TOP || position === Pos.BOTTOM + }) + + const anchor_combo = this._builder.get_object('panel_anchor_combo') + anchor_combo.remove_all() + + if (allHorizontal) { + anchor_combo.append(Pos.START, _('Left')) + anchor_combo.append(Pos.MIDDLE, _('Center')) + anchor_combo.append(Pos.END, _('Right')) + } else if (allVertical) { + anchor_combo.append(Pos.START, _('Top')) + anchor_combo.append(Pos.MIDDLE, _('Middle')) + anchor_combo.append(Pos.END, _('Bottom')) + } else { + // Setting for a mix of horizontal and vertical panels on different monitors. + anchor_combo.append(Pos.START, _('Start')) + anchor_combo.append(Pos.MIDDLE, _('Middle')) + anchor_combo.append(Pos.END, _('End')) + } + + // Set combo box after re-populating its options. But only if it's for a single-panel + // configuration, or a multi-panel configuration where they all have the same anchor + // setting. So don't set the combo box if there is a multi-panel configuration with + // different anchor settings. + const someAnchor = PanelSettings.getPanelAnchor( + this._settings, + currentMonitorIndex, + ) + if ( + monitorsToSetFor.every( + (i) => PanelSettings.getPanelAnchor(this._settings, i) === someAnchor, + ) + ) { + const panel_anchor = PanelSettings.getPanelAnchor( + this._settings, + currentMonitorIndex, + ) + this._builder.get_object('panel_anchor_combo').set_active_id(panel_anchor) + } + } + + /** + * When a monitor is selected, update the widgets for panel position, size, anchoring, + * and contents so they accurately show the settings for the panel on that monitor. + */ + _updateWidgetSettingsForMonitor(monitorIndex) { + // Update display of panel screen position setting + const panelPosition = this._getPanelPosition(monitorIndex) + this._setPositionRadios(panelPosition) + + // Update display of thickness, length, and anchor settings + const panel_size_scale = this._builder.get_object('panel_size_scale') + const size = PanelSettings.getPanelSize(this._settings, monitorIndex) + panel_size_scale.set_value(size) + + const panel_length_scale = this._builder.get_object('panel_length_scale') + const dynamicLengthButton = this._builder.get_object( + 'panel_length_dynamic_button', + ) + const length = PanelSettings.getPanelLength(this._settings, monitorIndex) + const isDynamicLength = length == -1 + + dynamicLengthButton.set_active(isDynamicLength) + panel_length_scale.set_value(isDynamicLength ? 100 : length) + + this._setAnchorLabels(monitorIndex) + + // Update display of panel content settings + this._displayPanelPositionsForMonitor(monitorIndex) + + this._setPanelLenghtWidgetSensitivity(length) + } + + /** + * Anchor is only relevant if panel length is less than 100%. Enable or disable + * anchor widget sensitivity accordingly. + */ + _setPanelLenghtWidgetSensitivity(panelLength) { + const taskbarListBox = this._builder.get_object('taskbar_display_listbox') + let i = 0 + let row + const isDynamicLength = panelLength == -1 + const isPartialLength = panelLength < 100 + + this._builder + .get_object('panel_length_scale') + .set_sensitive(!isDynamicLength) + this._builder + .get_object('panel_anchor_label') + .set_sensitive(isPartialLength) + this._builder + .get_object('panel_anchor_combo') + .set_sensitive(isPartialLength) + + while ((row = taskbarListBox.get_row_at_index(i++)) != null) { + let grid = row.get_child() + let positionCombo = grid.get_child_at(4, 0) + + positionCombo.set_sensitive(!isDynamicLength) + } + } + + _displayPanelPositionsForMonitor(monitorIndex) { + let taskbarListBox = this._builder.get_object('taskbar_display_listbox') + + while (taskbarListBox.get_first_child()) { + taskbarListBox.remove(taskbarListBox.get_first_child()) + } + + let labels = {} + let panelPosition = this._getPanelPosition(monitorIndex) + let isVertical = panelPosition == Pos.LEFT || panelPosition == Pos.RIGHT + let panelElementPositions = PanelSettings.getPanelElementPositions( + this._settings, + monitorIndex, + ) + let updateElementsSettings = () => { + let newPanelElementPositions = [] + let monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + let monitors = monitorSync ? Object.keys(this.monitors) : [monitorIndex] + + let child = taskbarListBox.get_first_child() + while (child != null) { + newPanelElementPositions.push({ + element: child.id, + visible: child.visibleToggleBtn.get_active(), + position: child.positionCombo.get_active_id(), + }) + child = child.get_next_sibling() + } + + monitors.forEach((m) => + PanelSettings.setPanelElementPositions( + this._settings, + m, + newPanelElementPositions, + ), + ) + } + + labels[Pos.SHOW_APPS_BTN] = _('Show Applications button') + labels[Pos.ACTIVITIES_BTN] = _('Activities button') + labels[Pos.TASKBAR] = _('Taskbar') + labels[Pos.DATE_MENU] = _('Date menu') + labels[Pos.SYSTEM_MENU] = _('System menu') + labels[Pos.LEFT_BOX] = _('Left box') + labels[Pos.CENTER_BOX] = _('Center box') + labels[Pos.RIGHT_BOX] = _('Right box') + labels[Pos.DESKTOP_BTN] = _('Desktop button') + + panelElementPositions.forEach((el) => { + let row = new Gtk.ListBoxRow() + let grid = new Gtk.Grid({ + margin_start: 12, + margin_end: 12, + column_spacing: 8, + }) + let upDownGrid = new Gtk.Grid({ column_spacing: 2 }) + let upBtn = new Gtk.Button({ tooltip_text: _('Move up') }) + let upImg = new Gtk.Image({ icon_name: 'go-up-symbolic', pixel_size: 12 }) + let downBtn = new Gtk.Button({ tooltip_text: _('Move down') }) + let downImg = new Gtk.Image({ + icon_name: 'go-down-symbolic', + pixel_size: 12, + }) + let visibleToggleBtn = new Gtk.ToggleButton({ + label: _('Visible'), + active: el.visible, + }) + let positionCombo = new Gtk.ComboBoxText({ + tooltip_text: _('Select element position'), + }) + let upDownClickHandler = (limit) => { + let index = row.get_index() + + if (index != limit) { + taskbarListBox.remove(row) + taskbarListBox.insert(row, index + (!limit ? -1 : 1)) + updateElementsSettings() + } + } + + positionCombo.append( + Pos.STACKED_TL, + isVertical ? _('Stacked to top') : _('Stacked to left'), + ) + positionCombo.append( + Pos.STACKED_BR, + isVertical ? _('Stacked to bottom') : _('Stacked to right'), + ) + positionCombo.append(Pos.CENTERED, _('Centered')) + positionCombo.append(Pos.CENTERED_MONITOR, _('Monitor Center')) + positionCombo.set_active_id(el.position) + + upBtn.connect('clicked', () => upDownClickHandler(0)) + downBtn.connect('clicked', () => + upDownClickHandler(panelElementPositions.length - 1), + ) + visibleToggleBtn.connect('toggled', () => updateElementsSettings()) + positionCombo.connect('changed', () => updateElementsSettings()) + + upBtn.set_child(upImg) + downBtn.set_child(downImg) + + upDownGrid.attach(upBtn, 0, 0, 1, 1) + upDownGrid.attach(downBtn, 1, 0, 1, 1) + + grid.attach(upDownGrid, 0, 0, 1, 1) + grid.attach( + new Gtk.Label({ label: labels[el.element], xalign: 0, hexpand: true }), + 1, + 0, + 1, + 1, + ) + + if (Pos.optionDialogFunctions[el.element]) { + let cogImg = new Gtk.Image({ icon_name: 'emblem-system-symbolic' }) + let optionsBtn = new Gtk.Button({ tooltip_text: _('More options') }) + + optionsBtn.get_style_context().add_class('circular') + optionsBtn.set_child(cogImg) + grid.attach(optionsBtn, 2, 0, 1, 1) + + optionsBtn.connect('clicked', () => + this[Pos.optionDialogFunctions[el.element]](), + ) + } + + grid.attach(visibleToggleBtn, 3, 0, 1, 1) + grid.attach(positionCombo, 4, 0, 1, 1) + + row.id = el.element + row.visibleToggleBtn = visibleToggleBtn + row.positionCombo = positionCombo + + row.set_child(grid) + taskbarListBox.insert(row, -1) + }) + } + + _createPreferencesDialog(title, content, reset_function = null) { + let dialog + + dialog = new Gtk.Dialog({ + title: title, + transient_for: this.notebook.get_root(), + use_header_bar: true, + modal: true, + }) + + // GTK+ leaves positive values for application-defined response ids. + // Use +1 for the reset action + if (reset_function != null) dialog.add_button(_('Reset to defaults'), 1) + + dialog.get_content_area().append(content) + + dialog.connect('response', (dialog, id) => { + if (id == 1) { + // restore default settings + if (reset_function) reset_function() + } else { + // remove the settings content so it doesn't get destroyed; + dialog.get_content_area().remove(content) + dialog.destroy() + } + return + }) + + return dialog + } + + _showDateMenuOptions() { + let box = this._builder.get_object('box_date_menu_options') + + let dialog = this._createPreferencesDialog( + _('Date Menu options'), + box, + () => { + // restore default settings + this._gnomeInterfaceSettings.set_value('clock-show-date', this._gnomeInterfaceSettings.get_default_value('clock-show-date')) + this._gnomeInterfaceSettings.set_value('clock-show-weekday', this._gnomeInterfaceSettings.get_default_value('clock-show-weekday')) + this._gnomeInterfaceSettings.set_value('clock-show-seconds', this._gnomeInterfaceSettings.get_default_value('clock-show-seconds')) + } + ) + + dialog.show() + dialog.set_default_size(1, 1) + } + + _showDesktopButtonOptions() { + let box = this._builder.get_object('box_show_showdesktop_options') + + let dialog = this._createPreferencesDialog( + _('Show Desktop options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'show-showdesktop-icon', + this._settings.get_default_value('show-showdesktop-icon'), + ) + this._settings.set_value( + 'showdesktop-button-width', + this._settings.get_default_value('showdesktop-button-width'), + ) + this._builder + .get_object('show_showdesktop_width_spinbutton') + .set_value(this._settings.get_int('showdesktop-button-width')) + + this._settings.set_value( + 'show-showdesktop-hover', + this._settings.get_default_value('show-showdesktop-hover'), + ) + }, + ) + + this._builder + .get_object('show_showdesktop_width_spinbutton') + .set_value(this._settings.get_int('showdesktop-button-width')) + this._builder + .get_object('show_showdesktop_width_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('showdesktop-button-width', widget.get_value()) + }) + + dialog.show() + dialog.set_default_size(1, 1) + } + + _setMonitorsInfo() { + this.monitors = PanelSettings.availableMonitors + + let panelOptionsMonitorCombo = this._builder.get_object( + 'taskbar_position_monitor_combo', + ) + let dtpPrimaryMonitorIndex = 0 + + this._currentMonitorIndex = dtpPrimaryMonitorIndex + + this._updateVerticalRelatedOptions() + + panelOptionsMonitorCombo.remove_all() + + for (let i = 0; i < this.monitors.length; ++i) { + let monitor = this.monitors[i] + let label = monitor.primary + ? _('Primary monitor') + : _('Monitor ') + (i + 1) + + label += monitor.product ? ` (${monitor.product})` : '' + + panelOptionsMonitorCombo.append_text(label) + } + + panelOptionsMonitorCombo.set_active(dtpPrimaryMonitorIndex) + } + + _bindSettings() { + //multi-monitor + this._setMonitorsInfo() + + this._settings.connect('changed::panel-positions', () => + this._updateVerticalRelatedOptions(), + ) + + this._settings.bind( + 'panel-element-positions-monitors-sync', + this._builder.get_object('taskbar_position_sync_button'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'panel-element-positions-monitors-sync', + this._builder.get_object('taskbar_position_monitor_combo'), + 'sensitive', + Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.connect( + 'changed::panel-element-positions-monitors-sync', + () => { + // The anchor combo box may has different labels for single- or all-monitor configuration. + this._setAnchorLabels(this._currentMonitorIndex) + }, + ) + + this._builder + .get_object('taskbar_position_monitor_combo') + .connect('changed', (widget) => { + this._currentMonitorIndex = widget.get_active() + this._updateWidgetSettingsForMonitor(this._currentMonitorIndex) + }) + + this._settings.bind( + 'multi-monitors', + this._builder.get_object('multimon_multi_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + if (this.monitors.length === 1) { + this._builder.get_object('multimon_multi_switch').set_sensitive(false) + } + + let panelLengthScale = { + objectName: 'panel_length_scale', + valueName: '', + range: LENGTH_MARKS, + unit: '%', + getValue: () => + PanelSettings.getPanelLength(this._settings, this._currentMonitorIndex), + setValue: (value) => setPanelLength(value), + manualConnect: true, + } + + let setPanelLength = (value) => { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? Object.keys(this.monitors) + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelLength(this._settings, monitorIndex, value) + }) + + maybeSetPanelLengthScaleValueChange(value) + this._setPanelLenghtWidgetSensitivity(value) + } + + let maybeSetPanelLengthScaleValueChange = (value) => { + const panel_length_scale = this._builder.get_object('panel_length_scale') + + if (panelLengthScale.valueChangedId) { + panel_length_scale.disconnect(panelLengthScale.valueChangedId) + panelLengthScale.valueChangedId = 0 + } + + if (value != -1) connectValueChanged(panel_length_scale, panelLengthScale) + else panel_length_scale.set_value(100) + } + + const dynamicLengthButton = this._builder.get_object( + 'panel_length_dynamic_button', + ) + dynamicLengthButton.connect('notify::active', () => { + setPanelLength(dynamicLengthButton.get_active() ? -1 : 100) + }) + + this._builder + .get_object('panel_anchor_combo') + .connect('changed', (widget) => { + const value = widget.get_active_id() + // Value can be null while anchor labels are being swapped out + if (value !== null) { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? Object.keys(this.monitors) + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelAnchor(this._settings, monitorIndex, value) + }) + } + }) + + this._updateWidgetSettingsForMonitor(this._currentMonitorIndex) + + //dynamic opacity + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_override_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_box'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-custom-opacity', + this._builder.get_object('trans_opacity_box2'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('trans_opacity_override_switch') + .connect('notify::active', (widget) => { + if (!widget.get_active()) + this._builder.get_object('trans_dyn_switch').set_active(false) + }) + + this._builder + .get_object('trans_opacity_spinbutton') + .set_value(this._settings.get_double('trans-panel-opacity') * 100) + this._builder + .get_object('trans_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-panel-opacity', + widget.get_value() * 0.01, + ) + }) + + this._settings.bind( + 'trans-use-dynamic-opacity', + this._builder.get_object('trans_dyn_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-use-dynamic-opacity', + this._builder.get_object('trans_dyn_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'trans-dynamic-behavior', + this._builder.get_object('trans_options_window_type_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .set_value(this._settings.get_double('trans-dynamic-anim-target') * 100) + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_double( + 'trans-dynamic-anim-target', + widget.get_value() * 0.01, + ) + }) + + this._builder + .get_object('trans_dyn_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_dynamic_opacity_options') + + let dialog = this._createPreferencesDialog( + _('Dynamic opacity options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'trans-dynamic-behavior', + this._settings.get_default_value('trans-dynamic-behavior'), + ) + + this._settings.set_value( + 'trans-dynamic-anim-target', + this._settings.get_default_value('trans-dynamic-anim-target'), + ) + this._builder + .get_object('trans_options_min_opacity_spinbutton') + .set_value( + this._settings.get_double('trans-dynamic-anim-target') * 100, + ) + }, + ) + + dialog.show() + dialog.set_default_size(1, 1) + }) + + this._settings.bind( + 'intellihide', + this._builder.get_object('intellihide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide', + this._builder.get_object('intellihide_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-hide-from-windows', + this._builder.get_object('intellihide_window_hide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-hide-from-windows', + this._builder.get_object('intellihide_behaviour_options'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-behaviour', + this._builder.get_object('intellihide_behaviour_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-use-pressure', + this._builder.get_object('intellihide_use_pressure_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-show-in-fullscreen', + this._builder.get_object('intellihide_show_in_fullscreen_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'intellihide-only-secondary', + this._builder.get_object('intellihide_only_secondary_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'multi-monitors', + this._builder.get_object('grid_intellihide_only_secondary'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('multimon_multi_switch') + .connect('notify::active', (widget) => { + if (!widget.get_active()) + this._builder + .get_object('intellihide_only_secondary_switch') + .set_active(false) + }) + + this._settings.bind( + 'intellihide-key-toggle-text', + this._builder.get_object('intellihide_toggle_entry'), + 'text', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.connect('changed::intellihide-key-toggle-text', () => + setShortcut(this._settings, 'intellihide-key-toggle'), + ) + + let intellihidePersistStateSwitch = this._builder.get_object( + 'intellihide_persist_state_switch', + ) + + intellihidePersistStateSwitch.set_active( + this._settings.get_int('intellihide-persisted-state') > -1, + ) + + intellihidePersistStateSwitch.connect('notify::active', (widget) => { + this._settings.set_int( + 'intellihide-persisted-state', + widget.get_active() ? 0 : -1, + ) + }) + + this._settings.bind( + 'intellihide-show-on-notification', + this._builder.get_object('intellihide_show_on_notification_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('intellihide_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_intellihide_options') + + let dialog = this._createPreferencesDialog( + _('Intellihide options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'intellihide-hide-from-windows', + this._settings.get_default_value('intellihide-hide-from-windows'), + ) + this._settings.set_value( + 'intellihide-behaviour', + this._settings.get_default_value('intellihide-behaviour'), + ) + this._settings.set_value( + 'intellihide-use-pressure', + this._settings.get_default_value('intellihide-use-pressure'), + ) + this._settings.set_value( + 'intellihide-show-in-fullscreen', + this._settings.get_default_value( + 'intellihide-show-in-fullscreen', + ), + ) + this._settings.set_value( + 'intellihide-show-on-notification', + this._settings.get_default_value( + 'intellihide-show-on-notification', + ), + ) + this._settings.set_value( + 'intellihide-persisted-state', + this._settings.get_default_value('intellihide-persisted-state'), + ) + this._settings.set_value( + 'intellihide-only-secondary', + this._settings.get_default_value('intellihide-only-secondary'), + ) + + this._settings.set_value( + 'intellihide-key-toggle-text', + this._settings.get_default_value('intellihide-key-toggle-text'), + ) + + intellihidePersistStateSwitch.set_active(false) + }, + ) + + dialog.show() + dialog.set_default_size(1, 1) + }) + + // Behavior panel + + this._gnomeInterfaceSettings.bind( + 'clock-show-date', + this._builder.get_object('date_menu_date_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._gnomeInterfaceSettings.bind( + 'clock-show-weekday', + this._builder.get_object('date_menu_weekday_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._gnomeInterfaceSettings.bind( + 'clock-show-seconds', + this._builder.get_object('date_menu_seconds_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-showdesktop-icon', + this._builder.get_object('show_showdesktop_icon_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-showdesktop-hover', + this._builder.get_object('show_showdesktop_hide_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-window-previews', + this._builder.get_object('show_window_previews_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-window-previews', + this._builder.get_object('show_window_previews_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-tooltip', + this._builder.get_object('show_tooltip_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites', + this._builder.get_object('show_favorite_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites-all-monitors', + this._builder.get_object('multimon_multi_show_favorites_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-favorites', + this._builder.get_object('multimon_multi_show_favorites_switch'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'show-running-apps', + this._builder.get_object('show_runnning_apps_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('show_window_previews_button') + .connect('clicked', () => { + let scrolledWindow = this._builder.get_object( + 'box_window_preview_options', + ) + + let dialog = this._createPreferencesDialog( + _('Window preview options'), + scrolledWindow, + () => { + // restore default settings + this._settings.set_value( + 'peek-mode', + this._settings.get_default_value('peek-mode'), + ) + + this._settings.set_value( + 'window-preview-size', + this._settings.get_default_value('window-preview-size'), + ) + this._builder + .get_object('preview_size_spinbutton') + .set_value(this._settings.get_int('window-preview-size')) + + this._settings.set_value( + 'preview-middle-click-close', + this._settings.get_default_value('preview-middle-click-close'), + ) + }, + ) + + this._settings.bind( + 'preview-middle-click-close', + this._builder.get_object('preview_middle_click_close_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'peek-mode', + this._builder.get_object('peek_mode_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('preview_size_spinbutton') + .set_value(this._settings.get_int('window-preview-size')) + this._builder + .get_object('preview_size_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int('window-preview-size', widget.get_value()) + }) + + dialog.show() + }) + + this._settings.bind( + 'group-apps', + this._builder.get_object('group_apps_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.bind( + 'group-apps', + this._builder.get_object('show_group_apps_options_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT | Gio.SettingsBindFlags.INVERT_BOOLEAN, + ) + + this._settings.bind( + 'progress-show-count', + this._builder.get_object('show_notification_badge_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'group-apps-use-fixed-width', + this._builder.get_object('group_apps_use_fixed_width_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'group-apps-use-launchers', + this._builder.get_object('group_apps_use_launchers_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('display_multitasking_settings') + .connect('activated', () => { + try { + const output = GLib.spawn_command_line_async('gnome-control-center multitasking') + } catch (e) { + logError(e) + } + } + ) + + this._builder + .get_object('show_group_apps_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_group_apps_options') + + let dialog = this._createPreferencesDialog( + _('Ungrouped application options'), + box, + () => { + // restore default settings + this._settings.set_value( + 'group-apps-label-max-width', + this._settings.get_default_value('group-apps-label-max-width'), + ) + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .set_value(this._settings.get_int('group-apps-label-max-width')) + + this._settings.set_value( + 'group-apps-use-fixed-width', + this._settings.get_default_value('group-apps-use-fixed-width'), + ) + this._settings.set_value( + 'group-apps-use-launchers', + this._settings.get_default_value('group-apps-use-launchers'), + ) + }, + ) + + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .set_value(this._settings.get_int('group-apps-label-max-width')) + this._builder + .get_object('group_apps_label_max_width_spinbutton') + .connect('value-changed', (widget) => { + this._settings.set_int( + 'group-apps-label-max-width', + widget.get_value(), + ) + }) + + dialog.show() + dialog.set_default_size(600, 1) + }) + + this._builder + .get_object('click_action_combo') + .set_active_id(this._settings.get_string('click-action')) + this._builder + .get_object('click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('click-action', widget.get_active_id()) + }) + + this._builder + .get_object('shift_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('shift-click-action', widget.get_active_id()) + }) + + this._builder + .get_object('middle_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string('middle-click-action', widget.get_active_id()) + }) + this._builder + .get_object('shift_middle_click_action_combo') + .connect('changed', (widget) => { + this._settings.set_string( + 'shift-middle-click-action', + widget.get_active_id(), + ) + }) + + // Create dialog for middle-click options + this._builder + .get_object('middle_click_options_button') + .connect('clicked', () => { + let box = this._builder.get_object('box_middle_click_options') + + let dialog = this._createPreferencesDialog( + _('Customize middle-click behavior'), + box, + () => { + // restore default settings for the relevant keys + let keys = [ + 'shift-click-action', + 'middle-click-action', + 'shift-middle-click-action', + ] + keys.forEach(function (val) { + this._settings.set_value( + val, + this._settings.get_default_value(val), + ) + }, this) + this._builder + .get_object('shift_click_action_combo') + .set_active_id(this._settings.get_string('shift-click-action')) + this._builder + .get_object('middle_click_action_combo') + .set_active_id(this._settings.get_string('middle-click-action')) + this._builder + .get_object('shift_middle_click_action_combo') + .set_active_id( + this._settings.get_string('shift-middle-click-action'), + ) + }, + ) + + this._builder + .get_object('shift_click_action_combo') + .set_active_id(this._settings.get_string('shift-click-action')) + + this._builder + .get_object('middle_click_action_combo') + .set_active_id(this._settings.get_string('middle-click-action')) + + this._builder + .get_object('shift_middle_click_action_combo') + .set_active_id(this._settings.get_string('shift-middle-click-action')) + + this._settings.bind( + 'shift-click-action', + this._builder.get_object('shift_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'middle-click-action', + this._builder.get_object('middle_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'shift-middle-click-action', + this._builder.get_object('shift_middle_click_action_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + dialog.show() + dialog.set_default_size(700, 1) + }) + + this._builder + .get_object('scroll_icon_combo') + .set_active_id(this._settings.get_string('scroll-icon-action')) + this._builder + .get_object('scroll_icon_combo') + .connect('changed', (widget) => { + this._settings.set_string('scroll-icon-action', widget.get_active_id()) + }) + + this._settings.bind( + 'hot-keys', + this._builder.get_object('hot_keys_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.bind( + 'hot-keys', + this._builder.get_object('overlay_button'), + 'sensitive', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder.get_object('overlay_combo').connect('changed', (widget) => { + this._settings.set_string('hotkeys-overlay-combo', widget.get_active_id()) + }) + + this._settings.bind( + 'shortcut-previews', + this._builder.get_object('shortcut_preview_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('shortcut_num_keys_combo') + .set_active_id(this._settings.get_string('shortcut-num-keys')) + this._builder + .get_object('shortcut_num_keys_combo') + .connect('changed', (widget) => { + this._settings.set_string('shortcut-num-keys', widget.get_active_id()) + }) + + this._settings.connect('changed::hotkey-prefix-text', () => { + checkHotkeyPrefix(this._settings) + }) + + this._builder + .get_object('hotkey_prefix_combo') + .set_active_id(this._settings.get_string('hotkey-prefix-text')) + + this._settings.bind( + 'hotkey-prefix-text', + this._builder.get_object('hotkey_prefix_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._builder + .get_object('overlay_combo') + .set_active_id(this._settings.get_string('hotkeys-overlay-combo')) + + this._settings.bind( + 'hotkeys-overlay-combo', + this._builder.get_object('overlay_combo'), + 'active-id', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'shortcut-text', + this._builder.get_object('shortcut_entry'), + 'text', + Gio.SettingsBindFlags.DEFAULT, + ) + this._settings.connect('changed::shortcut-text', () => { + setShortcut(this._settings, 'shortcut') + }) + + // Create dialog for number overlay options + this._builder.get_object('overlay_button').connect('clicked', () => { + let box = this._builder.get_object('box_overlay_shortcut') + + let dialog = this._createPreferencesDialog( + _('Advanced hotkeys options'), + box, + () => { + // restore default settings for the relevant keys + let keys = [ + 'hotkey-prefix-text', + 'shortcut-text', + 'hotkeys-overlay-combo', + 'shortcut-previews', + ] + keys.forEach(function (val) { + this._settings.set_value(val, this._settings.get_default_value(val)) + }, this) + }, + ) + + dialog.show() + dialog.set_default_size(600, 1) + }) + + // Fine-tune panel + + let scaleInfos = [ + { + objectName: 'panel_size_scale', + valueName: 'tray-size', + range: DEFAULT_PANEL_SIZES, + getValue: () => + PanelSettings.getPanelSize(this._settings, this._currentMonitorIndex), + setValue: (value) => { + const monitorSync = this._settings.get_boolean( + 'panel-element-positions-monitors-sync', + ) + const monitorsToSetFor = monitorSync + ? Object.keys(this.monitors) + : [this._currentMonitorIndex] + monitorsToSetFor.forEach((monitorIndex) => { + PanelSettings.setPanelSize(this._settings, monitorIndex, value) + }) + }, + }, + panelLengthScale, + { + objectName: 'global_border_radius_scale', + valueName: 'global-border-radius', + range: [5, 4, 3, 2, 1, 0], + rangeFactor: 5, + }, + { + objectName: 'panel_margin_scale', + valueName: 'panel-margin', + range: DEFAULT_MARGIN_SIZES, + }, + ] + + let connectValueChanged = (scaleObj, scaleInfo) => { + let timeoutId = 0 + + scaleObj = scaleObj || this._builder.get_object(scaleInfo.objectName) + + scaleInfo.valueChangedId = scaleObj.connect('value-changed', () => { + // Avoid settings the size consinuosly + if (timeoutId > 0) GLib.Source.remove(timeoutId) + + timeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + SCALE_UPDATE_TIMEOUT, + () => { + let value = scaleObj.get_value() + + scaleInfo.setValue + ? scaleInfo.setValue(value) + : this._settings.set_int(scaleInfo.valueName, value) + timeoutId = 0 + + return GLib.SOURCE_REMOVE + }, + ) + }) + } + + for (const idx in scaleInfos) { + let scaleInfo = scaleInfos[idx] + let scaleObj = this._builder.get_object(scaleInfo.objectName) + let range = scaleInfo.range + let factor = scaleInfo.rangeFactor + let value = scaleInfo.getValue + ? scaleInfo.getValue() + : this._settings.get_int(scaleInfo.valueName) + + scaleObj.set_range(range[range.length - 1], range[0]) + scaleObj.set_value(value) + // Add marks from range arrays, omitting the first and last values. + range.slice(1, -1).forEach(function (val) { + scaleObj.add_mark( + val, + Gtk.PositionType.TOP, + (val * (factor || 1)).toString(), + ) + }) + + if (!scaleInfo.manualConnect) connectValueChanged(scaleObj, scaleInfo) + + scaleObj.set_format_value_func((scale, value) => { + return `${value * (factor || 1)} ${scaleInfo.unit || 'px'}` + }) + + // Corrent for rtl languages + if (this._rtl) { + // Flip value position: this is not done automatically + scaleObj.set_value_pos(Gtk.PositionType.LEFT) + // I suppose due to a bug, having a more than one mark and one above a value of 100 + // makes the rendering of the marks wrong in rtl. This doesn't happen setting the scale as not flippable + // and then manually inverting it + scaleObj.set_flippable(false) + scaleObj.set_inverted(true) + } + } + + maybeSetPanelLengthScaleValueChange( + PanelSettings.getPanelLength(this._settings, this._currentMonitorIndex), + ) + + this._settings.bind( + 'stockgs-keep-top-panel', + this._builder.get_object('stockgs_top_panel_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + + this._settings.bind( + 'stockgs-panelbtn-click-only', + this._builder.get_object('stockgs_panelbtn_switch'), + 'active', + Gio.SettingsBindFlags.DEFAULT, + ) + } +} + +const BuilderScope = GObject.registerClass( + { + Implements: [Gtk.BuilderScope], + }, + class BuilderScope extends GObject.Object { + _init(preferences) { + this._preferences = preferences + super._init() + } + + vfunc_create_closure(builder, handlerName, flags, connectObject) { + if (flags & Gtk.BuilderClosureFlags.SWAPPED) + throw new Error('Unsupported template signal flag "swapped"') + + if (typeof this[handlerName] === 'undefined') + throw new Error(`${handlerName} is undefined`) + + return this[handlerName].bind(connectObject || this) + } + + on_btn_click(connectObject) { + connectObject.set_label('Clicked') + } + + position_bottom_button_clicked_cb(button) { + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.BOTTOM) + } + + position_top_button_clicked_cb(button) { + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.TOP) + } + + position_left_button_clicked_cb(button) { + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.LEFT) + } + + position_right_button_clicked_cb(button) { + if (!this._preferences._ignorePositionRadios && button.get_active()) + this._preferences._setPanelPosition(Pos.RIGHT) + } + }, +) + +export default class ZorinTaskbarPreferences extends ExtensionPreferences { + fillPreferencesWindow(window) { + let closeRequestId = null + + window._settings = this.getSettings( + 'org.gnome.shell.extensions.zorin-taskbar', + ) + window._gnomeInterfaceSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }) + + // use default width or window + window.set_default_size(0, 625) + + new Preferences(window, window._settings, window._gnomeInterfaceSettings, this.path) + } +} diff --git a/src/proximity.js b/src/proximity.js new file mode 100644 index 0000000..ee2ca5c --- /dev/null +++ b/src/proximity.js @@ -0,0 +1,285 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + */ + +import Meta from 'gi://Meta' +import Mtk from 'gi://Mtk' + +import * as Main from 'resource:///org/gnome/shell/ui/main.js' + +import * as Utils from './utils.js' + +//timeout intervals +const MIN_UPDATE_MS = 200 + +//timeout names +const T1 = 'limitUpdateTimeout' + +export const Mode = { + ALL_WINDOWS: 0, + FOCUSED_WINDOWS: 1, + MAXIMIZED_WINDOWS: 2, +} + +export class ProximityWatch { + constructor(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { + this.actor = actor + this.monitorIndex = monitorIndex + this.overlap = false + this.mode = mode + this.threshold = [xThreshold, yThreshold] + this.handler = handler + + this._allocationChangedId = actor.connect('notify::allocation', () => + this._updateWatchRect(), + ) + + this._updateWatchRect() + } + + destroy() { + this.actor.disconnect(this._allocationChangedId) + } + + _updateWatchRect() { + let [actorX, actorY] = this.actor.get_position() + + this.rect = new Mtk.Rectangle({ + x: actorX - this.threshold[0], + y: actorY - this.threshold[1], + width: this.actor.width + this.threshold[0] * 2, + height: this.actor.height + this.threshold[1] * 2, + }) + } +} + +export const ProximityManager = class { + constructor() { + this._counter = 1 + this._watches = {} + this._focusedWindowInfo = null + + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + this._bindSignals() + this._setFocusedWindow() + } + + createWatch(actor, monitorIndex, mode, xThreshold, yThreshold, handler) { + let watch = new ProximityWatch( + actor, + monitorIndex, + mode, + xThreshold, + yThreshold, + handler, + ) + + this._watches[this._counter] = watch + this.update() + + return this._counter++ + } + + removeWatch(id) { + if (this._watches[id]) { + this._watches[id].destroy() + delete this._watches[id] + } + } + + update() { + this._queueUpdate(true) + } + + destroy() { + this._signalsHandler.destroy() + this._timeoutsHandler.destroy() + this._disconnectFocusedWindow() + Object.keys(this._watches).forEach((id) => this.removeWatch(id)) + } + + _bindSignals() { + this._signalsHandler.add( + [global.window_manager, 'switch-workspace', () => this._queueUpdate()], + [Main.overview, 'hidden', () => this._queueUpdate()], + [ + global.display, + 'notify::focus-window', + () => { + this._setFocusedWindow() + this._queueUpdate() + }, + ], + [global.display, 'restacked', () => this._queueUpdate()], + ) + } + + _setFocusedWindow() { + this._disconnectFocusedWindow() + + let focusedWindow = global.display.focus_window + + if (focusedWindow) { + let focusedWindowInfo = this._getFocusedWindowInfo(focusedWindow) + + if ( + focusedWindowInfo && + this._checkIfHandledWindowType(focusedWindowInfo.metaWindow) + ) { + focusedWindowInfo.allocationId = focusedWindowInfo.window.connect( + 'notify::allocation', + () => this._queueUpdate(), + ) + focusedWindowInfo.destroyId = focusedWindowInfo.window.connect( + 'destroy', + () => this._disconnectFocusedWindow(true), + ) + + this._focusedWindowInfo = focusedWindowInfo + } + } + } + + _getFocusedWindowInfo(focusedWindow) { + let window = focusedWindow.get_compositor_private() + let focusedWindowInfo + + if (window) { + focusedWindowInfo = { window: window } + focusedWindowInfo.metaWindow = focusedWindow + + if (focusedWindow.is_attached_dialog()) { + let mainMetaWindow = focusedWindow.get_transient_for() + + if ( + focusedWindowInfo.metaWindow.get_frame_rect().height < + mainMetaWindow.get_frame_rect().height + ) { + focusedWindowInfo.window = mainMetaWindow.get_compositor_private() + focusedWindowInfo.metaWindow = mainMetaWindow + } + } + } + + return focusedWindowInfo + } + + _disconnectFocusedWindow(destroy) { + if (this._focusedWindowInfo && !destroy) { + this._focusedWindowInfo.window.disconnect( + this._focusedWindowInfo.allocationId, + ) + this._focusedWindowInfo.window.disconnect( + this._focusedWindowInfo.destroyId, + ) + } + + this._focusedWindowInfo = null + } + + _getHandledWindows() { + return Utils.getCurrentWorkspace() + .list_windows() + .filter((mw) => this._checkIfHandledWindow(mw)) + } + + _checkIfHandledWindow(metaWindow) { + return ( + metaWindow && + !metaWindow.minimized && + !metaWindow.customJS_ding && + metaWindow.window_type != Meta.WindowType.DESKTOP && + this._checkIfHandledWindowType(metaWindow) + ) + } + + _checkIfHandledWindowType(metaWindow) { + let metaWindowType = metaWindow.get_window_type() + + //https://www.roojs.org/seed/gir-1.2-gtk-3.0/seed/Meta.WindowType.html + return ( + metaWindowType <= Meta.WindowType.SPLASHSCREEN && + metaWindowType != Meta.WindowType.DESKTOP + ) + } + + _queueUpdate(noDelay) { + if (!noDelay && this._timeoutsHandler.getId(T1)) { + //limit the number of updates + this._pendingUpdate = true + return + } + + this._timeoutsHandler.add([T1, MIN_UPDATE_MS, () => this._endLimitUpdate()]) + + let metaWindows = this._getHandledWindows() + + Object.keys(this._watches).forEach((id) => { + let watch = this._watches[id] + let overlap = !!this._update(watch, metaWindows) + + if (overlap !== watch.overlap) { + watch.handler(overlap) + watch.overlap = overlap + } + }) + } + + _endLimitUpdate() { + if (this._pendingUpdate) { + this._pendingUpdate = false + this._queueUpdate() + } + } + + _update(watch, metaWindows) { + if (watch.mode === Mode.FOCUSED_WINDOWS) + return ( + this._focusedWindowInfo && + this._checkIfHandledWindow(this._focusedWindowInfo.metaWindow) && + this._checkProximity(this._focusedWindowInfo.metaWindow, watch) + ) + + if (watch.mode === Mode.MAXIMIZED_WINDOWS) + return metaWindows.some( + (mw) => + mw.maximized_vertically && + mw.maximized_horizontally && + mw.get_monitor() == watch.monitorIndex, + ) + + //Mode.ALL_WINDOWS + return metaWindows.some((mw) => this._checkProximity(mw, watch)) + } + + _checkProximity(metaWindow, watch) { + let windowRect = metaWindow.get_frame_rect() + + return ( + windowRect.overlap(watch.rect) && + ((!watch.threshold[0] && !watch.threshold[1]) || + metaWindow.get_monitor() == watch.monitorIndex || + windowRect.overlap( + global.display.get_monitor_geometry(watch.monitorIndex), + )) + ) + } +} diff --git a/stylesheet.css b/src/stylesheet.css similarity index 66% rename from stylesheet.css rename to src/stylesheet.css index 6dd4d17..1ea5d34 100644 --- a/stylesheet.css +++ b/src/stylesheet.css @@ -44,7 +44,6 @@ #zorintaskbarTaskbar .dash-item-container .overview-tile:hover, #zorintaskbarTaskbar .dash-item-container .overview-tile .dtp-container .overview-icon, -#zorintaskbarScrollview .overview-tile:hover .dtp-container.animate-appicon-hover, .zorintaskbarMainPanel .dash-item-container .show-apps:hover .overview-icon { background: none; } @@ -112,32 +111,13 @@ spacing: 0px; } -#panelBox.floating { - background: transparent; -} -#panelBox.floating.top { - padding: 8px 8px 0 8px; -} -#panelBox.floating.right { - padding: 8px 8px 8px 0; -} -#panelBox.floating.bottom { - padding: 0 8px 8px 8px; -} -#panelBox.floating.left { - padding: 8px 0 8px 8px; -} - -#panelBox.floating #panel, -#panelBox.floating .panel-button, -#panelBox.floating .panel-button.clock-display .clock { - border-radius: 10px; -} - #zorintaskbarScrollview .badge { color: rgba(255, 255, 255, 1); + padding: 0.2em 0.5em; + border-radius: 1em; font-weight: bold; text-align: center; + margin: 0 0 0 2px; } #zorintaskbarScrollview .number-overlay { @@ -159,3 +139,64 @@ #preview-menu { border-radius: 10px; } + +/* border radius, grrr no css variables in ST */ +#uiGroup.br5 .zorintaskbarPanel.dock, +#uiGroup.br5 .show-apps, +#uiGroup.br5 .dtp-container, +#uiGroup.br5 .dtp-dots-container { + border-radius: 5px !important; + overflow: hidden !important; +} +#uiGroup.br5 .zorintaskbarMainPanel .panel-button, +#uiGroup.br5 .zorintaskbarMainPanel .panel-button.clock-display .clock { + border-radius: 5px; +} + +#uiGroup.br10 .zorintaskbarPanel.dock, +#uiGroup.br10 .show-apps, +#uiGroup.br10 .dtp-container, +#uiGroup.br10 .dtp-dots-container { + border-radius: 10px !important; + overflow: hidden !important; +} +#uiGroup.br10 .zorintaskbarMainPanel .panel-button, +#uiGroup.br10 .zorintaskbarMainPanel .panel-button.clock-display .clock { + border-radius: 10px; +} + +#uiGroup.br15 .zorintaskbarPanel.dock, +#uiGroup.br15 .show-apps, +#uiGroup.br15 .dtp-container, +#uiGroup.br15 .dtp-dots-container { + border-radius: 15px !important; + overflow: hidden !important; +} +#uiGroup.br15 .zorintaskbarMainPanel .panel-button, +#uiGroup.br15 .zorintaskbarMainPanel .panel-button.clock-display .clock { + border-radius: 15px; +} + +#uiGroup.br20 .zorintaskbarPanel.dock, +#uiGroup.br20 .show-apps, +#uiGroup.br20 .dtp-container, +#uiGroup.br20 .dtp-dots-container { + border-radius: 20px !important; + overflow: hidden !important; +} +#uiGroup.br20 .zorintaskbarMainPanel .panel-button, +#uiGroup.br20 .zorintaskbarMainPanel .panel-button.clock-display .clock { + border-radius: 20px; +} + +#uiGroup.br25 .zorintaskbarPanel.dock, +#uiGroup.br25 .show-apps, +#uiGroup.br25 .dtp-container, +#uiGroup.br25 .dtp-dots-container { + border-radius: 25px !important; + overflow: hidden !important; +} +#uiGroup.br25 .zorintaskbarMainPanel .panel-button, +#uiGroup.br25 .zorintaskbarMainPanel .panel-button.clock-display .clock { + border-radius: 25px; +} diff --git a/src/taskbar.js b/src/taskbar.js new file mode 100644 index 0000000..3b43bf9 --- /dev/null +++ b/src/taskbar.js @@ -0,0 +1,1479 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import Clutter from 'gi://Clutter' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import GObject from 'gi://GObject' +import Graphene from 'gi://Graphene' +import Shell from 'gi://Shell' +import St from 'gi://St' + +import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js' +import * as Dash from 'resource:///org/gnome/shell/ui/dash.js' +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import { EventEmitter } from 'resource:///org/gnome/shell/misc/signals.js' + +import * as AppIcons from './appIcons.js' +import * as PanelManager from './panelManager.js' +import * as PanelSettings from './panelSettings.js' +import * as Pos from './panelPositions.js' +import * as Utils from './utils.js' +import * as WindowPreview from './windowPreview.js' +import { SETTINGS, tracker } from './extension.js' + +const SearchController = Main.overview.searchController + +export var hotkeyAppNumbers = {} + +export const DASH_ANIMATION_TIME = 0.2 // Dash.DASH_ANIMATION_TIME is now private +const DASH_ITEM_HOVER_TIMEOUT = 0.3 // Dash.DASH_ITEM_HOVER_TIMEOUT is now private +export const MIN_ICON_SIZE = 4 +export const APPICON_MARGIN = 6 +export const APPICON_PADDING = 8 + +const T1 = 'ensureAppIconVisibilityTimeout' +const T2 = 'showLabelTimeout' +const T3 = 'resetHoverTimeout' + +/** + * Extend DashItemContainer + * + * - set label position based on taskbar orientation + * + * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. + * thus use this ugly pattern. + */ + +export function extendDashItemContainer(dashItemContainer) { + dashItemContainer.showLabel = AppIcons.ItemShowLabel +} + +/* This class is a fork of the upstream DashActor class (ui.dash.js) + * + * Summary of changes: + * - modified chldBox calculations for when 'show-apps-at-top' option is checked + * - handle horizontal dash + */ +export const TaskbarActor = GObject.registerClass( + {}, + class TaskbarActor extends St.Widget { + _init(delegate) { + this._delegate = delegate + this._currentBackgroundColor = 0 + super._init({ + name: 'zorintaskbarTaskbar', + layout_manager: new Clutter.BoxLayout({ + orientation: + Clutter.Orientation[ + delegate.dtpPanel.getOrientation().toUpperCase() + ], + }), + clip_to_allocation: true, + }) + } + + vfunc_allocate(box) { + this.set_allocation(box) + + let panel = this._delegate.dtpPanel + let availFixedSize = box[panel.fixedCoord.c2] - box[panel.fixedCoord.c1] + let availVarSize = box[panel.varCoord.c2] - box[panel.varCoord.c1] + let [dummy, scrollview, leftFade, rightFade] = this.get_children() + let [, natSize] = this[panel.sizeFunc](availFixedSize) + let childBox = new Clutter.ActorBox() + let orientation = panel.getOrientation() + + dummy.allocate(childBox) + + childBox[panel.varCoord.c1] = box[panel.varCoord.c1] + childBox[panel.varCoord.c2] = Math.min(availVarSize, natSize) + childBox[panel.fixedCoord.c1] = box[panel.fixedCoord.c1] + childBox[panel.fixedCoord.c2] = box[panel.fixedCoord.c2] + + scrollview.allocate(childBox) + + let [, , upper, , , pageSize] = + scrollview[orientation[0] + 'adjustment'].get_values() + upper = Math.floor(upper) + scrollview._dtpFadeSize = upper > pageSize ? this._delegate.iconSize : 0 + + if ( + this._currentBackgroundColor !== + panel.dynamicTransparency.currentBackgroundColor + ) { + this._currentBackgroundColor = + panel.dynamicTransparency.currentBackgroundColor + let gradientStyle = + 'background-gradient-start: ' + + this._currentBackgroundColor + + 'background-gradient-direction: ' + + orientation + + leftFade.set_style(gradientStyle) + rightFade.set_style(gradientStyle) + } + + childBox[panel.varCoord.c2] = + childBox[panel.varCoord.c1] + scrollview._dtpFadeSize + leftFade.allocate(childBox) + + childBox[panel.varCoord.c1] = + box[panel.varCoord.c2] - scrollview._dtpFadeSize + childBox[panel.varCoord.c2] = box[panel.varCoord.c2] + rightFade.allocate(childBox) + } + + // We want to request the natural size of all our children + // as our natural width, so we chain up to StWidget (which + // then calls BoxLayout) + vfunc_get_preferred_width(forHeight) { + let [, natWidth] = St.Widget.prototype.vfunc_get_preferred_width.call( + this, + forHeight, + ) + + return [0, natWidth] + } + + vfunc_get_preferred_height(forWidth) { + let [, natHeight] = St.Widget.prototype.vfunc_get_preferred_height.call( + this, + forWidth, + ) + + return [0, natHeight] + } + }, +) + +/* This class is a fork of the upstream dash class (ui.dash.js) + * + * Summary of changes: + * - disconnect global signals adding a destroy method; + * - play animations even when not in overview mode + * - set a maximum icon size + * - show running and/or favorite applications + * - emit a custom signal when an app icon is added + * - Add scrollview + * Ensure actor is visible on keyfocus inside the scrollview + * - add 128px icon size, might be useful for hidpi display + * - Sync minimization application target position. + */ + +export const Taskbar = class extends EventEmitter { + constructor(panel) { + super() + + this.dtpPanel = panel + + // start at smallest size due to running indicator drawing area expanding but not shrinking + this.iconSize = 16 + + this._shownInitially = false + + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._timeoutsHandler = new Utils.TimeoutsHandler() + + this._labelShowing = false + this.fullScrollView = 0 + + let isVertical = panel.checkIfVertical() + + this._box = Utils.createBoxLayout({ + vertical: isVertical, + clip_to_allocation: false, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + }) + + this._container = new TaskbarActor(this) + this._scrollView = new St.ScrollView({ + name: 'zorintaskbarScrollview', + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.NEVER, + enable_mouse_scrolling: true, + }) + + this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this)) + this._scrollView.add_child(this._box) + + this._showAppsIconWrapper = panel.showAppsIconWrapper + this._showAppsIconWrapper.connect( + 'menu-state-changed', + (showAppsIconWrapper, opened) => { + this._itemMenuStateChanged(showAppsIconWrapper, opened) + }, + ) + // an instance of the showAppsIcon class is encapsulated in the wrapper + this._showAppsIcon = this._showAppsIconWrapper.realShowAppsIcon + this.showAppsButton = this._showAppsIcon.toggleButton + + if (isVertical) { + this.showAppsButton.set_width(panel.geom.w) + } + + this.showAppsButton.connect( + 'notify::checked', + this._onShowAppsButtonToggled.bind(this), + ) + + this.showAppsButton.checked = SearchController._showAppsButton + ? SearchController._showAppsButton.checked + : false + + this._showAppsIcon.childScale = 1 + this._showAppsIcon.childOpacity = 255 + this._showAppsIcon.icon.setIconSize(this.iconSize) + this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper) + + this._container.add_child(new St.Widget({ width: 0, reactive: false })) + this._container.add_child(this._scrollView) + + let orientation = panel.getOrientation() + let fadeStyle = 'background-gradient-direction:' + orientation + this._fadeLeft = new St.Widget({ + style_class: 'scrollview-fade', + reactive: false, + }) + this._fadeRight = new St.Widget({ + style_class: 'scrollview-fade', + reactive: false, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + rotation_angle_z: 180, + }) + + this._fadeLeft.set_style(fadeStyle) + this._fadeRight.set_style(fadeStyle) + + this._container.add_child(this._fadeLeft) + this._container.add_child(this._fadeRight) + + this.previewMenu = new WindowPreview.PreviewMenu(panel) + this.previewMenu.enable() + + let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL + this.actor = new St.Bin({ + child: this._container, + y_align: Clutter.ActorAlign.START, + x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START, + }) + + const adjustment = this._scrollView[orientation[0] + 'adjustment'] + + this._workId = Main.initializeDeferredWork( + this._box, + this._redisplay.bind(this), + ) + + this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }) + + this._appSystem = Shell.AppSystem.get_default() + + this.iconAnimator = new PanelManager.IconAnimator(this.dtpPanel.panel) + + this._signalsHandler.add( + [this.dtpPanel.panel, 'notify::height', () => this._queueRedisplay()], + [this.dtpPanel.panel, 'notify::width', () => this._queueRedisplay()], + [ + this._appSystem, + 'installed-changed', + () => { + AppFavorites.getAppFavorites().reload() + this._queueRedisplay() + }, + ], + [this._appSystem, 'app-state-changed', this._queueRedisplay.bind(this)], + [ + AppFavorites.getAppFavorites(), + 'changed', + this._queueRedisplay.bind(this), + ], + [ + global.window_manager, + 'switch-workspace', + () => this._connectWorkspaceSignals(), + ], + [ + Utils.DisplayWrapper.getScreen(), + ['window-entered-monitor', 'window-left-monitor'], + () => { + if (SETTINGS.get_boolean('isolate-monitors')) { + this._queueRedisplay() + } + }, + ], + [Main.overview, 'item-drag-begin', this._onDragBegin.bind(this)], + [Main.overview, 'item-drag-end', this._onDragEnd.bind(this)], + [Main.overview, 'item-drag-cancelled', this._onDragCancelled.bind(this)], + [ + // Ensure the ShowAppsButton status is kept in sync + SearchController._showAppsButton, + 'notify::checked', + this._syncShowAppsButtonToggled.bind(this), + ], + [ + SETTINGS, + [ + 'changed::show-favorites', + 'changed::show-running-apps', + 'changed::show-favorites-all-monitors', + ], + () => { + setAttributes() + this._redisplay() + }, + ], + [ + SETTINGS, + 'changed::group-apps', + () => { + setAttributes() + this._connectWorkspaceSignals() + }, + ], + [ + SETTINGS, + [ + 'changed::group-apps-use-launchers', + 'changed::taskbar-locked', + ], + () => { + setAttributes() + this.resetAppIcons() + }, + ], + [ + adjustment, + ['notify::upper', 'notify::pageSize'], + () => this._onScrollSizeChange(adjustment), + ], + ) + + let setAttributes = () => { + this.isGroupApps = SETTINGS.get_boolean('group-apps') + this.usingLaunchers = + !this.isGroupApps && SETTINGS.get_boolean('group-apps-use-launchers') + this.showFavorites = + SETTINGS.get_boolean('show-favorites') && + (this.dtpPanel.isPrimary || + SETTINGS.get_boolean('show-favorites-all-monitors')) + this.showRunningApps = SETTINGS.get_boolean('show-running-apps') + this.allowSplitApps = + this.usingLaunchers || (!this.isGroupApps && !this.showFavorites) + } + + setAttributes() + + this._onScrollSizeChange(adjustment) + this._connectWorkspaceSignals() + } + + destroy() { + if (this._waitIdleId) { + GLib.source_remove(this._waitIdleId) + this._waitIdleId = 0 + } + + this._timeoutsHandler.destroy() + this.iconAnimator.destroy() + + this._signalsHandler.destroy() + this._signalsHandler = 0 + + this._container.destroy() + + this.previewMenu.disable() + this.previewMenu.destroy() + + this._disconnectWorkspaceSignals() + } + + _maybeUpdateScrollviewFade(adjustment) { + if (this._scrollView._dtpFadeSize) { + adjustment = + adjustment || + this._scrollView[this.dtpPanel.getOrientation()[0] + 'adjustment'] + let [value, , upper, , , pageSize] = adjustment.get_values() + + this._fadeLeft.visible = value > 0 + this._fadeRight.visible = value + pageSize < upper + } + } + + _onScrollEvent(actor, event) { + let orientation = this.dtpPanel.getOrientation() + + // reset timeout to avid conflicts with the mousehover event + this._timeoutsHandler.add([T1, 0, () => (this._swiping = false)]) + + // Skip to avoid double events mouse + if (event.is_pointer_emulated()) return Clutter.EVENT_STOP + + let adjustment, delta + + adjustment = this._scrollView[orientation[0] + 'adjustment'] + + let increment = adjustment.step_increment + + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + case Clutter.ScrollDirection.LEFT: + delta = -increment + break + case Clutter.ScrollDirection.DOWN: + case Clutter.ScrollDirection.RIGHT: + delta = +increment + break + case Clutter.ScrollDirection.SMOOTH: { + let [dx, dy] = event.get_scroll_delta() + delta = dy * increment + delta += dx * increment + break + } + } + + adjustment.set_value(adjustment.get_value() + delta) + + this._maybeUpdateScrollviewFade(adjustment) + + return Clutter.EVENT_STOP + } + + _onScrollSizeChange(adjustment) { + // Update minimization animation target position on scrollview change. + this._updateAppIcons() + this._maybeUpdateScrollviewFade() + + // When applications are ungrouped and there is some empty space on the horizontal taskbar, + // force a fixed label width to prevent the icons from "wiggling" when an animation runs + // (adding or removing an icon). When the taskbar is full, revert to a dynamic label width + // to allow them to resize and make room for new icons. + if (!this.dtpPanel.checkIfVertical() && !this.isGroupApps) { + let initial = this.fullScrollView + + if ( + !this.fullScrollView && + Math.floor(adjustment.upper) > adjustment.page_size + ) { + this.fullScrollView = adjustment.page_size + } else if (adjustment.page_size < this.fullScrollView) { + this.fullScrollView = 0 + } + + if (initial != this.fullScrollView && !this._waitIdleId) { + this._waitIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + this._getAppIcons().forEach((a) => a.updateTitleStyle()) + this._waitIdleId = 0 + + return GLib.SOURCE_REMOVE + }) + } + } + } + + _onDragBegin() { + this._dragCancelled = false + this._dragMonitor = { + dragMotion: this._onDragMotion.bind(this), + } + DND.addDragMonitor(this._dragMonitor) + + if (this._box.get_n_children() == 0) { + this._emptyDropTarget = new Dash.EmptyDropTargetItem() + this._box.insert_child_at_index(this._emptyDropTarget, 0) + this._emptyDropTarget.show(true) + } + + this._toggleFavoriteHighlight(true) + } + + _onDragCancelled() { + this._dragCancelled = true + + if (this._dragInfo) { + this._box.set_child_at_index( + this._dragInfo[1]._dashItemContainer, + this._dragInfo[0], + ) + } + + this._endDrag() + } + + _onDragEnd() { + if (this._dragCancelled) return + + this._endDrag() + } + + _endDrag() { + if ( + this._dragInfo && + this._dragInfo[1]._dashItemContainer instanceof DragPlaceholderItem + ) { + this._box.remove_child(this._dragInfo[1]._dashItemContainer) + this._dragInfo[1]._dashItemContainer.destroy() + delete this._dragInfo[1]._dashItemContainer + } + + this._dragInfo = null + this._clearEmptyDropTarget() + this._showAppsIcon.setDragApp(null) + DND.removeDragMonitor(this._dragMonitor) + + this._dragMonitor = null + this.emit('end-drag') + + this._toggleFavoriteHighlight() + } + + _onDragMotion(dragEvent) { + let app = Dash.Dash.getAppFromSource(dragEvent.source) + if (app == null) return DND.DragMotionResult.CONTINUE + + let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor) + + if (showAppsHovered) this._showAppsIcon.setDragApp(app) + else this._showAppsIcon.setDragApp(null) + + return DND.DragMotionResult.CONTINUE + } + + _toggleFavoriteHighlight(show) { + let appFavorites = AppFavorites.getAppFavorites() + let cssFuncName = (show ? 'add' : 'remove') + '_style_class_name' + + if (this.showFavorites) + this._getAppIcons() + .filter( + (appIcon) => + (this.usingLaunchers && appIcon.isLauncher) || + (!this.usingLaunchers && + appFavorites.isFavorite(appIcon.app.get_id())), + ) + .forEach((fav) => fav._container[cssFuncName]('favorite')) + } + + handleIsolatedWorkspaceSwitch() { + this._shownInitially = this.isGroupApps + this._queueRedisplay() + } + + _connectWorkspaceSignals() { + this._disconnectWorkspaceSignals() + + this._lastWorkspace = + Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace() + + this._workspaceWindowAddedId = this._lastWorkspace.connect( + 'window-added', + () => this._queueRedisplay(), + ) + this._workspaceWindowRemovedId = this._lastWorkspace.connect( + 'window-removed', + () => this._queueRedisplay(), + ) + } + + _disconnectWorkspaceSignals() { + if (this._lastWorkspace) { + this._lastWorkspace.disconnect(this._workspaceWindowAddedId) + this._lastWorkspace.disconnect(this._workspaceWindowRemovedId) + + this._lastWorkspace = null + } + } + + _queueRedisplay() { + Main.queueDeferredWork(this._workId) + } + + _hookUpLabel(item, syncHandler) { + item.child.connect('notify::hover', () => { + this._syncLabel(item, syncHandler) + }) + + syncHandler.connect('sync-tooltip', () => { + this._syncLabel(item, syncHandler) + }) + } + + _createAppItem(app, window, isLauncher) { + let appIcon = new AppIcons.TaskbarAppIcon( + { + app, + window, + isLauncher, + }, + this.dtpPanel, + { + setSizeManually: true, + showLabel: false, + isDraggable: !SETTINGS.get_boolean('taskbar-locked'), + }, + this.previewMenu, + this.iconAnimator, + ) + + if (appIcon._draggable) { + appIcon._draggable.connect('drag-begin', () => { + appIcon.opacity = 0 + appIcon.isDragged = 1 + }) + appIcon._draggable.connect('drag-end', () => { + appIcon.opacity = 255 + delete appIcon.isDragged + this._updateAppIcons() + }) + } + + appIcon.connect('menu-state-changed', (appIcon, opened) => { + this._itemMenuStateChanged(item, opened) + }) + + let item = new TaskbarItemContainer() + + item._dtpPanel = this.dtpPanel + extendDashItemContainer(item) + + item.setChild(appIcon) + appIcon._dashItemContainer = item + + appIcon.connect('notify::hover', () => { + if (appIcon.hover) { + this._timeoutsHandler.add([ + T1, + 100, + () => + Utils.ensureActorVisibleInScrollView( + this._scrollView, + appIcon, + this._scrollView._dtpFadeSize, + ), + ]) + } else { + this._timeoutsHandler.remove(T1) + } + }) + + appIcon.connect('clicked', (actor) => { + Utils.ensureActorVisibleInScrollView( + this._scrollView, + actor, + this._scrollView._dtpFadeSize, + ) + }) + + appIcon.connect('key-focus-in', (actor) => { + let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView( + this._scrollView, + actor, + this._scrollView._dtpFadeSize, + ) + + // This signal is triggered also by mouse click. The popup menu is opened at the original + // coordinates. Thus correct for the shift which is going to be applied to the scrollview. + if (appIcon._menu) { + appIcon._menu._boxPointer.xOffset = -x_shift + appIcon._menu._boxPointer.yOffset = -y_shift + } + }) + + // Override default AppIcon label_actor, now the + // accessible_name is set at DashItemContainer.setLabelText + appIcon.label_actor = null + item.setLabelText(app.get_name()) + + appIcon.icon.setIconSize(this.iconSize) + this._hookUpLabel(item, appIcon) + + return item + } + + // Return an array with the "proper" appIcons currently in the taskbar + _getAppIcons() { + // Only consider children which are "proper" icons and which are not + // animating out (which means they will be destroyed at the end of + // the animation) + return this._getTaskbarIcons().map(function (actor) { + return actor.child._delegate + }) + } + + _getTaskbarIcons(includeAnimated) { + return this._box.get_children().filter(function (actor) { + return ( + actor.child && + actor.child._delegate && + actor.child._delegate.icon && + (includeAnimated || !actor.animatingOut) + ) + }) + } + + _updateAppIcons() { + let appIcons = this._getAppIcons() + + appIcons + .filter((icon) => icon.constructor === AppIcons.TaskbarAppIcon) + .forEach((icon) => { + icon.updateIcon() + }) + } + + _itemMenuStateChanged(item, opened) { + // When the menu closes, it calls sync_hover, which means + // that the notify::hover handler does everything we need to. + if (opened) { + this._timeoutsHandler.remove(T2) + + item.hideLabel() + } else { + // I want to listen from outside when a menu is closed. I used to + // add a custom signal to the appIcon, since gnome 3.8 the signal + // calling this callback was added upstream. + this.emit('menu-closed') + } + } + + _syncLabel(item, syncHandler) { + let shouldShow = syncHandler + ? syncHandler.shouldShowTooltip() + : item.child.get_hover() + + if (shouldShow) { + if (!this._timeoutsHandler.getId(T2)) { + let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT + + this._timeoutsHandler.add([ + T2, + timeout, + () => { + this._labelShowing = true + item.showLabel() + }, + ]) + + this._timeoutsHandler.remove(T3) + } + } else { + this._timeoutsHandler.remove(T2) + + item.hideLabel() + if (this._labelShowing) { + this._timeoutsHandler.add([ + T3, + DASH_ITEM_HOVER_TIMEOUT, + () => (this._labelShowing = false), + ]) + } + } + } + + _adjustIconSize() { + let panelSize = this.dtpPanel.geom.iconSize / Utils.getScaleFactor() + let availSize = panelSize - APPICON_PADDING * 2 + let minIconSize = MIN_ICON_SIZE + (panelSize % 2) + + if (availSize == this.iconSize) return + + if (availSize < minIconSize) { + availSize = minIconSize + } + + // For the icon size, we only consider children which are "proper" + // icons and which are not animating out (which means they will be + // destroyed at the end of the animation) + let iconChildren = this._getTaskbarIcons().concat([this._showAppsIcon]) + let scale = this.iconSize / availSize + + this.iconSize = availSize + + for (let i = 0; i < iconChildren.length; i++) { + let icon = iconChildren[i].child._delegate.icon + + // Set the new size immediately, to keep the icons' sizes + // in sync with this.iconSize + icon.setIconSize(this.iconSize) + + // Don't animate the icon size change when the overview + // is transitioning, or when initially filling + // the taskbar + if (Main.overview.animationInProgress || !this._shownInitially) continue + + let [targetWidth, targetHeight] = icon.icon.get_size() + + // Scale the icon's texture to the previous size and + // tween to the new size + icon.icon.set_size(icon.icon.width * scale, icon.icon.height * scale) + + Utils.animate(icon.icon, { + width: targetWidth, + height: targetHeight, + time: DASH_ANIMATION_TIME, + transition: 'easeOutQuad', + }) + } + } + + sortAppsCompareFunction(appA, appB) { + return ( + getAppStableSequence(appA, this.dtpPanel.monitor) - + getAppStableSequence(appB, this.dtpPanel.monitor) + ) + } + + getAppInfos() { + //get the user's favorite apps + let favoriteApps = this.showFavorites + ? AppFavorites.getAppFavorites().getFavorites() + : [] + + //find the apps that should be in the taskbar: the favorites first, then add the running apps + // When using isolation, we filter out apps that have no windows in + // the current workspace (this check is done in AppIcons.getInterestingWindows) + let runningApps = this.showRunningApps + ? this._getRunningApps().sort(this.sortAppsCompareFunction.bind(this)) + : [] + let appInfos + + if (this.allowSplitApps) { + appInfos = this._createAppInfos(favoriteApps, [], true).concat( + this._createAppInfos(runningApps).filter( + (appInfo) => appInfo.windows.length, + ), + ) + } else { + appInfos = this._createAppInfos( + favoriteApps.concat( + runningApps.filter((app) => favoriteApps.indexOf(app) < 0), + ), + ).filter( + (appInfo) => + appInfo.windows.length || favoriteApps.indexOf(appInfo.app) >= 0, + ) + } + + return appInfos + } + + _redisplay() { + if (!this._signalsHandler) { + return + } + + //get the currently displayed appIcons + let currentAppIcons = this._getTaskbarIcons() + let expectedAppInfos = this.getAppInfos() + + //remove the appIcons which are not in the expected apps list + for (let i = currentAppIcons.length - 1; i > -1; --i) { + let appIcon = currentAppIcons[i].child._delegate + let appIndex = Utils.findIndex( + expectedAppInfos, + (appInfo) => + appInfo.app == appIcon.app && + (!this.allowSplitApps || + this.isGroupApps || + appInfo.windows[0] == appIcon.window) && + appInfo.isLauncher == appIcon.isLauncher, + ) + + if ( + appIndex < 0 || + (appIcon.window && + (this.isGroupApps || + expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) || + (!appIcon.window && + !appIcon.isLauncher && + !this.isGroupApps && + expectedAppInfos[appIndex].windows.length) + ) { + currentAppIcons[i][ + this._shownInitially ? 'animateOutAndDestroy' : 'destroy' + ]() + currentAppIcons.splice(i, 1) + } + } + + //if needed, reorder the existing appIcons and create the missing ones + let currentPosition = 0 + for (let i = 0, l = expectedAppInfos.length; i < l; ++i) { + let neededAppIcons = + this.isGroupApps || !expectedAppInfos[i].windows.length + ? [ + { + app: expectedAppInfos[i].app, + window: null, + isLauncher: expectedAppInfos[i].isLauncher, + }, + ] + : expectedAppInfos[i].windows.map((window) => ({ + app: expectedAppInfos[i].app, + window: window, + isLauncher: false, + })) + + for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) { + //check if the icon already exists + let matchingAppIconIndex = Utils.findIndex( + currentAppIcons, + (appIcon) => + appIcon.child._delegate.app == neededAppIcons[j].app && + appIcon.child._delegate.window == neededAppIcons[j].window, + ) + + if ( + matchingAppIconIndex > 0 && + matchingAppIconIndex != currentPosition + ) { + //moved icon, reposition it + this._box.remove_child(currentAppIcons[matchingAppIconIndex]) + this._box.insert_child_at_index( + currentAppIcons[matchingAppIconIndex], + currentPosition, + ) + } else if (matchingAppIconIndex < 0) { + //the icon doesn't exist yet, create a new one + let newAppIcon = this._createAppItem( + neededAppIcons[j].app, + neededAppIcons[j].window, + neededAppIcons[j].isLauncher, + ) + + this._box.insert_child_at_index(newAppIcon, currentPosition) + currentAppIcons.splice(currentPosition, 0, newAppIcon) + + // Skip animations on first run when adding the initial set + // of items, to avoid all items zooming in at once + newAppIcon.show(this._shownInitially) + } + + ++currentPosition + } + } + + this._adjustIconSize() + + // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 + // Without it, StBoxLayout may use a stale size cache + this._box.queue_relayout() + + // This is required for icon reordering when the scrollview is used. + this._updateAppIcons() + + // This will update the size, and the corresponding number for each icon + this._updateHotkeysNumberOverlay() + + this._shownInitially = true + } + + _getRunningApps() { + let windows = Utils.getAllMetaWindows() + let apps = [] + + for (let i = 0, l = windows.length; i < l; ++i) { + let app = tracker.get_window_app(windows[i]) + + if (app && apps.indexOf(app) < 0) { + apps.push(app) + } + } + + return apps + } + + _createAppInfos(apps, defaultWindows, defaultIsLauncher) { + if (this.allowSplitApps && !defaultIsLauncher) { + let separateApps = [] + + if (apps.length) { + let windows = AppIcons.getInterestingWindows( + null, + this.dtpPanel.monitor, + ).sort(sortWindowsCompareFunction) + + windows.forEach((w) => { + let windowApp = tracker.get_window_app(w) + + if (apps.indexOf(windowApp) >= 0) + separateApps.push({ + app: windowApp, + isLauncher: false, + windows: [w], + }) + }) + } + + return separateApps + } + + return apps.map((app) => ({ + app: app, + isLauncher: defaultIsLauncher || false, + windows: + defaultWindows || + AppIcons.getInterestingWindows(app, this.dtpPanel.monitor).sort( + sortWindowsCompareFunction, + ), + })) + } + + // Reset the displayed apps icon to mantain the correct order + resetAppIcons(geometryChange) { + let children = this._getTaskbarIcons(true) + + for (let i = 0; i < children.length; i++) { + let item = children[i] + item.destroy() + } + + // to avoid ugly animations, just suppress them like when taskbar is first loaded. + this._shownInitially = false + this._redisplay() + + if (geometryChange && this.dtpPanel.checkIfVertical()) { + this.previewMenu._updateClip() + } + } + + _updateHotkeysNumberOverlay() { + let counter = 0 + + if (this.dtpPanel.isPrimary) hotkeyAppNumbers = {} + + this._getAppIcons().forEach((icon) => { + if ( + this.dtpPanel.isPrimary && + (!hotkeyAppNumbers[icon.app] || this.allowSplitApps) + ) { + hotkeyAppNumbers[icon.app] = ++counter + } + + let label = hotkeyAppNumbers[icon.app] + + if (label <= 10) { + icon.setHotkeysNumberOverlayLabel(label == 10 ? 0 : label) + } else { + // No overlay after 10 + icon.setHotkeysNumberOverlayLabel(-1) + } + }) + + if ( + SETTINGS.get_boolean('hot-keys') && + SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS' + ) + this.toggleHotkeysNumberOverlay(true) + } + + toggleHotkeysNumberOverlay(activate) { + let appIcons = this._getAppIcons() + appIcons.forEach(function (icon) { + icon.toggleHotkeysNumberOverlay( + activate ? SETTINGS.get_string('hotkeys-overlay-combo') : false, + ) + }) + } + + _clearEmptyDropTarget() { + if (this._emptyDropTarget) { + this._emptyDropTarget.animateOutAndDestroy() + this._emptyDropTarget = null + } + } + + handleDragOver(source, actor, x, y) { + if (source == Main.xdndHandler) return DND.DragMotionResult.CONTINUE + + // Don't allow favoriting of transient apps + if (source.app == null || source.app.is_window_backed()) + return DND.DragMotionResult.NO_DROP + + if (!this._settings.is_writable('favorite-apps')) + return DND.DragMotionResult.NO_DROP + + let isVertical = this.dtpPanel.checkIfVertical() + + if (!this._box.contains(source) && !source._dashItemContainer) { + //not an appIcon of the taskbar, probably from the applications view + source._dashItemContainer = new DragPlaceholderItem( + source, + this.iconSize, + isVertical, + ) + this._box.insert_child_above(source._dashItemContainer, null) + } + + let sizeProp = isVertical ? 'height' : 'width' + let posProp = isVertical ? 'y' : 'x' + let pos = isVertical ? y : x + + let currentAppIcons = this._getAppIcons() + let sourceIndex = currentAppIcons.indexOf(source) + let hoveredIndex = Utils.findIndex( + currentAppIcons, + (appIcon) => + pos >= appIcon._dashItemContainer[posProp] && + pos <= + appIcon._dashItemContainer[posProp] + + appIcon._dashItemContainer[sizeProp], + ) + + if (!this._dragInfo) { + this._dragInfo = [sourceIndex, source] + } + + if (hoveredIndex >= 0) { + let isLeft = + pos < + currentAppIcons[hoveredIndex]._dashItemContainer[posProp] + + currentAppIcons[hoveredIndex]._dashItemContainer[sizeProp] * 0.5 + let prevIcon = currentAppIcons[hoveredIndex - 1] + let nextIcon = currentAppIcons[hoveredIndex + 1] + + // Don't allow positioning before or after self and between icons of same app if ungrouped and showing favorites + if ( + !( + hoveredIndex === sourceIndex || + (isLeft && hoveredIndex - 1 == sourceIndex) || + (!this.allowSplitApps && + isLeft && + hoveredIndex - 1 >= 0 && + source.app != prevIcon.app && + prevIcon.app == currentAppIcons[hoveredIndex].app) || + (!isLeft && hoveredIndex + 1 == sourceIndex) || + (!this.allowSplitApps && + !isLeft && + hoveredIndex + 1 < currentAppIcons.length && + source.app != nextIcon.app && + nextIcon.app == currentAppIcons[hoveredIndex].app) + ) + ) { + this._box.set_child_at_index(source._dashItemContainer, hoveredIndex) + + // Ensure the next and previous icon are visible when moving the icon + // (I assume there's room for both of them) + if (hoveredIndex > 1) + Utils.ensureActorVisibleInScrollView( + this._scrollView, + this._box.get_children()[hoveredIndex - 1], + this._scrollView._dtpFadeSize, + ) + if (hoveredIndex < this._box.get_children().length - 1) + Utils.ensureActorVisibleInScrollView( + this._scrollView, + this._box.get_children()[hoveredIndex + 1], + this._scrollView._dtpFadeSize, + ) + } + } + + return this._dragInfo[0] !== sourceIndex + ? DND.DragMotionResult.MOVE_DROP + : DND.DragMotionResult.CONTINUE + } + + // Draggable target interface + acceptDrop(source) { + // Don't allow favoriting of transient apps + if ( + !this._dragInfo || + !source.app || + source.app.is_window_backed() || + !this._settings.is_writable('favorite-apps') + ) { + return false + } + + let appIcons = this._getAppIcons() + let sourceIndex = appIcons.indexOf(source) + let usingLaunchers = !this.isGroupApps && this.usingLaunchers + + // dragging the icon to its original position + if (this._dragInfo[0] === sourceIndex) { + return true + } + + let appFavorites = AppFavorites.getAppFavorites() + let sourceAppId = source.app.get_id() + let appIsFavorite = + this.showFavorites && appFavorites.isFavorite(sourceAppId) + let replacingIndex = + sourceIndex + (sourceIndex > this._dragInfo[0] ? -1 : 1) + let favoriteIndex = + replacingIndex >= 0 + ? appFavorites.getFavorites().indexOf(appIcons[replacingIndex].app) + : 0 + let sameApps = this.allowSplitApps + ? [] + : appIcons.filter((a) => a != source && a.app == source.app) + let favoritesCount = 0 + let position = 0 + let interestingWindows = {} + let getAppWindows = (app) => { + if (!interestingWindows[app]) { + interestingWindows[app] = AppIcons.getInterestingWindows( + app, + this.dtpPanel.monitor, + ) + } + + let appWindows = interestingWindows[app] //prevents "reference to undefined property Symbol.toPrimitive" warning + return appWindows + } + + if ( + sameApps.length && + (!appIcons[sourceIndex - 1] || + appIcons[sourceIndex - 1].app !== source.app) && + (!appIcons[sourceIndex + 1] || + appIcons[sourceIndex + 1].app !== source.app) + ) { + appIcons.splice(appIcons.indexOf(sameApps[0]), sameApps.length) + Array.prototype.splice.apply( + appIcons, + [sourceIndex + 1, 0].concat(sameApps), + ) + } + + for (let i = 0, l = appIcons.length; i < l; ++i) { + let windows = [] + + if (!usingLaunchers || (!source.isLauncher && !appIcons[i].isLauncher)) { + windows = appIcons[i].window + ? [appIcons[i].window] + : getAppWindows(appIcons[i].app) + } + + windows.forEach((w) => (w._dtpPosition = position++)) + + if ( + this.showFavorites && + ((usingLaunchers && appIcons[i].isLauncher) || + (!usingLaunchers && + appFavorites.isFavorite(appIcons[i].app.get_id()))) + ) { + ++favoritesCount + } + } + + if (sourceIndex < favoritesCount) { + if (appIsFavorite) { + appFavorites.moveFavoriteToPos(sourceAppId, favoriteIndex) + } else { + appFavorites.addFavoriteAtPos(sourceAppId, favoriteIndex) + } + } else if ( + appIsFavorite && + this.showFavorites && + (!usingLaunchers || source.isLauncher) + ) { + appFavorites.removeFavorite(sourceAppId) + } + + appFavorites.emit('changed') + + return true + } + + _onShowAppsButtonToggled() { + // Sync the status of the default appButtons. Only if the two statuses are + // different, that means the user interacted with the extension provided + // application button, cutomize the behaviour. Otherwise the shell has changed the + // status (due to the _syncShowAppsButtonToggled function below) and it + // has already performed the desired action. + let selector = SearchController + + if ( + selector._showAppsButton && + selector._showAppsButton.checked !== this.showAppsButton.checked + ) { + // find visible view + + if (this.showAppsButton.checked) { + //override escape key to return to the desktop when entering the overview using the showapps button + SearchController._onStageKeyPress = function (actor, event) { + if ( + Main.modalCount == 1 && + event.get_key_symbol() === Clutter.KEY_Escape + ) { + this._searchActive ? this.reset() : Main.overview.hide() + + return Clutter.EVENT_STOP + } + + return Object.getPrototypeOf(this)._onStageKeyPress.call( + this, + actor, + event, + ) + } + + let overviewHiddenId = Main.overview.connect('hidden', () => { + Main.overview.disconnect(overviewHiddenId) + delete SearchController._onStageKeyPress + }) + + // force exiting overview if needed + if (!Main.overview._shown) { + this.forcedOverview = true + } + + //temporarily use as primary the monitor on which the showapps btn was clicked, this is + //restored by the panel when exiting the overview + this.dtpPanel.panelManager.setFocusedMonitor(this.dtpPanel.monitor) + + // Finally show the overview + selector._showAppsButton.checked = true + Main.overview.show(2 /*APP_GRID*/) + } else { + if (this.forcedOverview) { + // force exiting overview if needed + Main.overview.hide() + } else { + selector._showAppsButton.checked = false + } + + this.forcedOverview = false + } + } + } + + _syncShowAppsButtonToggled() { + let status = SearchController._showAppsButton.checked + if (this.showAppsButton.checked !== status) + this.showAppsButton.checked = status + } + + showShowAppsButton() { + this.showAppsButton.visible = true + this.showAppsButton.set_width(-1) + this.showAppsButton.set_height(-1) + } + + popupFocusedAppSecondaryMenu() { + let appIcons = this._getAppIcons() + + for (let i in appIcons) { + if (appIcons[i].app == tracker.focus_app) { + let appIcon = appIcons[i] + if (appIcon._menu && appIcon._menu.isOpen) appIcon._menu.close() + else appIcon.popupMenu() + + appIcon.sync_hover() + break + } + } + } +} + +export const TaskbarItemContainer = GObject.registerClass( + {}, + class TaskbarItemContainer extends Dash.DashItemContainer { + _init() { + super._init() + this.x_expand = this.y_expand = false + } + + vfunc_allocate(box) { + if (this.child == null) return + + this.set_allocation(box) + + let availWidth = box.x2 - box.x1 + let availHeight = box.y2 - box.y1 + let [, , natChildWidth, natChildHeight] = this.child.get_preferred_size() + let [childScaleX, childScaleY] = this.child.get_scale() + + let childWidth = Math.min(natChildWidth * childScaleX, availWidth) + let childHeight = Math.min(natChildHeight * childScaleY, availHeight) + let childBox = new Clutter.ActorBox() + + childBox.x1 = (availWidth - childWidth) / 2 + childBox.y1 = (availHeight - childHeight) / 2 + childBox.x2 = childBox.x1 + childWidth + childBox.y2 = childBox.y1 + childHeight + + this.child.allocate(childBox) + } + + // In case appIcon is removed from the taskbar while it is hovered, + // restore opacity before dashItemContainer.animateOutAndDestroy does the destroy animation. + animateOutAndDestroy() { + if (this._raisedClone) { + this._raisedClone.source.opacity = 255 + this._raisedClone.destroy() + } + + super.animateOutAndDestroy() + } + }, +) + +const DragPlaceholderItem = GObject.registerClass( + {}, + class DragPlaceholderItem extends St.Widget { + _init(appIcon, iconSize, isVertical) { + super._init({ + style: AppIcons.getIconContainerStyle(isVertical), + layout_manager: new Clutter.BinLayout(), + }) + + this.child = { _delegate: appIcon } + + this._clone = new Clutter.Clone({ + source: appIcon.icon._iconBin, + width: iconSize, + height: iconSize, + }) + + this.add_child(this._clone) + } + + destroy() { + this._clone.destroy() + super.destroy() + } + }, +) + +export function getAppStableSequence(app, monitor) { + let windows = AppIcons.getInterestingWindows(app, monitor) + + return windows.reduce((prevWindow, window) => { + return Math.min(prevWindow, getWindowStableSequence(window)) + }, Infinity) +} + +export function sortWindowsCompareFunction(windowA, windowB) { + return getWindowStableSequence(windowA) - getWindowStableSequence(windowB) +} + +export function getWindowStableSequence(window) { + return '_dtpPosition' in window + ? window._dtpPosition + : window.get_stable_sequence() +} diff --git a/src/transparency.js b/src/transparency.js new file mode 100644 index 0000000..a7a6361 --- /dev/null +++ b/src/transparency.js @@ -0,0 +1,199 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extension + */ + +import GdkPixbuf from 'gi://GdkPixbuf' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import St from 'gi://St' + +import * as Proximity from './proximity.js' +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' + +const TRANS_DYNAMIC_DISTANCE = 20 + +export const DynamicTransparency = class { + constructor(dtpPanel) { + this._dtpPanel = dtpPanel + this._proximityManager = dtpPanel.panelManager.proximityManager + this._proximityWatchId = 0 + this.currentBackgroundColor = 0 + + this._initialPanelStyle = dtpPanel.panel.get_style() + + this._signalsHandler = new Utils.GlobalSignalsHandler() + this._bindSignals() + + this._updateAllAndSet() + this._updateProximityWatch() + } + + destroy() { + this._signalsHandler.destroy() + this._proximityManager.removeWatch(this._proximityWatchId) + + this._dtpPanel.panel.set_style(this._initialPanelStyle) + } + + updateExternalStyle() { + this._setBackground() + } + + _bindSignals() { + this._signalsHandler.add( + [Utils.getStageTheme(), 'changed', () => this._updateAllAndSet()], + [Main.overview, ['showing', 'hiding'], () => this._updateAlphaAndSet()], + [ + SETTINGS, + [ + 'changed::trans-use-custom-opacity', + 'changed::trans-panel-opacity', + 'changed::trans-dynamic-anim-target', + 'changed::trans-use-dynamic-opacity', + ], + () => this._updateAlphaAndSet(), + ], + [ + SETTINGS, + [ + 'changed::trans-dynamic-behavior', + 'changed::trans-use-dynamic-opacity', + ], + () => this._updateProximityWatch(), + ], + ) + } + + _updateProximityWatch() { + this._proximityManager.removeWatch(this._proximityWatchId) + + if (SETTINGS.get_boolean('trans-use-dynamic-opacity')) { + let isVertical = this._dtpPanel.checkIfVertical() + let threshold = TRANS_DYNAMIC_DISTANCE + + this._windowOverlap = false + this._updateAlphaAndSet() + + this._proximityWatchId = this._proximityManager.createWatch( + this._dtpPanel.panelBox.get_parent(), + this._dtpPanel.monitor.index, + Proximity.Mode[SETTINGS.get_string('trans-dynamic-behavior')], + isVertical ? threshold : 0, + isVertical ? 0 : threshold, + (overlap) => { + this._windowOverlap = overlap + this._updateAlphaAndSet() + }, + ) + } + } + + _updateAllAndSet() { + let themeBackground = this._getThemeBackground(true) + + this._updateColor(themeBackground) + this._updateAlpha(themeBackground) + this._setBackground() + this._setActorStyle() + } + + _updateAlphaAndSet() { + this._updateAlpha() + this._setBackground() + } + + _updateColor(themeBackground) { + this.backgroundColorRgb = themeBackground || this._getThemeBackground() + } + + _updateAlpha(themeBackground) { + if ( + this._windowOverlap && + !Main.overview.visibleTarget && + SETTINGS.get_boolean('trans-use-dynamic-opacity') + ) { + this.alpha = SETTINGS.get_double('trans-dynamic-anim-target') + } else { + this.alpha = SETTINGS.get_boolean('trans-use-custom-opacity') + ? SETTINGS.get_double('trans-panel-opacity') + : (themeBackground || this._getThemeBackground()).alpha * 0.003921569 // 1 / 255 = 0.003921569 + } + } + + _setBackground() { + this.currentBackgroundColor = Utils.getrgbaColor( + this.backgroundColorRgb, + this.alpha, + ) + + let transition = 'transition-duration: 300ms;' + + this._dtpPanel.set_style( + 'background-color: ' + this.currentBackgroundColor + transition, + ) + } + + _setActorStyle() { + this._dtpPanel.panel.set_style( + 'background: none; ' + + 'border-image: none; ' + + 'background-image: none; ' + + 'transition-duration: 300ms;' + ) + } + + _getThemeBackground(reload) { + if (reload || !this._themeBackground) { + let fakePanel = new St.Bin({ name: 'panel' }) + Main.uiGroup.add_child(fakePanel) + let fakeTheme = fakePanel.get_theme_node() + this._themeBackground = + this._getBackgroundImageColor(fakeTheme) || + fakeTheme.get_background_color() + Main.uiGroup.remove_child(fakePanel) + } + + return this._themeBackground + } + + _getBackgroundImageColor(theme) { + let bg = null + + try { + let imageFile = + theme.get_background_image() || theme.get_border_image()?.get_file() + + if (imageFile) { + let imageBuf = GdkPixbuf.Pixbuf.new_from_file(imageFile.get_path()) + let pixels = imageBuf.get_pixels() + + bg = { + red: pixels[0], + green: pixels[1], + blue: pixels[2], + alpha: pixels[3], + } + } + } catch (error) { + console.log(error) + } + + return bg + } +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..d9494a9 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,969 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * Credits: + * This file is based on code from the Dash to Dock extension by micheleg + * and code from the Dash to Panel extension + * Some code was also adapted from the upstream Gnome Shell source code. + */ + +import Clutter from 'gi://Clutter' +import Cogl from 'gi://Cogl' +import GdkPixbuf from 'gi://GdkPixbuf' +import Gio from 'gi://Gio' +import GLib from 'gi://GLib' +import Graphene from 'gi://Graphene' +import Meta from 'gi://Meta' +import Shell from 'gi://Shell' +import St from 'gi://St' +import * as Config from 'resource:///org/gnome/shell/misc/config.js' +import * as Util from 'resource:///org/gnome/shell/misc/util.js' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js' + +const SCROLL_TIME = Util.SCROLL_TIME / (Util.SCROLL_TIME > 1 ? 1000 : 1) + +// simplify global signals and function injections handling +// abstract class +export const BasicHandler = class { + constructor() { + this._storage = new Object() + } + + add(/*unlimited 3-long array arguments*/) { + // convert arguments object to array, concatenate with generic + let args = [].concat('generic', [].slice.call(arguments)) + // call addWithLabel with ags as if they were passed arguments + this.addWithLabel.apply(this, args) + } + + destroy() { + for (let label in this._storage) this.removeWithLabel(label) + } + + addWithLabel(label /* plus unlimited 3-long array arguments*/) { + if (this._storage[label] == undefined) this._storage[label] = new Array() + + // skip first element of the arguments + for (let i = 1; i < arguments.length; i++) { + let item = this._storage[label] + let handlers = this._create(arguments[i]) + + for (let j = 0, l = handlers.length; j < l; ++j) { + item.push(handlers[j]) + } + } + } + + removeWithLabel(label) { + if (this._storage[label]) { + for (let i = 0; i < this._storage[label].length; i++) { + this._remove(this._storage[label][i]) + } + + delete this._storage[label] + } + } + + hasLabel(label) { + return !!this._storage[label] + } + + /* Virtual methods to be implemented by subclass */ + // create single element to be stored in the storage structure + _create() { + throw new Error('no implementation of _create in ' + this) + } + + // correctly delete single element + _remove() { + throw new Error('no implementation of _remove in ' + this) + } +} + +// Manage global signals +export const GlobalSignalsHandler = class extends BasicHandler { + _create(item) { + let handlers = [] + + item[1] = [].concat(item[1]) + + for (let i = 0, l = item[1].length; i < l; ++i) { + let object = item[0] + let event = item[1][i] + let callback = item[2] + try { + let id = object.connect(event, callback) + + handlers.push([object, id]) + } catch (e) { + console.log(e) + } + } + + return handlers + } + + _remove(item) { + item[0].disconnect(item[1]) + } +} + +/** + * Manage function injection: both instances and prototype can be overridden + * and restored + */ +export const InjectionsHandler = class extends BasicHandler { + _create(item) { + let object = item[0] + let name = item[1] + let injectedFunction = item[2] + let original = object[name] + + object[name] = injectedFunction + return [[object, name, injectedFunction, original]] + } + + _remove(item) { + let object = item[0] + let name = item[1] + let original = item[3] + object[name] = original + } +} + +/** + * Manage timeouts: the added timeouts have their id reset on completion + */ +export const TimeoutsHandler = class extends BasicHandler { + _create(item) { + let name = item[0] + let delay = item[1] + let timeoutHandler = item[2] + + this._remove(item) + + this[name] = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { + this[name] = 0 + timeoutHandler() + + return GLib.SOURCE_REMOVE + }) + + return [[name]] + } + + remove(name) { + this._remove([name]) + } + + _remove(item) { + let name = item[0] + + if (this[name]) { + GLib.Source.remove(this[name]) + this[name] = 0 + } + } + + getId(name) { + return this[name] ? this[name] : 0 + } +} + +export function createBoxLayout(options) { + if (options && 'vertical' in options) { + let vertical = options.vertical + + delete options.vertical + setBoxLayoutVertical(options, vertical) + } + + return new St.BoxLayout(options) +} + +export function setBoxLayoutVertical(box, vertical) { + if (Config.PACKAGE_VERSION >= '48') + // https://mutter.gnome.org/clutter/enum.Orientation.html + box.orientation = vertical ? 1 : 0 + else box.vertical = vertical +} + +export function getBoxLayoutVertical(box) { + return Config.PACKAGE_VERSION >= '48' ? box.orientation == 1 : box.vertical +} + +// This is wrapper to maintain compatibility with GNOME-Shell 3.30+ as well as +// previous versions. +export const DisplayWrapper = { + getScreen() { + return global.screen || global.display + }, + + getWorkspaceManager() { + return global.screen || global.workspace_manager + }, + + getMonitorManager() { + return global.screen || global.backend.get_monitor_manager() + }, +} + +let unredirectEnabled = true +export const setDisplayUnredirect = (enable) => { + let v48 = Config.PACKAGE_VERSION >= '48' + + if (enable && !unredirectEnabled) + v48 + ? global.compositor.enable_unredirect() + : Meta.enable_unredirect_for_display(global.display) + else if (!enable && unredirectEnabled) + v48 + ? global.compositor.disable_unredirect() + : Meta.disable_unredirect_for_display(global.display) + + unredirectEnabled = enable +} + +export const getSystemMenuInfo = function () { + return { + name: 'quickSettings', + constructor: Main.panel.statusArea.quickSettings.constructor, + } +} + +export function getOverviewWorkspaces() { + let workspaces = [] + + Main.overview._overview._controls._workspacesDisplay._workspacesViews.forEach( + (wv) => + (workspaces = [ + ...workspaces, + ...(wv._workspaces || []), // WorkspacesDisplay --> WorkspacesView (primary monitor) + ...(wv._workspacesView?._workspaces || []), // WorkspacesDisplay --> SecondaryMonitorDisplay --> WorkspacesView + ...(wv._workspacesView?._workspace // WorkspacesDisplay --> SecondaryMonitorDisplay --> ExtraWorkspaceView + ? [wv._workspacesView?._workspace] + : []), + ]), + ) + + return workspaces +} + +export const getCurrentWorkspace = function () { + return DisplayWrapper.getWorkspaceManager().get_active_workspace() +} + +export const getWorkspaceByIndex = function (index) { + return DisplayWrapper.getWorkspaceManager().get_workspace_by_index(index) +} + +export const getWorkspaceCount = function () { + return DisplayWrapper.getWorkspaceManager().n_workspaces +} + +export const getStageTheme = function () { + return St.ThemeContext.get_for_stage(global.stage) +} + +export const getScaleFactor = function () { + return getStageTheme().scale_factor || 1 +} + +export const findIndex = function (array, predicate) { + if (array) { + if (Array.prototype.findIndex) { + return array.findIndex(predicate) + } + + for (let i = 0, l = array.length; i < l; ++i) { + if (predicate(array[i])) { + return i + } + } + } + + return -1 +} + +export const find = function (array, predicate) { + let index = findIndex(array, predicate) + + if (index > -1) { + return array[index] + } +} + +export const mergeObjects = function (main, bck) { + for (const prop in bck) { + if (!Object.hasOwn(main, prop) && Object.hasOwn(bck, prop)) { + main[prop] = bck[prop] + } + } + + return main +} + +export const getTrackedActorData = (actor) => { + let trackedIndex = Main.layoutManager._findActor(actor) + + if (trackedIndex >= 0) return Main.layoutManager._trackedActors[trackedIndex] +} + +export const getTransformedAllocation = function (actor) { + let extents = actor.get_transformed_extents() + let topLeft = extents.get_top_left() + let bottomRight = extents.get_bottom_right() + + return { x1: topLeft.x, x2: bottomRight.x, y1: topLeft.y, y2: bottomRight.y } +} + +export const setClip = function (actor, x, y, width, height, offsetX, offsetY) { + actor.set_clip(offsetX || 0, offsetY || 0, width, height) + actor.set_position(x, y) + actor.set_size(width, height) +} + +export const addKeybinding = function (key, settings, handler, modes) { + if (!Main.wm._allowedKeybindings[key]) { + Main.wm.addKeybinding( + key, + settings, + Meta.KeyBindingFlags.NONE, + modes || Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, + handler, + ) + } +} + +export const removeKeybinding = function (key) { + if (Main.wm._allowedKeybindings[key]) { + Main.wm.removeKeybinding(key) + } +} + +export const getrgbColor = function (color) { + color = + typeof color === 'string' ? ColorUtils.color_from_string(color)[1] : color + + return { red: color.red, green: color.green, blue: color.blue } +} + +export const getrgbaColor = function (color, alpha, offset) { + if (alpha <= 0) { + return 'transparent; ' + } + + let rgb = getrgbColor(color) + + if (offset) { + ;['red', 'green', 'blue'].forEach((k) => { + rgb[k] = Math.min(255, Math.max(0, rgb[k] + offset)) + + if (rgb[k] == color[k]) { + rgb[k] = Math.min(255, Math.max(0, rgb[k] - offset)) + } + }) + } + + return ( + 'rgba(' + + rgb.red + + ',' + + rgb.green + + ',' + + rgb.blue + + ',' + + Math.floor(alpha * 100) * 0.01 + + '); ' + ) +} + +export const checkIfColorIsBright = function (color) { + let rgb = getrgbColor(color) + let brightness = 0.2126 * rgb.red + 0.7152 * rgb.green + 0.0722 * rgb.blue + + return brightness > 128 +} + +export const getMouseScrollDirection = function (event) { + let direction + + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + case Clutter.ScrollDirection.LEFT: + direction = 'up' + break + case Clutter.ScrollDirection.DOWN: + case Clutter.ScrollDirection.RIGHT: + direction = 'down' + break + } + + return direction +} + +export function getAllMetaWindows() { + return global.get_window_actors().map((w) => w.meta_window) +} + +export const checkIfWindowHasTransient = function (window) { + let hasTransient + + window.foreach_transient(() => (hasTransient = true)) + + return hasTransient +} + +export const activateSiblingWindow = function ( + windows, + direction, + startWindow, +) { + let windowIndex = windows.indexOf(global.display.focus_window) + let nextWindowIndex = + windowIndex < 0 + ? startWindow + ? windows.indexOf(startWindow) + : 0 + : windowIndex + (direction == 'up' ? -1 : 1) + + if (nextWindowIndex == windows.length) { + nextWindowIndex = 0 + } else if (nextWindowIndex < 0) { + nextWindowIndex = windows.length - 1 + } + + if (windowIndex != nextWindowIndex) { + Main.activateWindow(windows[nextWindowIndex]) + } +} + +export const animateWindowOpacity = function (window, tweenOpts) { + //there currently is a mutter bug with the windowactor opacity, starting with 3.34 + //https://gitlab.gnome.org/GNOME/mutter/issues/836 + + //since 3.36, a workaround is to use the windowactor's child for the fade animation + //this leaves a "shadow" on the desktop, so the windowactor needs to be hidden + //when the animation is complete + let visible = tweenOpts.opacity > 0 + let windowActor = window + let initialOpacity = window.opacity + + window = windowActor.get_first_child() || windowActor + + if (!windowActor.visible && visible) { + window.opacity = 0 + windowActor.visible = visible + tweenOpts.opacity = Math.min(initialOpacity, tweenOpts.opacity) + } + + if (!visible) { + tweenOpts.onComplete = () => { + windowActor.visible = visible + window.opacity = initialOpacity + } + } + + animate(window, tweenOpts) +} + +export const animate = function (actor, options) { + //the original animations used Tweener instead of Clutter animations, so we + //use "time" and "delay" properties defined in seconds, as opposed to Clutter + //animations "duration" and "delay" which are defined in milliseconds + if (options.delay) { + options.delay = options.delay * 1000 + } + + options.duration = options.time * 1000 + delete options.time + + if (options.transition) { + //map Tweener easing equations to Clutter animation modes + options.mode = + { + easeInCubic: Clutter.AnimationMode.EASE_IN_CUBIC, + easeInOutCubic: Clutter.AnimationMode.EASE_IN_OUT_CUBIC, + easeInOutQuad: Clutter.AnimationMode.EASE_IN_OUT_QUAD, + easeOutQuad: Clutter.AnimationMode.EASE_OUT_QUAD, + }[options.transition] || Clutter.AnimationMode.LINEAR + + delete options.transition + } + + let params = [options] + + if ('value' in options && actor instanceof St.Adjustment) { + params.unshift(options.value) + delete options.value + } + + actor.ease.apply(actor, params) +} + +export const stopAnimations = function (actor) { + actor.remove_all_transitions() +} + +export const getIndicators = function (delegate) { + if (delegate instanceof St.BoxLayout) { + return delegate + } + + return delegate.indicators +} + +export const getPoint = function (coords) { + return new Graphene.Point(coords) +} + +export const notify = function ( + title, + body, + sourceIconName, + notificationIcon, + action, + isTransient, +) { + let source = MessageTray.getSystemSource() + let notification = new MessageTray.Notification({ + source, + title, + body, + isTransient: isTransient || false, + gicon: notificationIcon || null, + }) + + if (sourceIconName) source.iconName = sourceIconName + + if (action) { + if (!(action instanceof Array)) { + action = [action] + } + + action.forEach((a) => notification.addAction(a.text, a.func)) + } + + source.addNotification(notification) +} + +/* + * This is a copy of the same function in utils.js, but also adjust horizontal scrolling + * and perform few further cheks on the current value to avoid changing the values when + * it would be clamp to the current one in any case. + * Return the amount of shift applied + */ +export const ensureActorVisibleInScrollView = function ( + scrollView, + actor, + fadeSize, + onComplete, +) { + const vadjustment = scrollView.vadjustment + const hadjustment = scrollView.hadjustment + let [vvalue, , vupper, , , vpageSize] = vadjustment.get_values() + let [hvalue, , hupper, , , hpageSize] = hadjustment.get_values() + + let [hvalue0, vvalue0] = [hvalue, vvalue] + + let voffset = fadeSize + let hoffset = fadeSize + + let box = actor.get_allocation_box() + let y1 = box.y1, + y2 = box.y2, + x1 = box.x1, + x2 = box.x2 + + let parent = actor.get_parent() + while (parent != scrollView) { + if (!parent) throw new Error('actor not in scroll view') + + let box = parent.get_allocation_box() + y1 += box.y1 + y2 += box.y1 + x1 += box.x1 + x2 += box.x1 + parent = parent.get_parent() + } + + if (y1 < vvalue + voffset) vvalue = Math.max(0, y1 - voffset) + else if (vvalue < vupper - vpageSize && y2 > vvalue + vpageSize - voffset) + vvalue = Math.min(vupper - vpageSize, y2 + voffset - vpageSize) + + if (x1 < hvalue + hoffset) hvalue = Math.max(0, x1 - hoffset) + else if (hvalue < hupper - hpageSize && x2 > hvalue + hpageSize - hoffset) + hvalue = Math.min(hupper - hpageSize, x2 + hoffset - hpageSize) + + let tweenOpts = { + time: SCROLL_TIME, + onComplete: onComplete || (() => {}), + transition: 'easeOutQuad', + } + + if (vvalue !== vvalue0) { + animate(vadjustment, mergeObjects(tweenOpts, { value: vvalue })) + } + + if (hvalue !== hvalue0) { + animate(hadjustment, mergeObjects(tweenOpts, { value: hvalue })) + } + + return [hvalue - hvalue0, vvalue - vvalue0] +} + +/** + * ColorUtils is adapted from https://github.com/micheleg/dash-to-dock + */ +let colorNs = Clutter.Color ? Clutter : Cogl + +export const ColorUtils = { + color_from_string: colorNs.color_from_string, + Color: colorNs.Color, + + colorLuminance(r, g, b, dlum) { + // Darken or brighten color by a fraction dlum + // Each rgb value is modified by the same fraction. + // Return "#rrggbb" strin + + let rgbString = '#' + + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), + 2, + ) + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), + 2, + ) + rgbString += ColorUtils._decimalToHex( + Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), + 2, + ) + + return rgbString + }, + + _decimalToHex(d, padding) { + // Convert decimal to an hexadecimal string adding the desired padding + + let hex = d.toString(16) + while (hex.length < padding) hex = '0' + hex + return hex + }, + + HSVtoRGB(h, s, v) { + // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]). + // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV + // here with h = [0,1] instead of [0, 360] + // Accept either (h,s,v) independently or {h:h, s:s, v:v} object. + // Return {r:r, g:g, b:b} object. + + if (arguments.length === 1) { + s = h.s + v = h.v + h = h.h + } + + let r, g, b + let c = v * s + let h1 = h * 6 + let x = c * (1 - Math.abs((h1 % 2) - 1)) + let m = v - c + + if (h1 <= 1) (r = c + m), (g = x + m), (b = m) + else if (h1 <= 2) (r = x + m), (g = c + m), (b = m) + else if (h1 <= 3) (r = m), (g = c + m), (b = x + m) + else if (h1 <= 4) (r = m), (g = x + m), (b = c + m) + else if (h1 <= 5) (r = x + m), (g = m), (b = c + m) + else (r = c + m), (g = m), (b = x + m) + + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255), + } + }, + + RGBtoHSV(r, g, b) { + // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]). + // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV + // here with h = [0,1] instead of [0, 360] + // Accept either (r,g,b) independently or {r:r, g:g, b:b} object. + // Return {h:h, s:s, v:v} object. + + if (arguments.length === 1) { + r = r.r + g = r.g + b = r.b + } + + let h, s, v + + let M = Math.max(r, g, b) + let m = Math.min(r, g, b) + let c = M - m + + if (c == 0) h = 0 + else if (M == r) h = ((g - b) / c) % 6 + else if (M == g) h = (b - r) / c + 2 + else h = (r - g) / c + 4 + + h = h / 6 + v = M / 255 + if (M !== 0) s = c / M + else s = 0 + + return { h: h, s: s, v: v } + }, +} + +/** + * DominantColorExtractor is adapted from https://github.com/micheleg/dash-to-dock + */ +let themeLoader = null +let iconCacheMap = new Map() +const MAX_CACHED_ITEMS = 1000 +const BATCH_SIZE_TO_DELETE = 50 +const DOMINANT_COLOR_ICON_SIZE = 64 + +export const DominantColorExtractor = class { + constructor(app) { + this._app = app + } + + /** + * Try to get the pixel buffer for the current icon, if not fail gracefully + */ + _getIconPixBuf() { + let iconTexture = this._app.create_icon_texture(16) + + if (themeLoader === null) { + themeLoader = new St.IconTheme() + } + + // Unable to load the icon texture, use fallback + if (iconTexture instanceof St.Icon === false) { + return null + } + + iconTexture = iconTexture.get_gicon() + + // Unable to load the icon texture, use fallback + if (iconTexture === null) { + return null + } + + if (iconTexture instanceof Gio.FileIcon) { + // Use GdkPixBuf to load the pixel buffer from the provided file path + return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path()) + } + + // Get the pixel buffer from the icon theme + if (iconTexture instanceof Gio.ThemedIcon) { + let icon_info = themeLoader.lookup_icon( + iconTexture.get_names()[0], + DOMINANT_COLOR_ICON_SIZE, + 0, + ) + + if (icon_info !== null) { + return icon_info.load_icon() + } + } + + return null + } + + /** + * The backlight color choosing algorithm was mostly ported to javascript from the + * Unity7 C++ source of Canonicals: + * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp + * so it more or less works the same way. + */ + _getColorPalette() { + if (iconCacheMap.get(this._app.get_id())) { + // We already know the answer + return iconCacheMap.get(this._app.get_id()) + } + + let pixBuf = this._getIconPixBuf() + if (pixBuf == null) return null + + let pixels = pixBuf.get_pixels() + + let total = 0, + rTotal = 0, + gTotal = 0, + bTotal = 0 + + let resample_y = 1, + resample_x = 1 + + // Resampling of large icons + // We resample icons larger than twice the desired size, as the resampling + // to a size s + // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE, + // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is tipycally + // a multiple of it. + let width = pixBuf.get_width() + let height = pixBuf.get_height() + + // Resample + if (height >= 2 * DOMINANT_COLOR_ICON_SIZE) + resample_y = Math.floor(height / DOMINANT_COLOR_ICON_SIZE) + + if (width >= 2 * DOMINANT_COLOR_ICON_SIZE) + resample_x = Math.floor(width / DOMINANT_COLOR_ICON_SIZE) + + if (resample_x !== 1 || resample_y !== 1) + pixels = this._resamplePixels(pixels, resample_x, resample_y) + + // computing the limit outside the for (where it would be repeated at each iteration) + // for performance reasons + let limit = pixels.length + for (let offset = 0; offset < limit; offset += 4) { + let r = pixels[offset], + g = pixels[offset + 1], + b = pixels[offset + 2], + a = pixels[offset + 3] + + let saturation = Math.max(r, g, b) - Math.min(r, g, b) + let relevance = 0.1 * 255 * 255 + 0.9 * a * saturation + + rTotal += r * relevance + gTotal += g * relevance + bTotal += b * relevance + + total += relevance + } + + total = total * 255 + + let r = rTotal / total, + g = gTotal / total, + b = bTotal / total + + let hsv = ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255) + + if (hsv.s > 0.15) hsv.s = 0.65 + hsv.v = 0.9 + + let rgb = ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v) + + // Cache the result. + let backgroundColor = { + lighter: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0.2), + original: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, 0), + darker: ColorUtils.colorLuminance(rgb.r, rgb.g, rgb.b, -0.5), + } + + if (iconCacheMap.size >= MAX_CACHED_ITEMS) { + //delete oldest cached values (which are in order of insertions) + let ctr = 0 + for (let key of iconCacheMap.keys()) { + if (++ctr > BATCH_SIZE_TO_DELETE) break + iconCacheMap.delete(key) + } + } + + iconCacheMap.set(this._app.get_id(), backgroundColor) + + return backgroundColor + } + + /** + * Downsample large icons before scanning for the backlight color to + * improve performance. + * + * @param pixBuf + * @param pixels + * @param resampleX + * @param resampleY + * + * @return []; + */ + _resamplePixels(pixels, resampleX, resampleY) { + let resampledPixels = [] + // computing the limit outside the for (where it would be repeated at each iteration) + // for performance reasons + let limit = pixels.length / (resampleX * resampleY) / 4 + for (let i = 0; i < limit; i++) { + let pixel = i * resampleX * resampleY + + resampledPixels.push(pixels[pixel * 4]) + resampledPixels.push(pixels[pixel * 4 + 1]) + resampledPixels.push(pixels[pixel * 4 + 2]) + resampledPixels.push(pixels[pixel * 4 + 3]) + } + + return resampledPixels + } +} + +export const drawRoundedLine = function ( + cr, + x, + y, + width, + height, + isRoundLeft, + isRoundRight, + stroke, + fill, +) { + if (height > width) { + y += Math.floor((height - width) / 2.0) + height = width + } + + height = 2.0 * Math.floor(height / 2.0) + + const leftRadius = isRoundLeft ? height / 2.0 : 0.0 + const rightRadius = isRoundRight ? height / 2.0 : 0.0 + + cr.moveTo(x + width - rightRadius, y) + cr.lineTo(x + leftRadius, y) + if (isRoundLeft) + cr.arcNegative( + x + leftRadius, + y + leftRadius, + leftRadius, + -Math.PI / 2, + Math.PI / 2, + ) + else cr.lineTo(x, y + height) + cr.lineTo(x + width - rightRadius, y + height) + if (isRoundRight) + cr.arcNegative( + x + width - rightRadius, + y + rightRadius, + rightRadius, + Math.PI / 2, + -Math.PI / 2, + ) + else cr.lineTo(x + width, y) + cr.closePath() + + if (fill != null) { + cr.setSource(fill) + cr.fillPreserve() + } + if (stroke != null) cr.setSource(stroke) + cr.stroke() +} diff --git a/src/windowPreview.js b/src/windowPreview.js new file mode 100644 index 0000000..170c267 --- /dev/null +++ b/src/windowPreview.js @@ -0,0 +1,1362 @@ +/* + * This file is part of the Zorin Taskbar extension for Zorin OS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Credits: + * This file is based on code from the Dash to Panel extensionq + */ + +import GObject from 'gi://GObject' +import Clutter from 'gi://Clutter' +import GLib from 'gi://GLib' +import Graphene from 'gi://Graphene' +import * as Main from 'resource:///org/gnome/shell/ui/main.js' +import Meta from 'gi://Meta' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js' +import St from 'gi://St' + +import * as Taskbar from './taskbar.js' +import * as Utils from './utils.js' +import { SETTINGS } from './extension.js' +import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js' + +//timeout intervals +const ENSURE_VISIBLE_MS = 200 + +//timeout names +const T1 = 'openMenuTimeout' +const T2 = 'closeMenuTimeout' +const T3 = 'peekTimeout' +const T4 = 'ensureVisibleTimeout' + +const MAX_TRANSLATION = 40 +const HEADER_HEIGHT = 38 +const MAX_CLOSE_BUTTON_SIZE = 30 +const MIN_DIMENSION = 100 +const FOCUSED_COLOR_OFFSET = 24 +const FADE_SIZE = 36 +const PEEK_INDEX_PROP = '_dtpPeekInitialIndex' +const MARGIN_SIZE = 8 + +const SHOW_WINDOW_PREVIEWS_TIMEOUT = 400 +const LEAVE_TIMEOUT = 250 +const WINDOW_PREVIEW_ANIMATION_TIME = 200 +const WINDOW_PREVIEW_ASPECT_RATIO_X = 16 +const WINDOW_PREVIEW_FIXED_X = false +const WINDOW_PREVIEW_ASPECT_RATIO_Y = 9 +const WINDOW_PREVIEW_FIXED_Y = true +const WINDOW_PREVIEW_PADDING = 8 +const ENTER_PEEK_MODE_TIMEOUT = 500 +const PEEK_MODE_OPACITY = 40 + +let headerHeight = 0 +let alphaBg = 0 +let isLeftButtons = false +let isTopHeader = true +let scaleFactor = 1 +let animationTime = 0 +let aspectRatio = {} + +export const PreviewMenu = GObject.registerClass( + { + Signals: { 'open-state-changed': {} }, + }, + class PreviewMenu extends St.Widget { + _init(panel) { + super._init({ layout_manager: new Clutter.BinLayout() }) + + let geom = panel.geom + this.panel = panel + this.currentAppIcon = null + this._focusedPreview = null + this._peekedWindow = null + this.allowCloseWindow = true + this.peekInitialWorkspaceIndex = -1 + this.opened = false + this.isVertical = + geom.position == St.Side.LEFT || geom.position == St.Side.RIGHT + this._translationProp = 'translation_' + (this.isVertical ? 'x' : 'y') + this._translationDirection = + geom.position == St.Side.TOP || geom.position == St.Side.LEFT ? -1 : 1 + this._translationOffset = + Math.min(panel.geom.innerSize, MAX_TRANSLATION) * + this._translationDirection + + this.menu = new St.Widget({ + name: 'preview-menu', + style_class: 'dash-label', + layout_manager: new Clutter.BinLayout(), + reactive: true, + track_hover: true, + x_expand: true, + y_expand: true, + x_align: + Clutter.ActorAlign[geom.position != St.Side.RIGHT ? 'START' : 'END'], + y_align: + Clutter.ActorAlign[geom.position != St.Side.BOTTOM ? 'START' : 'END'], + }) + this._box = Utils.createBoxLayout({ vertical: this.isVertical }) + this._scrollView = new St.ScrollView({ + name: 'zorintaskbarPreviewScrollview', + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.NEVER, + enable_mouse_scrolling: true, + y_expand: !this.isVertical, + }) + + this._scrollView.add_child(this._box) + this.menu.add_child(this._scrollView) + this.add_child(this.menu) + } + + enable() { + this._timeoutsHandler = new Utils.TimeoutsHandler() + this._signalsHandler = new Utils.GlobalSignalsHandler() + + Main.layoutManager.addChrome(this, { affectsInputRegion: false }) + Main.layoutManager.trackChrome(this.menu, { affectsInputRegion: true }) + + this._resetHiddenState() + this._refreshGlobals() + this._updateClip() + this.menu.set_position(1, 1) + + this._signalsHandler.add( + [this.menu, 'notify::hover', () => this._onHoverChanged()], + [this._scrollView, 'scroll-event', this._onScrollEvent.bind(this)], + [this.panel.panelBox, 'style-changed', () => this._updateClip()], + [ + Utils.DisplayWrapper.getScreen(), + 'in-fullscreen-changed', + () => { + if ( + global.display.focus_window && + global.display.focus_window.is_fullscreen() + ) { + this.close(true) + } + }, + ], + [ + SETTINGS, + [ + 'changed::panel-sizes', + 'changed::panel-margin', + 'changed::window-preview-size' + ], + () => { + this._refreshGlobals() + this._updateClip() + }, + ], + ) + } + + disable() { + this._timeoutsHandler.destroy() + this._signalsHandler.destroy() + + this.close(true) + + Main.layoutManager.untrackChrome(this.menu) + Main.layoutManager.removeChrome(this) + } + + requestOpen(appIcon) { + let timeout = SHOW_WINDOW_PREVIEWS_TIMEOUT + + if (this.opened) { + timeout = Math.min(100, timeout) + } + + this._endOpenCloseTimeouts() + this._timeoutsHandler.add([T1, timeout, () => this.open(appIcon)]) + } + + requestClose() { + this._endOpenCloseTimeouts() + this._addCloseTimeout() + } + + open(appIcon, preventCloseWindow) { + if (this.currentAppIcon != appIcon) { + this.currentAppIcon = appIcon + this.allowCloseWindow = !preventCloseWindow + + if (!this.opened) { + this._refreshGlobals() + + this.set_height(this.clipHeight) + this.show() + + setStyle( + this.menu, + 'padding: 0; margin: 0; border: none; background-image: none;', + ) + } + + this._mergeWindows(appIcon) + this._updatePosition() + this._animateOpenOrClose(true) + + this._setReactive(true) + this._setOpenedState(true) + } + } + + close(immediate) { + this._endOpenCloseTimeouts() + this._removeFocus() + this._endPeek() + + if (immediate) { + Utils.stopAnimations(this.menu) + this._resetHiddenState() + } else { + this._animateOpenOrClose(false, () => this._resetHiddenState()) + } + + this._setReactive(false) + this.currentAppIcon = null + } + + update(appIcon, windows) { + if (this.currentAppIcon == appIcon) { + if (windows && !windows.length) { + this.close() + } else { + this._addAndRemoveWindows(windows) + this._updatePosition() + } + } + } + + updatePosition() { + this._updatePosition() + } + + focusNext() { + let previews = this._box.get_children() + let currentIndex = this._focusedPreview + ? previews.indexOf(this._focusedPreview) + : -1 + let nextIndex = currentIndex + 1 + + nextIndex = previews[nextIndex] ? nextIndex : 0 + + if (previews[nextIndex]) { + this._removeFocus() + previews[nextIndex].setFocus(true) + this._focusedPreview = previews[nextIndex] + } + + return nextIndex + } + + activateFocused() { + if (this.opened && this._focusedPreview) { + this._focusedPreview.activate() + } + } + + requestPeek(window) { + this._timeoutsHandler.remove(T3) + + if (SETTINGS.get_boolean('peek-mode')) { + if (this.peekInitialWorkspaceIndex < 0) { + this._timeoutsHandler.add([ + T3, + ENTER_PEEK_MODE_TIMEOUT, + () => this._peek(window), + ]) + } else { + this._peek(window) + } + } + } + + endPeekHere() { + this._endPeek(true) + } + + ensureVisible(preview) { + let [, upper, pageSize] = this._getScrollAdjustmentValues() + + if (upper > pageSize) { + this._timeoutsHandler.add([ + T4, + ENSURE_VISIBLE_MS, + () => + Utils.ensureActorVisibleInScrollView( + this._scrollView, + preview, + MIN_DIMENSION, + () => this._updateScrollFade(), + ), + ]) + } + } + + getCurrentAppIcon() { + return this.currentAppIcon + } + + _setReactive(reactive) { + this._box.get_children().forEach((c) => (c.reactive = reactive)) + this.menu.reactive = reactive + } + + _setOpenedState(opened) { + this.opened = opened + this.emit('open-state-changed') + } + + _resetHiddenState() { + this.hide() + this.set_height(0) + this._setOpenedState(false) + this.menu.opacity = 0 + this.menu[this._translationProp] = this._translationOffset + this._box.get_children().forEach((c) => c.destroy()) + } + + _removeFocus() { + if (this._focusedPreview) { + this._focusedPreview.setFocus(false) + this._focusedPreview = null + } + } + + _mergeWindows(appIcon, windows) { + windows = + windows || + (appIcon.window + ? [appIcon.window] + : appIcon.getAppIconInterestingWindows()) + windows.sort(Taskbar.sortWindowsCompareFunction) + + let currentPreviews = this._box.get_children() + let l = Math.max(windows.length, currentPreviews.length) + + for (let i = 0; i < l; ++i) { + if (currentPreviews[i] && windows[i]) { + currentPreviews[i].assignWindow(windows[i], this.opened) + } else if (!currentPreviews[i]) { + this._addNewPreview(windows[i]) + } else if (!windows[i]) { + currentPreviews[i][!this.opened ? 'destroy' : 'animateOut']() + } + } + } + + _addAndRemoveWindows(windows) { + let currentPreviews = this._box.get_children() + + windows.sort(Taskbar.sortWindowsCompareFunction) + + for (let i = 0, l = windows.length; i < l; ++i) { + let currentIndex = Utils.findIndex( + currentPreviews, + (c) => c.window == windows[i], + ) + + if (currentIndex < 0) { + this._addNewPreview(windows[i]) + } else { + currentPreviews[currentIndex].assignWindow(windows[i]) + currentPreviews.splice(currentIndex, 1) + + if (this._peekedWindow && this._peekedWindow == windows[i]) { + this.requestPeek(windows[i]) + } + } + } + + currentPreviews.forEach((c) => c.animateOut()) + } + + _addNewPreview(window) { + let preview = new Preview(this) + + this._box.add_child(preview) + preview.adjustOnStage() + preview.assignWindow(window, this.opened) + } + + _addCloseTimeout() { + this._timeoutsHandler.add([ + T2, + LEAVE_TIMEOUT, + () => this.close(), + ]) + } + + _onHoverChanged() { + this._endOpenCloseTimeouts() + + if (this.currentAppIcon) { + this.menu.sync_hover() // See dash-to-panel issue #2169 + + if (!this.menu.hover) { + this._addCloseTimeout() + this._endPeek() + } + } + } + + _onScrollEvent(actor, event) { + if (!event.is_pointer_emulated()) { + let vOrh = this.isVertical ? 'v' : 'h' + let adjustment = + this._scrollView['get_' + vOrh + 'scroll_bar']().get_adjustment() + let increment = adjustment.step_increment + let delta = increment + + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + delta = -increment + break + case Clutter.ScrollDirection.SMOOTH: { + let [dx, dy] = event.get_scroll_delta() + delta = dy * increment + delta += dx * increment + break + } + } + + adjustment.set_value(adjustment.get_value() + delta) + this._updateScrollFade() + } + + return Clutter.EVENT_STOP + } + + _endOpenCloseTimeouts() { + this._timeoutsHandler.remove(T1) + this._timeoutsHandler.remove(T2) + this._timeoutsHandler.remove(T4) + } + + _refreshGlobals() { + isLeftButtons = + Meta.prefs_get_button_layout().left_buttons.indexOf( + Meta.ButtonFunction.CLOSE, + ) >= 0 + scaleFactor = Utils.getScaleFactor() + headerHeight = HEADER_HEIGHT * scaleFactor + animationTime = WINDOW_PREVIEW_ANIMATION_TIME * 0.001 + aspectRatio.x = { + size: WINDOW_PREVIEW_ASPECT_RATIO_X, + fixed: WINDOW_PREVIEW_FIXED_X, + } + aspectRatio.y = { + size: WINDOW_PREVIEW_ASPECT_RATIO_Y, + fixed: WINDOW_PREVIEW_FIXED_Y, + } + + alphaBg = this._getThemeBackground().alpha + } + + _updateClip() { + let x, y, w + let geom = this.panel.getGeometry() + let panelSize = geom.outerSize - geom.fixedPadding + let panelBoxTheme = this.panel.panelBox.get_theme_node() + let previewSize = + (SETTINGS.get_int('window-preview-size') + + WINDOW_PREVIEW_PADDING * 2) * + scaleFactor + + if (this.isVertical) { + w = previewSize + this.clipHeight = this.panel.monitor.height + y = this.panel.monitor.y + } else { + w = this.panel.monitor.width + this.clipHeight = previewSize + headerHeight + x = this.panel.monitor.x + } + + if (geom.position == St.Side.LEFT) { + x = + this.panel.monitor.x + + panelSize - + panelBoxTheme.get_padding(St.Side.RIGHT) + + MARGIN_SIZE + } else if (geom.position == St.Side.RIGHT) { + x = + this.panel.monitor.x + + this.panel.monitor.width - + (panelSize + previewSize) + + panelBoxTheme.get_padding(St.Side.LEFT) - + MARGIN_SIZE + } else if (geom.position == St.Side.TOP) { + y = geom.y + panelSize - panelBoxTheme.get_padding(St.Side.BOTTOM) + MARGIN_SIZE + } else { + //St.Side.BOTTOM + y = + this.panel.monitor.y + + this.panel.monitor.height - + (panelSize - + panelBoxTheme.get_padding(St.Side.TOP) + + previewSize + + headerHeight) - + MARGIN_SIZE + } + + Utils.setClip(this, x, y, w, this.clipHeight) + } + + _updatePosition() { + let sourceNode = this.currentAppIcon.get_theme_node() + let sourceContentBox = sourceNode.get_content_box( + this.currentAppIcon.get_allocation_box(), + ) + let sourceAllocation = Utils.getTransformedAllocation(this.currentAppIcon) + let [previewsWidth, previewsHeight] = this._getPreviewsSize() + let appIconMargin = Taskbar.APPICON_MARGIN / scaleFactor + let x = 0, + y = 0 + + previewsWidth = Math.min(previewsWidth, this.panel.monitor.width) + previewsHeight = Math.min(previewsHeight, this.panel.monitor.height) + this._updateScrollFade( + previewsWidth < this.panel.monitor.width && + previewsHeight < this.panel.monitor.height, + ) + + if (this.isVertical) { + y = + sourceAllocation.y1 + + appIconMargin - + this.panel.monitor.y + + (sourceContentBox.y2 - sourceContentBox.y1 - previewsHeight) * 0.5 + y = Math.max(y, 0) + y = Math.min(y, this.panel.monitor.height - previewsHeight) + } else { + x = + sourceAllocation.x1 + + appIconMargin - + this.panel.monitor.x + + (sourceContentBox.x2 - sourceContentBox.x1 - previewsWidth) * 0.5 + x = Math.max(x, 0) + x = Math.min(x, this.panel.monitor.width - previewsWidth) + } + + if (!this.opened) { + this.menu.set_position(x, y) + this.menu.set_size(previewsWidth, previewsHeight) + } else { + Utils.animate( + this.menu, + getTweenOpts({ + x: x, + y: y, + width: previewsWidth, + height: previewsHeight, + }), + ) + } + } + + _updateScrollFade(remove) { + let [value, upper, pageSize] = this._getScrollAdjustmentValues() + let needsFade = Math.round(upper) > Math.round(pageSize) + let fadeWidgets = this.menu + .get_children() + .filter((c) => c != this._scrollView) + + if (!remove && needsFade) { + if (!fadeWidgets.length) { + fadeWidgets.push(this._getFadeWidget()) + fadeWidgets.push(this._getFadeWidget(true)) + + this.menu.add_child(fadeWidgets[0]) + this.menu.add_child(fadeWidgets[1]) + } + + fadeWidgets[0].visible = value > 0 + fadeWidgets[1].visible = value + pageSize < upper + } else if (remove || (!needsFade && fadeWidgets.length)) { + fadeWidgets.forEach((fw) => fw.destroy()) + } + } + + _getScrollAdjustmentValues() { + let [value, , upper, , , pageSize] = + this._scrollView[ + (this.isVertical ? 'v' : 'h') + 'adjustment' + ].get_values() + + return [value, upper, pageSize] + } + + _getFadeWidget(end) { + let x = 0, + y = 0 + let startBg = Utils.getrgbaColor( + this._getThemeBackground(), + Math.min(alphaBg + 0.1, 1), + ) + let endBg = Utils.getrgbaColor( + this._getThemeBackground(), + 0, + ) + let fadeStyle = + 'background-gradient-start:' + + startBg + + 'background-gradient-end:' + + endBg + + 'background-gradient-direction:' + + this.panel.getOrientation() + + if (this.isVertical) { + y = end ? this.panel.monitor.height - FADE_SIZE : 0 + } else { + x = end ? this.panel.monitor.width - FADE_SIZE : 0 + } + + let fadeWidget = new St.Widget({ + reactive: false, + pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }), + rotation_angle_z: end ? 180 : 0, + style: fadeStyle, + x: x, + y: y, + width: this.isVertical ? this.width : FADE_SIZE, + height: this.isVertical ? FADE_SIZE : this.height, + }) + + return fadeWidget + } + + _getPreviewsSize() { + let previewsWidth = 0 + let previewsHeight = 0 + + this._box.get_children().forEach((c) => { + if (!c.animatingOut) { + let [width, height] = c.getSize() + + if (this.isVertical) { + previewsWidth = Math.max(width, previewsWidth) + previewsHeight += height + } else { + previewsWidth += width + previewsHeight = Math.max(height, previewsHeight) + } + } + }) + + return [previewsWidth, previewsHeight] + } + + _getThemeBackground(reload) { + if (reload || !this._themeBackground) { + let fakeTooltip = new St.Bin({ style_class: 'dash-label' }) + Main.uiGroup.add_child(fakeTooltip) + let fakeTheme = fakeTooltip.get_theme_node() + this._themeBackground = fakeTheme.get_background_color() + Main.uiGroup.remove_child(fakeTooltip) + } + + return this._themeBackground + } + + _animateOpenOrClose(show, onComplete) { + let isTranslationAnimation = this.menu[this._translationProp] != 0 + let tweenOpts = { + opacity: show ? 255 : 0, + transition: show ? 'easeInOutQuad' : 'easeInCubic', + onComplete: () => { + if (isTranslationAnimation) { + Main.layoutManager._queueUpdateRegions() + } + + ;(onComplete || (() => {}))() + }, + } + + tweenOpts[this._translationProp] = show + ? this._translationDirection + : this._translationOffset + + Utils.animate(this.menu, getTweenOpts(tweenOpts)) + } + + _peek(window) { + let currentWorkspace = Utils.getCurrentWorkspace() + let isAppSpread = !Main.sessionMode.hasWorkspaces + let windowWorkspace = isAppSpread + ? currentWorkspace + : window.get_workspace() + let focusWindow = () => + this._focusMetaWindow(PEEK_MODE_OPACITY, window) + + this._restorePeekedWindowStack() + + if (this._peekedWindow && windowWorkspace != currentWorkspace) { + currentWorkspace + .list_windows() + .forEach((mw) => this.animateWindowOpacity(mw, null, 255)) + } + + this._peekedWindow = window + + if (currentWorkspace != windowWorkspace) { + this._switchToWorkspaceImmediate(windowWorkspace.index()) + this._timeoutsHandler.add([T3, 100, focusWindow]) + } else { + focusWindow() + } + + if (this.peekInitialWorkspaceIndex < 0) { + this.peekInitialWorkspaceIndex = currentWorkspace.index() + } + } + + _endPeek(stayHere) { + this._timeoutsHandler.remove(T3) + + if (this._peekedWindow) { + let immediate = + !stayHere && + this.peekInitialWorkspaceIndex != Utils.getCurrentWorkspace().index() + + this._restorePeekedWindowStack() + this._focusMetaWindow(255, this._peekedWindow, immediate, true) + this._peekedWindow = null + + if (!stayHere) { + this._switchToWorkspaceImmediate(this.peekInitialWorkspaceIndex) + } + + this.peekInitialWorkspaceIndex = -1 + } + } + + _switchToWorkspaceImmediate(workspaceIndex) { + let workspace = Utils.getWorkspaceByIndex(workspaceIndex) + let shouldAnimate = Main.wm._shouldAnimate + + if ( + !workspace || + (!workspace.list_windows().length && + workspaceIndex < Utils.getWorkspaceCount() - 1) + ) { + workspace = Utils.getCurrentWorkspace() + } + + Main.wm._shouldAnimate = () => false + workspace.activate(global.display.get_current_time_roundtrip()) + Main.wm._shouldAnimate = shouldAnimate + } + + _focusMetaWindow(dimOpacity, window, immediate, ignoreFocus) { + let isAppSpread = !Main.sessionMode.hasWorkspaces + let windowWorkspace = isAppSpread + ? Utils.getCurrentWorkspace() + : window.get_workspace() + let windows = isAppSpread + ? Utils.getAllMetaWindows() + : windowWorkspace.list_windows() + + windows.forEach((mw) => { + let wa = mw.get_compositor_private() + let isFocused = !ignoreFocus && mw == window + + if (wa) { + if (isFocused) { + mw[PEEK_INDEX_PROP] = wa.get_parent().get_children().indexOf(wa) + wa.get_parent().set_child_above_sibling(wa, null) + } + + if (isFocused && mw.minimized) { + wa.show() + } + + this.animateWindowOpacity( + mw, + wa, + isFocused ? 255 : dimOpacity, + immediate, + ) + } + }) + } + + animateWindowOpacity(metaWindow, windowActor, opacity, immediate) { + windowActor = windowActor || metaWindow.get_compositor_private() + + if (windowActor && !metaWindow.minimized) { + let tweenOpts = getTweenOpts({ opacity }) + + if (immediate && !metaWindow.is_on_all_workspaces()) { + tweenOpts.time = 0 + } + + Utils.animateWindowOpacity(windowActor, tweenOpts) + } + } + + _restorePeekedWindowStack() { + let windowActor = this._peekedWindow + ? this._peekedWindow.get_compositor_private() + : null + + if (windowActor) { + if (Object.hasOwn(this._peekedWindow, PEEK_INDEX_PROP)) { + windowActor + .get_parent() + .set_child_at_index( + windowActor, + this._peekedWindow[PEEK_INDEX_PROP], + ) + delete this._peekedWindow[PEEK_INDEX_PROP] + } + + if (this._peekedWindow.minimized) { + windowActor.hide() + } + } + } + }, +) + +export const Preview = GObject.registerClass( + {}, + class Preview extends St.Widget { + _init(previewMenu) { + super._init({ + style_class: 'preview-container', + reactive: true, + track_hover: true, + layout_manager: new Clutter.BinLayout(), + }) + + this.window = null + this._waitWindowId = 0 + this._needsCloseButton = true + this.cloneWidth = this.cloneHeight = 0 + this._previewMenu = previewMenu + this._padding = WINDOW_PREVIEW_PADDING * scaleFactor + this._previewDimensions = this._getPreviewDimensions() + this.animatingOut = false + + let box = new St.Widget({ + layout_manager: new Clutter.BoxLayout({ + orientation: Clutter.Orientation.VERTICAL, + }), + y_expand: true, + }) + let [previewBinWidth, previewBinHeight] = this._getBinSize() + let closeButton = new St.Button({ + style_class: 'window-close', + accessible_name: 'Close window', + }) + + closeButton.add_child(new St.Icon({ icon_name: 'window-close-symbolic' })) + + this._closeButtonBin = new St.Widget({ + style_class: 'preview-close-btn-container', + layout_manager: new Clutter.BinLayout(), + opacity: 0, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign[isLeftButtons ? 'START' : 'END'], + y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'], + }) + + this._closeButtonBin.add_child(closeButton) + + this._previewBin = new St.Widget({ + layout_manager: new Clutter.BinLayout(), + x_expand: true, + y_expand: true, + style: 'padding: ' + this._padding / scaleFactor + 'px;', + }) + + this._previewBin.set_size(previewBinWidth, previewBinHeight) + + box.add_child(this._previewBin) + + if (headerHeight) { + let headerBox = new St.Widget({ + style_class: 'preview-header-box', + layout_manager: new Clutter.BoxLayout(), + x_expand: true, + y_align: Clutter.ActorAlign[isTopHeader ? 'START' : 'END'], + }) + headerBox.set_size(headerHeight, headerHeight) + + this._windowTitle = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + x_expand: true, + }) + + headerBox.insert_child_at_index( + this._windowTitle, + isLeftButtons ? 1 : 2, + ) + + box.insert_child_at_index(headerBox, isTopHeader ? 0 : 1) + } + + this.add_child(box) + this.add_child(this._closeButtonBin) + + closeButton.connect('clicked', () => this._onCloseBtnClick()) + this.connect('notify::hover', () => this._onHoverChanged()) + this.connect('button-release-event', (actor, e) => + this._onButtonReleaseEvent(e), + ) + this.connect('destroy', () => this._onDestroy()) + } + + adjustOnStage() { + let closeButton = this._closeButtonBin.get_first_child() + let closeButtonHeight = closeButton.height + let maxCloseButtonSize = MAX_CLOSE_BUTTON_SIZE * scaleFactor + let closeButtonBorderRadius = '' + + if (closeButtonHeight > maxCloseButtonSize) { + closeButtonHeight = maxCloseButtonSize + closeButton.set_size(closeButtonHeight, closeButtonHeight) + } + + if (!headerHeight) { + closeButtonBorderRadius = 'border-radius: ' + + if (isTopHeader) { + closeButtonBorderRadius += isLeftButtons ? '0 0 8px 0;' : '0 0 0 8px;' + } else { + closeButtonBorderRadius += isLeftButtons ? '0 8px 0 0;' : '8px 0 0 0;' + } + } + + setStyle( + this._closeButtonBin, + 'padding: ' + + (headerHeight + ? Math.round( + ((headerHeight - closeButtonHeight) * 0.5) / scaleFactor, + ) + : 4) + + 'px;' + + closeButtonBorderRadius, + ) + } + + assignWindow(window, animateSize) { + if (this.window != window) { + let _assignWindowClone = () => { + if (window.get_compositor_private()) { + let cloneBin = this._getWindowCloneBin(window) + + this._resizeClone(cloneBin, window) + this._addClone(cloneBin, animateSize) + this._previewMenu.updatePosition() + } else if (!this._waitWindowId) { + this._waitWindowId = GLib.idle_add( + GLib.PRIORITY_DEFAULT_IDLE, + () => { + this._waitWindowId = 0 + + if (this._previewMenu.opened) { + _assignWindowClone() + } + + return GLib.SOURCE_REMOVE + }, + ) + } + } + + _assignWindowClone() + } + + this._cancelAnimateOut() + this._removeWindowSignals() + this.window = window + this._needsCloseButton = + this._previewMenu.allowCloseWindow && + window.can_close() && + !Utils.checkIfWindowHasTransient(window) + this._updateHeader() + } + + animateOut() { + if (!this.animatingOut) { + let tweenOpts = getTweenOpts({ + opacity: 0, + width: 0, + height: 0, + onComplete: () => this.destroy(), + }) + + this.animatingOut = true + + Utils.stopAnimations(this) + Utils.animate(this, tweenOpts) + } + } + + getSize() { + let [binWidth, binHeight] = this._getBinSize() + + binWidth = Math.max(binWidth, this.cloneWidth + this._padding * 2) + binHeight = + Math.max(binHeight, this.cloneHeight + this._padding * 2) + headerHeight + + return [binWidth, binHeight] + } + + setFocus(focused) { + this._hideOrShowCloseButton(!focused) + setStyle( + this, + this._getBackgroundColor(FOCUSED_COLOR_OFFSET, focused ? '-' : 0), + ) + + if (focused) { + this._previewMenu.ensureVisible(this) + this._previewMenu.requestPeek(this.window) + } + } + + activate() { + this._previewMenu.endPeekHere() + this._previewMenu.close() + Main.activateWindow(this.window) + } + + _onDestroy() { + if (this._waitWindowId) { + GLib.source_remove(this._waitWindowId) + this._waitWindowId = 0 + } + + this._removeWindowSignals() + } + + _onHoverChanged() { + this.setFocus(this.hover) + } + + _onCloseBtnClick() { + this._hideOrShowCloseButton(true) + this.reactive = false + + if (!SETTINGS.get_boolean('group-apps')) { + this._previewMenu.close() + } else { + this._previewMenu.endPeekHere() + } + + this.window.delete(global.get_current_time()) + } + + _onButtonReleaseEvent(e) { + switch (e.get_button()) { + case 1: // Left click + this.activate() + break + case 2: // Middle click + if (SETTINGS.get_boolean('preview-middle-click-close')) { + this._onCloseBtnClick() + } + break + case 3: // Right click + this._showContextMenu(e) + break + } + + return Clutter.EVENT_STOP + } + + _cancelAnimateOut() { + if (this.animatingOut) { + this.animatingOut = false + + Utils.stopAnimations(this) + Utils.animate( + this, + getTweenOpts({ + opacity: 255, + width: this.cloneWidth, + height: this.cloneHeight, + }), + ) + } + } + + _showContextMenu(e) { + let coords = e.get_coords() + let currentWorkspace = + this._previewMenu.peekInitialWorkspaceIndex < 0 + ? Utils.getCurrentWorkspace() + : Utils.getWorkspaceByIndex( + this._previewMenu.peekInitialWorkspaceIndex, + ) + + Main.wm._showWindowMenu(null, this.window, Meta.WindowMenuType.WM, { + x: coords[0], + y: coords[1], + width: 0, + height: 0, + }) + + let menu = Main.wm._windowMenuManager._manager._menus[0] + + menu.connect('open-state-changed', () => + this._previewMenu.menu.sync_hover(), + ) + this._previewMenu.menu.sync_hover() + + if (this.window.get_workspace() != currentWorkspace) { + let menuItem = new PopupMenu.PopupMenuItem( + _('Move to current Workspace') + + ' [' + + (currentWorkspace.index() + 1) + + ']', + ) + let menuItems = menu.box.get_children() + let insertIndex = Utils.findIndex( + menuItems, + (c) => c._delegate instanceof PopupMenu.PopupSeparatorMenuItem, + ) + + insertIndex = insertIndex >= 0 ? insertIndex : menuItems.length - 1 + menu.addMenuItem(menuItem, insertIndex) + menuItem.connect('activate', () => + this.window.change_workspace(currentWorkspace), + ) + } + } + + _removeWindowSignals() { + if (this._titleWindowChangeId) { + this.window.disconnect(this._titleWindowChangeId) + this._titleWindowChangeId = 0 + } + } + + _updateHeader() { + if (headerHeight) { + this._titleWindowChangeId = this.window.connect('notify::title', () => + this._updateWindowTitle(), + ) + setStyle( + this._windowTitle, + 'max-width: 0px; padding-left: 8px; padding-right: 8px; text-align: center;', + ) + this._updateWindowTitle() + } + } + + _updateWindowTitle() { + this._windowTitle.text = this.window.title + } + + _hideOrShowCloseButton(hide) { + if (this._needsCloseButton) { + Utils.animate( + this._closeButtonBin, + getTweenOpts({ opacity: hide ? 0 : 255 }), + ) + } + } + + _getBackgroundColor(offset, alpha) { + return ( + 'background-color: ' + + this._getRgbaColor(offset, alpha) + + 'transition-duration:' + + this._previewMenu.panel.dynamicTransparency.animationDuration + ) + } + + _getRgbaColor(offset, alpha) { + alpha = Math.abs(alpha) + + if (isNaN(alpha)) { + alpha = alphaBg + } + + return Utils.getrgbaColor( + this._previewMenu._getThemeBackground(), + alpha, + offset, + ) + } + + _addClone(newCloneBin, animateSize) { + let currentClones = this._previewBin.get_children() + let newCloneOpts = getTweenOpts({ opacity: 255 }) + + this._previewBin.add_child(newCloneBin) + + if (currentClones.length) { + let currentCloneBin = currentClones.pop() + let currentCloneOpts = getTweenOpts({ + opacity: 0, + onComplete: () => currentCloneBin.destroy(), + }) + + if (newCloneBin.width > currentCloneBin.width) { + newCloneOpts.width = newCloneBin.width + newCloneBin.width = currentCloneBin.width + } else { + currentCloneOpts.width = newCloneBin.width + } + + if (newCloneBin.height > currentCloneBin.height) { + newCloneOpts.height = newCloneBin.height + newCloneBin.height = currentCloneBin.height + } else { + currentCloneOpts.height = newCloneBin.height + } + + currentClones.forEach((c) => c.destroy()) + Utils.animate(currentCloneBin, currentCloneOpts) + } else if (animateSize) { + newCloneBin.width = 0 + newCloneBin.height = 0 + newCloneOpts.width = this.cloneWidth + newCloneOpts.height = this.cloneHeight + } + + Utils.animate(newCloneBin, newCloneOpts) + } + + _getWindowCloneBin(window) { + let frameRect = window.get_frame_rect() + let bufferRect = window.get_buffer_rect() + let clone = new Clutter.Clone({ source: window.get_compositor_private() }) + let cloneBin = new St.Widget({ + opacity: 0, + layout_manager: + frameRect.width != bufferRect.width || + frameRect.height != bufferRect.height + ? new WindowCloneLayout(frameRect, bufferRect) + : new Clutter.BinLayout(), + }) + + cloneBin.add_child(clone) + + return cloneBin + } + + _getBinSize() { + let [fixedWidth, fixedHeight] = this._previewDimensions + + return [ + aspectRatio.x.fixed ? fixedWidth + this._padding * 2 : -1, + aspectRatio.y.fixed ? fixedHeight + this._padding * 2 : -1, + ] + } + + _resizeClone(cloneBin, window) { + let frameRect = + cloneBin.layout_manager.frameRect || window.get_frame_rect() + let [fixedWidth, fixedHeight] = this._previewDimensions + let ratio = Math.min( + fixedWidth / frameRect.width, + fixedHeight / frameRect.height, + 1, + ) + let cloneWidth = frameRect.width * ratio + let cloneHeight = frameRect.height * ratio + + let clonePaddingTB = + cloneHeight < MIN_DIMENSION ? MIN_DIMENSION - cloneHeight : 0 + let clonePaddingLR = + cloneWidth < MIN_DIMENSION ? MIN_DIMENSION - cloneWidth : 0 + let clonePaddingTop = clonePaddingTB * 0.5 + let clonePaddingLeft = clonePaddingLR * 0.5 + + this.cloneWidth = cloneWidth + clonePaddingLR * scaleFactor + this.cloneHeight = cloneHeight + clonePaddingTB * scaleFactor + + cloneBin.set_style( + 'padding: ' + clonePaddingTop + 'px ' + clonePaddingLeft + 'px;', + ) + cloneBin.layout_manager.ratio = ratio + cloneBin.layout_manager.padding = [ + clonePaddingLeft * scaleFactor, + clonePaddingTop * scaleFactor, + ] + + cloneBin.get_first_child().set_size(cloneWidth, cloneHeight) + } + + _getPreviewDimensions() { + let size = SETTINGS.get_int('window-preview-size') * scaleFactor + let w, h + + if (this._previewMenu.isVertical) { + w = size + h = (w * aspectRatio.y.size) / aspectRatio.x.size + } else { + h = size + w = (h * aspectRatio.x.size) / aspectRatio.y.size + } + + return [w, h] + } + }, +) + +export const WindowCloneLayout = GObject.registerClass( + {}, + class WindowCloneLayout extends Clutter.BinLayout { + _init(frameRect, bufferRect) { + super._init() + + //the buffer_rect contains the transparent padding that must be removed + this.frameRect = frameRect + this.bufferRect = bufferRect + } + + vfunc_allocate(actor, box) { + let [width, height] = box.get_size() + + box.set_origin( + (this.bufferRect.x - this.frameRect.x) * this.ratio + this.padding[0], + (this.bufferRect.y - this.frameRect.y) * this.ratio + this.padding[1], + ) + + box.set_size( + width + (this.bufferRect.width - this.frameRect.width) * this.ratio, + height + (this.bufferRect.height - this.frameRect.height) * this.ratio, + ) + + actor.get_first_child().allocate(box) + } + }, +) + +export function setStyle(actor, style) { + actor.set_style(style) +} + +export function getTweenOpts(opts) { + let defaults = { + time: animationTime, + transition: 'easeInOutQuad', + } + + return Utils.mergeObjects(opts || {}, defaults) +} diff --git a/taskbar.js b/taskbar.js deleted file mode 100644 index 7ded7ab..0000000 --- a/taskbar.js +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * - * Credits: - * This file is based on code from the Dash to Dock extension by micheleg - * and code from the Dash to Panel extension - * Some code was also adapted from the upstream Gnome Shell source code. - */ - - -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Graphene from 'gi://Graphene'; -import Shell from 'gi://Shell'; -import St from 'gi://St'; - -import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; -import * as Dash from 'resource:///org/gnome/shell/ui/dash.js'; -import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; - -import * as AppIcons from './appIcons.js'; -import * as PanelManager from './panelManager.js'; -import * as PanelSettings from './panelSettings.js'; -import * as Pos from './panelPositions.js'; -import * as Utils from './utils.js'; -import * as WindowPreview from './windowPreview.js'; -import {SETTINGS} from './extension.js'; - -const SearchController = Main.overview.searchController; - -export const DASH_ANIMATION_TIME = .2; // Dash.DASH_ANIMATION_TIME is now private -const DASH_ITEM_HOVER_TIMEOUT = .3; // Dash.DASH_ITEM_HOVER_TIMEOUT is now private -export const MIN_ICON_SIZE = 4; -export const APPICON_MARGIN = 4; -export const APPICON_PADDING = 8; - -const T1 = 'ensureAppIconVisibilityTimeout' -const T2 = 'showLabelTimeout' -const T3 = 'resetHoverTimeout' - - -/** - * Extend DashItemContainer - * - * - set label position based on taskbar orientation - * - * I can't subclass the original object because of this: https://bugzilla.gnome.org/show_bug.cgi?id=688973. - * thus use this ugly pattern. - */ - -export function extendDashItemContainer(dashItemContainer) { - dashItemContainer.showLabel = AppIcons.ItemShowLabel; -} - -/* This class is a fork of the upstream DashActor class (ui.dash.js) - * - * Summary of changes: - * - modified chldBox calculations for when 'show-apps-at-top' option is checked - * - handle horizontal dash - */ -export const TaskbarActor = GObject.registerClass({ -}, class TaskbarActor extends St.Widget { - _init(delegate) { - this._delegate = delegate; - this._currentBackgroundColor = 0; - super._init({ name: 'zorintaskbarTaskbar', - layout_manager: new Clutter.BoxLayout({ orientation: Clutter.Orientation[delegate.dtpPanel.getOrientation().toUpperCase()] }), - clip_to_allocation: true }); - } - - vfunc_allocate(box) { - this.set_allocation(box); - - let panel = this._delegate.dtpPanel; - let availFixedSize = box[panel.fixedCoord.c2] - box[panel.fixedCoord.c1]; - let availVarSize = box[panel.varCoord.c2] - box[panel.varCoord.c1]; - let [dummy, scrollview, leftFade, rightFade] = this.get_children(); - let [, natSize] = this[panel.sizeFunc](availFixedSize); - let childBox = new Clutter.ActorBox(); - let orientation = panel.getOrientation(); - - dummy.allocate(childBox); - - childBox[panel.varCoord.c1] = box[panel.varCoord.c1]; - childBox[panel.varCoord.c2] = Math.min(availVarSize, natSize); - childBox[panel.fixedCoord.c1] = box[panel.fixedCoord.c1]; - childBox[panel.fixedCoord.c2] = box[panel.fixedCoord.c2]; - - scrollview.allocate(childBox); - - let [value, , upper, , , pageSize] = scrollview[orientation[0] + 'adjustment'].get_values(); - upper = Math.floor(upper); - scrollview._dtpFadeSize = upper > pageSize ? this._delegate.iconSize : 0; - - if (this._currentBackgroundColor !== panel.dynamicTransparency.currentBackgroundColor) { - this._currentBackgroundColor = panel.dynamicTransparency.currentBackgroundColor; - let gradientStyle = 'background-gradient-start: ' + this._currentBackgroundColor + - 'background-gradient-direction: ' + orientation; - - leftFade.set_style(gradientStyle); - rightFade.set_style(gradientStyle); - } - - childBox[panel.varCoord.c2] = childBox[panel.varCoord.c1] + (value > 0 ? scrollview._dtpFadeSize : 0); - leftFade.allocate(childBox); - - childBox[panel.varCoord.c1] = box[panel.varCoord.c2] - (value + pageSize < upper ? scrollview._dtpFadeSize : 0); - childBox[panel.varCoord.c2] = box[panel.varCoord.c2]; - rightFade.allocate(childBox); - } - - // We want to request the natural size of all our children - // as our natural width, so we chain up to StWidget (which - // then calls BoxLayout) - vfunc_get_preferred_width(forHeight) { - let [, natWidth] = St.Widget.prototype.vfunc_get_preferred_width.call(this, forHeight); - - return [0, natWidth]; - } - - vfunc_get_preferred_height(forWidth) { - let [, natHeight] = St.Widget.prototype.vfunc_get_preferred_height.call(this, forWidth); - - return [0, natHeight]; - } -}); - -/* This class is a fork of the upstream dash class (ui.dash.js) - * - * Summary of changes: - * - disconnect global signals adding a destroy method; - * - play animations even when not in overview mode - * - set a maximum icon size - * - show running and/or favorite applications - * - emit a custom signal when an app icon is added - * - Add scrollview - * Ensure actor is visible on keyfocus inside the scrollview - * - add 128px icon size, might be useful for hidpi display - * - Sync minimization application target position. - */ - -export const Taskbar = class extends EventEmitter { - - constructor(panel) { - super(); - - this.dtpPanel = panel; - - // start at smallest size due to running indicator drawing area expanding but not shrinking - this.iconSize = 16; - - this._shownInitially = false; - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._timeoutsHandler = new Utils.TimeoutsHandler(); - - this._labelShowing = false; - this.fullScrollView = 0; - - let isVertical = panel.checkIfVertical(); - - this._box = new St.BoxLayout({ vertical: isVertical, - clip_to_allocation: false, - x_align: Clutter.ActorAlign.START, - y_align: Clutter.ActorAlign.START }); - - this._container = new TaskbarActor(this); - this._scrollView = new St.ScrollView({ name: 'zorintaskbarScrollview', - hscrollbar_policy: St.PolicyType.NEVER, - vscrollbar_policy: St.PolicyType.NEVER, - enable_mouse_scrolling: true }); - - this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this)); - this._scrollView.add_child(this._box); - - this._showAppsIconWrapper = panel.showAppsIconWrapper; - this._showAppsIconWrapper.connect('menu-state-changed', (showAppsIconWrapper, opened) => { - this._itemMenuStateChanged(showAppsIconWrapper, opened); - }); - // an instance of the showAppsIcon class is encapsulated in the wrapper - this._showAppsIcon = this._showAppsIconWrapper.realShowAppsIcon; - this.showAppsButton = this._showAppsIcon.toggleButton; - - if (isVertical) { - this.showAppsButton.set_width(panel.geom.w); - } - - this.showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this)); - - this.showAppsButton.checked = (SearchController._showAppsButton) ? SearchController._showAppsButton.checked : false; - - this._showAppsIcon.childScale = 1; - this._showAppsIcon.childOpacity = 255; - this._showAppsIcon.icon.setIconSize(this.iconSize); - this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper); - - this._container.add_child(new St.Widget({ width: 0, reactive: false })); - this._container.add_child(this._scrollView); - - let orientation = panel.getOrientation(); - let fadeStyle = 'background-gradient-direction:' + orientation; - let fade1 = new St.Widget({ style_class: 'scrollview-fade', reactive: false }); - let fade2 = new St.Widget({ style_class: 'scrollview-fade', - reactive: false, - pivot_point: new Graphene.Point({ x: .5, y: .5 }), - rotation_angle_z: 180 }); - - fade1.set_style(fadeStyle); - fade2.set_style(fadeStyle); - - this._container.add_child(fade1); - this._container.add_child(fade2); - - this.previewMenu = new WindowPreview.PreviewMenu(panel); - this.previewMenu.enable(); - - let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; - this.actor = new St.Bin({ - child: this._container, - y_align: Clutter.ActorAlign.START, - x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START - }); - - const adjustment = this._scrollView[orientation[0] + 'adjustment']; - - this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this)); - - this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); - - this._appSystem = Shell.AppSystem.get_default(); - - this.iconAnimator = new PanelManager.IconAnimator(this.dtpPanel.panel); - - this._signalsHandler.add( - [ - this.dtpPanel.panel, - 'notify::height', - () => this._queueRedisplay() - ], - [ - this.dtpPanel.panel, - 'notify::width', - () => this._queueRedisplay() - ], - [ - this._appSystem, - 'installed-changed', - () => { - AppFavorites.getAppFavorites().reload(); - this._queueRedisplay(); - } - ], - [ - this._appSystem, - 'app-state-changed', - this._queueRedisplay.bind(this) - ], - [ - AppFavorites.getAppFavorites(), - 'changed', - this._queueRedisplay.bind(this) - ], - [ - global.window_manager, - 'switch-workspace', - () => this._connectWorkspaceSignals() - ], - [ - Utils.DisplayWrapper.getScreen(), - [ - 'window-entered-monitor', - 'window-left-monitor' - ], - () => { - if (SETTINGS.get_boolean('isolate-monitors')) { - this._queueRedisplay(); - } - } - ], - [ - Main.overview, - 'item-drag-begin', - this._onDragBegin.bind(this) - ], - [ - Main.overview, - 'item-drag-end', - this._onDragEnd.bind(this) - ], - [ - Main.overview, - 'item-drag-cancelled', - this._onDragCancelled.bind(this) - ], - [ - // Ensure the ShowAppsButton status is kept in sync - SearchController._showAppsButton, - 'notify::checked', - this._syncShowAppsButtonToggled.bind(this) - ], - [ - SETTINGS, - [ - 'changed::show-favorites', - 'changed::show-running-apps', - 'changed::show-favorites-all-monitors' - ], - () => { - setAttributes() - this._redisplay() - } - ], - [ - SETTINGS, - 'changed::group-apps', - () => { - setAttributes() - this._connectWorkspaceSignals(); - } - ], - [ - SETTINGS, - [ - 'changed::group-apps-use-launchers', - 'changed::taskbar-locked' - ], - () => { - setAttributes() - this.resetAppIcons() - } - ], - [ - adjustment, - [ - 'notify::upper', - 'notify::pageSize' - ], - () => this._onScrollSizeChange(adjustment) - ] - ); - - let setAttributes = () => { - this.isGroupApps = SETTINGS.get_boolean('group-apps'); - this.usingLaunchers = !this.isGroupApps && SETTINGS.get_boolean('group-apps-use-launchers'); - this.showFavorites = SETTINGS.get_boolean('show-favorites') && - (this.dtpPanel.isPrimary || SETTINGS.get_boolean('show-favorites-all-monitors')) - this.showRunningApps = SETTINGS.get_boolean('show-running-apps') - this.allowSplitApps = this.usingLaunchers || (!this.isGroupApps && !this.showFavorites) - } - - setAttributes() - - this._onScrollSizeChange(adjustment); - this._connectWorkspaceSignals(); - } - - destroy() { - if (this._waitIdleId) { - GLib.source_remove(this._waitIdleId); - this._waitIdleId = 0; - } - - this._timeoutsHandler.destroy(); - this.iconAnimator.destroy(); - - this._signalsHandler.destroy(); - this._signalsHandler = 0; - - this._container.destroy(); - - this.previewMenu.disable(); - this.previewMenu.destroy(); - - this._disconnectWorkspaceSignals(); - } - - _onScrollEvent(actor, event) { - - let orientation = this.dtpPanel.getOrientation(); - - // reset timeout to avid conflicts with the mousehover event - this._timeoutsHandler.add([T1, 0, - () => this._swiping = false - ]); - - // Skip to avoid double events mouse - if (event.is_pointer_emulated()) - return Clutter.EVENT_STOP; - - let adjustment, delta; - - adjustment = this._scrollView[orientation[0] + 'adjustment']; - - let increment = adjustment.step_increment; - - switch ( event.get_scroll_direction() ) { - case Clutter.ScrollDirection.UP: - case Clutter.ScrollDirection.LEFT: - delta = -increment; - break; - case Clutter.ScrollDirection.DOWN: - case Clutter.ScrollDirection.RIGHT: - delta = +increment; - break; - case Clutter.ScrollDirection.SMOOTH: - let [dx, dy] = event.get_scroll_delta(); - delta = dy*increment; - delta += dx*increment; - break; - - } - - adjustment.set_value(adjustment.get_value() + delta); - - return Clutter.EVENT_STOP; - - } - - _onScrollSizeChange(adjustment) { - // Update minimization animation target position on scrollview change. - this._updateAppIcons(); - - // When applications are ungrouped and there is some empty space on the horizontal taskbar, - // force a fixed label width to prevent the icons from "wiggling" when an animation runs - // (adding or removing an icon). When the taskbar is full, revert to a dynamic label width - // to allow them to resize and make room for new icons. - if (!this.dtpPanel.checkIfVertical() && !this.isGroupApps) { - let initial = this.fullScrollView; - - if (!this.fullScrollView && Math.floor(adjustment.upper) > adjustment.page_size) { - this.fullScrollView = adjustment.page_size; - } else if (adjustment.page_size < this.fullScrollView) { - this.fullScrollView = 0; - } - - if (initial != this.fullScrollView && !this._waitIdleId) { - this._waitIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this._getAppIcons().forEach(a => a.updateTitleStyle()) - this._waitIdleId = 0; - - return GLib.SOURCE_REMOVE; - }); - } - } - } - - _onDragBegin() { - this._dragCancelled = false; - this._dragMonitor = { - dragMotion: this._onDragMotion.bind(this) - }; - DND.addDragMonitor(this._dragMonitor); - - if (this._box.get_n_children() == 0) { - this._emptyDropTarget = new Dash.EmptyDropTargetItem(); - this._box.insert_child_at_index(this._emptyDropTarget, 0); - this._emptyDropTarget.show(true); - } - - this._toggleFavoriteHighlight(true); - } - - _onDragCancelled() { - this._dragCancelled = true; - - if (this._dragInfo) { - this._box.set_child_at_index(this._dragInfo[1]._dashItemContainer, this._dragInfo[0]); - } - - this._endDrag(); - } - - _onDragEnd() { - if (this._dragCancelled) - return; - - this._endDrag(); - } - - _endDrag() { - if (this._dragInfo && this._dragInfo[1]._dashItemContainer instanceof DragPlaceholderItem) { - this._box.remove_child(this._dragInfo[1]._dashItemContainer); - this._dragInfo[1]._dashItemContainer.destroy(); - delete this._dragInfo[1]._dashItemContainer; - } - - this._dragInfo = null; - this._clearEmptyDropTarget(); - this._showAppsIcon.setDragApp(null); - DND.removeDragMonitor(this._dragMonitor); - - this._dragMonitor = null; - this.emit('end-drag'); - - this._toggleFavoriteHighlight(); - } - - _onDragMotion(dragEvent) { - let app = Dash.Dash.getAppFromSource(dragEvent.source); - if (app == null) - return DND.DragMotionResult.CONTINUE; - - let showAppsHovered = this._showAppsIcon.contains(dragEvent.targetActor); - - if (showAppsHovered) - this._showAppsIcon.setDragApp(app); - else - this._showAppsIcon.setDragApp(null); - - return DND.DragMotionResult.CONTINUE; - } - - _toggleFavoriteHighlight(show) { - let appFavorites = AppFavorites.getAppFavorites(); - let cssFuncName = (show ? 'add' : 'remove') + '_style_class_name'; - - if (this.showFavorites) - this._getAppIcons().filter(appIcon => (this.usingLaunchers && appIcon.isLauncher) || - (!this.usingLaunchers && appFavorites.isFavorite(appIcon.app.get_id()))) - .forEach(fav => fav._container[cssFuncName]('favorite')); - } - - handleIsolatedWorkspaceSwitch() { - this._shownInitially = this.isGroupApps; - this._queueRedisplay(); - } - - _connectWorkspaceSignals() { - this._disconnectWorkspaceSignals(); - - this._lastWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace(); - - this._workspaceWindowAddedId = this._lastWorkspace.connect('window-added', () => this._queueRedisplay()); - this._workspaceWindowRemovedId = this._lastWorkspace.connect('window-removed', () => this._queueRedisplay()); - } - - _disconnectWorkspaceSignals() { - if (this._lastWorkspace) { - this._lastWorkspace.disconnect(this._workspaceWindowAddedId); - this._lastWorkspace.disconnect(this._workspaceWindowRemovedId); - - this._lastWorkspace = null; - } - } - - _queueRedisplay() { - Main.queueDeferredWork(this._workId); - } - - _hookUpLabel(item, syncHandler) { - item.child.connect('notify::hover', () => { - this._syncLabel(item, syncHandler); - }); - - syncHandler.connect('sync-tooltip', () => { - this._syncLabel(item, syncHandler); - }); - } - - _createAppItem(app, window, isLauncher) { - let appIcon = new AppIcons.TaskbarAppIcon( - { - app, - window, - isLauncher - }, - this.dtpPanel, - { - setSizeManually: true, - showLabel: false, - isDraggable: !SETTINGS.get_boolean('taskbar-locked'), - }, - this.previewMenu, - this.iconAnimator - ); - - if (appIcon._draggable) { - appIcon._draggable.connect('drag-begin', - () => { - appIcon.opacity = 0; - appIcon.isDragged = 1; - }); - appIcon._draggable.connect('drag-end', - () => { - appIcon.opacity = 255; - delete appIcon.isDragged; - this._updateAppIcons(); - }); - } - - appIcon.connect('menu-state-changed', - (appIcon, opened) => { - this._itemMenuStateChanged(item, opened); - }); - - let item = new TaskbarItemContainer(); - - item._dtpPanel = this.dtpPanel - extendDashItemContainer(item); - - item.setChild(appIcon); - appIcon._dashItemContainer = item; - - appIcon.connect('notify::hover', () => { - if (appIcon.hover){ - this._timeoutsHandler.add([T1, 100, - () => Utils.ensureActorVisibleInScrollView(this._scrollView, appIcon, this._scrollView._dtpFadeSize) - ]) - } else { - this._timeoutsHandler.remove(T1) - } - }); - - appIcon.connect('clicked', - (actor) => { - Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize); - }); - - appIcon.connect('key-focus-in', (actor) => { - let [x_shift, y_shift] = Utils.ensureActorVisibleInScrollView(this._scrollView, actor, this._scrollView._dtpFadeSize); - - // This signal is triggered also by mouse click. The popup menu is opened at the original - // coordinates. Thus correct for the shift which is going to be applied to the scrollview. - if (appIcon._menu) { - appIcon._menu._boxPointer.xOffset = -x_shift; - appIcon._menu._boxPointer.yOffset = -y_shift; - } - }); - - // Override default AppIcon label_actor, now the - // accessible_name is set at DashItemContainer.setLabelText - appIcon.label_actor = null; - item.setLabelText(app.get_name()); - - appIcon.icon.setIconSize(this.iconSize); - this._hookUpLabel(item, appIcon); - - return item; - } - - // Return an array with the "proper" appIcons currently in the taskbar - _getAppIcons() { - // Only consider children which are "proper" icons and which are not - // animating out (which means they will be destroyed at the end of - // the animation) - return this._getTaskbarIcons().map(function(actor){ - return actor.child._delegate; - }); - } - - _getTaskbarIcons(includeAnimated) { - return this._box.get_children().filter(function(actor) { - return actor.child && - actor.child._delegate && - actor.child._delegate.icon && - (includeAnimated || !actor.animatingOut); - }); - } - - _updateAppIcons() { - let appIcons = this._getAppIcons(); - - appIcons.filter(icon => icon.constructor === AppIcons.TaskbarAppIcon).forEach(icon => { - icon.updateIcon(); - }); - } - - _itemMenuStateChanged(item, opened) { - // When the menu closes, it calls sync_hover, which means - // that the notify::hover handler does everything we need to. - if (opened) { - this._timeoutsHandler.remove(T2) - - item.hideLabel(); - } else { - // I want to listen from outside when a menu is closed. I used to - // add a custom signal to the appIcon, since gnome 3.8 the signal - // calling this callback was added upstream. - this.emit('menu-closed'); - } - } - - _syncLabel(item, syncHandler) { - let shouldShow = syncHandler ? syncHandler.shouldShowTooltip() : item.child.get_hover(); - - if (shouldShow) { - if (!this._timeoutsHandler.getId(T2)) { - let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; - - this._timeoutsHandler.add([T2, timeout, - () => { - this._labelShowing = true; - item.showLabel(); - } - ]); - - this._timeoutsHandler.remove(T3) - } - } else { - this._timeoutsHandler.remove(T2) - - item.hideLabel(); - if (this._labelShowing) { - this._timeoutsHandler.add([T3, DASH_ITEM_HOVER_TIMEOUT, - () => this._labelShowing = false - ]); - } - } - } - - _adjustIconSize() { - const thisMonitorIndex = this.dtpPanel.monitor.index; - let panelSize = PanelSettings.getPanelSize(SETTINGS, thisMonitorIndex); - let availSize = panelSize - APPICON_PADDING * 2; - let minIconSize = MIN_ICON_SIZE + panelSize % 2; - - if (availSize == this.iconSize) - return; - - if (availSize < minIconSize) { - availSize = minIconSize; - } - - // For the icon size, we only consider children which are "proper" - // icons and which are not animating out (which means they will be - // destroyed at the end of the animation) - let iconChildren = this._getTaskbarIcons().concat([this._showAppsIcon]); - let scale = this.iconSize / availSize; - - this.iconSize = availSize; - - for (let i = 0; i < iconChildren.length; i++) { - let icon = iconChildren[i].child._delegate.icon; - - // Set the new size immediately, to keep the icons' sizes - // in sync with this.iconSize - icon.setIconSize(this.iconSize); - - // Don't animate the icon size change when the overview - // is transitioning, or when initially filling - // the taskbar - if (Main.overview.animationInProgress || - !this._shownInitially) - continue; - - let [targetWidth, targetHeight] = icon.icon.get_size(); - - // Scale the icon's texture to the previous size and - // tween to the new size - icon.icon.set_size(icon.icon.width * scale, icon.icon.height * scale); - - Utils.animate(icon.icon, - { width: targetWidth, - height: targetHeight, - time: DASH_ANIMATION_TIME, - transition: 'easeOutQuad', - }); - } - } - - sortAppsCompareFunction(appA, appB) { - return getAppStableSequence(appA, this.dtpPanel.monitor) - - getAppStableSequence(appB, this.dtpPanel.monitor); - } - - getAppInfos() { - //get the user's favorite apps - let favoriteApps = this.showFavorites ? AppFavorites.getAppFavorites().getFavorites() : []; - - //find the apps that should be in the taskbar: the favorites first, then add the running apps - // When using isolation, we filter out apps that have no windows in - // the current workspace (this check is done in AppIcons.getInterestingWindows) - let runningApps = this.showRunningApps ? this._getRunningApps().sort(this.sortAppsCompareFunction.bind(this)) : []; - - if (this.allowSplitApps) { - return this._createAppInfos(favoriteApps, [], true) - .concat(this._createAppInfos(runningApps) - .filter(appInfo => appInfo.windows.length)); - } else { - return this._createAppInfos(favoriteApps.concat(runningApps.filter(app => favoriteApps.indexOf(app) < 0))) - .filter(appInfo => appInfo.windows.length || favoriteApps.indexOf(appInfo.app) >= 0); - } - } - - _redisplay() { - if (!this._signalsHandler) { - return; - } - - //get the currently displayed appIcons - let currentAppIcons = this._getTaskbarIcons(); - let expectedAppInfos = this.getAppInfos(); - - //remove the appIcons which are not in the expected apps list - for (let i = currentAppIcons.length - 1; i > -1; --i) { - let appIcon = currentAppIcons[i].child._delegate; - let appIndex = Utils.findIndex(expectedAppInfos, appInfo => appInfo.app == appIcon.app && - (!this.allowSplitApps || this.isGroupApps || appInfo.windows[0] == appIcon.window) && - appInfo.isLauncher == appIcon.isLauncher); - - if (appIndex < 0 || - (appIcon.window && (this.isGroupApps || expectedAppInfos[appIndex].windows.indexOf(appIcon.window) < 0)) || - (!appIcon.window && !appIcon.isLauncher && - !this.isGroupApps && expectedAppInfos[appIndex].windows.length)) { - currentAppIcons[i][this._shownInitially ? 'animateOutAndDestroy' : 'destroy'](); - currentAppIcons.splice(i, 1); - } - } - - //if needed, reorder the existing appIcons and create the missing ones - let currentPosition = 0; - for (let i = 0, l = expectedAppInfos.length; i < l; ++i) { - let neededAppIcons = this.isGroupApps || !expectedAppInfos[i].windows.length ? - [{ app: expectedAppInfos[i].app, window: null, isLauncher: expectedAppInfos[i].isLauncher }] : - expectedAppInfos[i].windows.map(window => ({ app: expectedAppInfos[i].app, window: window, isLauncher: false })); - - for (let j = 0, ll = neededAppIcons.length; j < ll; ++j) { - //check if the icon already exists - let matchingAppIconIndex = Utils.findIndex(currentAppIcons, appIcon => appIcon.child._delegate.app == neededAppIcons[j].app && - appIcon.child._delegate.window == neededAppIcons[j].window); - - if (matchingAppIconIndex > 0 && matchingAppIconIndex != currentPosition) { - //moved icon, reposition it - this._box.remove_child(currentAppIcons[matchingAppIconIndex]); - this._box.insert_child_at_index(currentAppIcons[matchingAppIconIndex], currentPosition); - } else if (matchingAppIconIndex < 0) { - //the icon doesn't exist yet, create a new one - let newAppIcon = this._createAppItem(neededAppIcons[j].app, neededAppIcons[j].window, neededAppIcons[j].isLauncher); - - this._box.insert_child_at_index(newAppIcon, currentPosition); - currentAppIcons.splice(currentPosition, 0, newAppIcon); - - // Skip animations on first run when adding the initial set - // of items, to avoid all items zooming in at once - newAppIcon.show(this._shownInitially); - } - - ++currentPosition; - } - } - - this._adjustIconSize(); - - // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 - // Without it, StBoxLayout may use a stale size cache - this._box.queue_relayout(); - - // This is required for icon reordering when the scrollview is used. - this._updateAppIcons(); - - // This will update the size, and the corresponding number for each icon on the primary panel - if (this.dtpPanel.isPrimary) { - this._updateNumberOverlay(); - } - - this._shownInitially = true; - } - - _getRunningApps() { - let tracker = Shell.WindowTracker.get_default(); - let windows = global.get_window_actors(); - let apps = []; - - for (let i = 0, l = windows.length; i < l; ++i) { - let app = tracker.get_window_app(windows[i].metaWindow); - - if (app && apps.indexOf(app) < 0) { - apps.push(app); - } - } - - return apps; - } - - _createAppInfos(apps, defaultWindows, defaultIsLauncher) { - if (this.allowSplitApps && !defaultIsLauncher) { - let separateApps = [] - - if (apps.length) { - let tracker = Shell.WindowTracker.get_default(); - let windows = AppIcons.getInterestingWindows(null, this.dtpPanel.monitor) - .sort(sortWindowsCompareFunction) - - windows.forEach(w => { - let windowApp = tracker.get_window_app(w) - - if (apps.indexOf(windowApp) >= 0) - separateApps.push({ - app: windowApp, - isLauncher: false, - windows: [w] - }) - }) - } - - return separateApps - } - - return apps.map(app => ({ - app: app, - isLauncher: defaultIsLauncher || false, - windows: defaultWindows || AppIcons.getInterestingWindows(app, this.dtpPanel.monitor) - .sort(sortWindowsCompareFunction) - })); - } - - // Reset the displayed apps icon to mantain the correct order - resetAppIcons (geometryChange) { - let children = this._getTaskbarIcons(true); - - for (let i = 0; i < children.length; i++) { - let item = children[i]; - item.destroy(); - } - - // to avoid ugly animations, just suppress them like when taskbar is first loaded. - this._shownInitially = false; - this._redisplay(); - - if (geometryChange && this.dtpPanel.checkIfVertical()) { - this.previewMenu._updateClip(); - } - } - - _updateNumberOverlay() { - let seenApps = {}; - let counter = 0; - - this._getAppIcons().forEach(icon => { - if (!seenApps[icon.app] || this.allowSplitApps) { - seenApps[icon.app] = 1; - counter++; - } - - if (counter <= 10) { - icon.setNumberOverlay(counter == 10 ? 0 : counter); - } else { - // No overlay after 10 - icon.setNumberOverlay(-1); - } - - icon.updateHotkeyNumberOverlay(); - }); - - if (SETTINGS.get_boolean('hot-keys') && - SETTINGS.get_string('hotkeys-overlay-combo') === 'ALWAYS') - this.toggleNumberOverlay(true); - } - - toggleNumberOverlay(activate) { - let appIcons = this._getAppIcons(); - appIcons.forEach(function(icon) { - icon.toggleNumberOverlay(activate); - }); - } - - _clearEmptyDropTarget() { - if (this._emptyDropTarget) { - this._emptyDropTarget.animateOutAndDestroy(); - this._emptyDropTarget = null; - } - } - - handleDragOver(source, actor, x, y, time) { - if (source == Main.xdndHandler) - return DND.DragMotionResult.CONTINUE; - - // Don't allow favoriting of transient apps - if (source.app == null || source.app.is_window_backed()) - return DND.DragMotionResult.NO_DROP; - - if (!this._settings.is_writable('favorite-apps')) - return DND.DragMotionResult.NO_DROP; - - let isVertical = this.dtpPanel.checkIfVertical(); - - if (!this._box.contains(source) && !source._dashItemContainer) { - //not an appIcon of the taskbar, probably from the applications view - source._dashItemContainer = new DragPlaceholderItem(source, this.iconSize, isVertical); - this._box.insert_child_above(source._dashItemContainer, null); - } - - let sizeProp = isVertical ? 'height' : 'width'; - let posProp = isVertical ? 'y' : 'x'; - let pos = isVertical ? y : x; - - let currentAppIcons = this._getAppIcons(); - let sourceIndex = currentAppIcons.indexOf(source); - let hoveredIndex = Utils.findIndex(currentAppIcons, - appIcon => pos >= appIcon._dashItemContainer[posProp] && - pos <= (appIcon._dashItemContainer[posProp] + appIcon._dashItemContainer[sizeProp])); - - if (!this._dragInfo) { - this._dragInfo = [sourceIndex, source]; - } - - if (hoveredIndex >= 0) { - let isLeft = pos < currentAppIcons[hoveredIndex]._dashItemContainer[posProp] + currentAppIcons[hoveredIndex]._dashItemContainer[sizeProp] * .5; - let prevIcon = currentAppIcons[hoveredIndex - 1] - let nextIcon = currentAppIcons[hoveredIndex + 1] - - // Don't allow positioning before or after self and between icons of same app if ungrouped and showing favorites - if (!(hoveredIndex === sourceIndex || - (isLeft && hoveredIndex - 1 == sourceIndex) || - (!this.allowSplitApps && isLeft && hoveredIndex - 1 >= 0 && source.app != prevIcon.app && - prevIcon.app == currentAppIcons[hoveredIndex].app) || - (!isLeft && hoveredIndex + 1 == sourceIndex) || - (!this.allowSplitApps && !isLeft && hoveredIndex + 1 < currentAppIcons.length && source.app != nextIcon.app && - nextIcon.app == currentAppIcons[hoveredIndex].app))) { - this._box.set_child_at_index(source._dashItemContainer, hoveredIndex); - - // Ensure the next and previous icon are visible when moving the icon - // (I assume there's room for both of them) - if (hoveredIndex > 1) - Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex-1], this._scrollView._dtpFadeSize); - if (hoveredIndex < this._box.get_children().length-1) - Utils.ensureActorVisibleInScrollView(this._scrollView, this._box.get_children()[hoveredIndex+1], this._scrollView._dtpFadeSize); - } - } - - return this._dragInfo[0] !== sourceIndex ? DND.DragMotionResult.MOVE_DROP : DND.DragMotionResult.CONTINUE; - } - - // Draggable target interface - acceptDrop (source, actor, x, y, time) { - // Don't allow favoriting of transient apps - if (!this._dragInfo || !source.app || source.app.is_window_backed() || - !this._settings.is_writable('favorite-apps')) { - return false; - } - - let appIcons = this._getAppIcons(); - let sourceIndex = appIcons.indexOf(source); - let usingLaunchers = !this.isGroupApps && this.usingLaunchers; - - // dragging the icon to its original position - if (this._dragInfo[0] === sourceIndex) { - return true; - } - - let appFavorites = AppFavorites.getAppFavorites(); - let sourceAppId = source.app.get_id(); - let appIsFavorite = this.showFavorites && appFavorites.isFavorite(sourceAppId); - let replacingIndex = sourceIndex + (sourceIndex > this._dragInfo[0] ? -1 : 1); - let favoriteIndex = replacingIndex >= 0 ? appFavorites.getFavorites().indexOf(appIcons[replacingIndex].app) : 0; - let sameApps = this.allowSplitApps ? [] : appIcons.filter(a => a != source && a.app == source.app); - let favoritesCount = 0; - let position = 0; - let interestingWindows = {}; - let getAppWindows = app => { - if (!interestingWindows[app]) { - interestingWindows[app] = AppIcons.getInterestingWindows(app, this.dtpPanel.monitor); - } - - let appWindows = interestingWindows[app]; //prevents "reference to undefined property Symbol.toPrimitive" warning - return appWindows; - }; - - if (sameApps.length && - ((!appIcons[sourceIndex - 1] || appIcons[sourceIndex - 1].app !== source.app) && - (!appIcons[sourceIndex + 1] || appIcons[sourceIndex + 1].app !== source.app))) { - appIcons.splice(appIcons.indexOf(sameApps[0]), sameApps.length); - Array.prototype.splice.apply(appIcons, [sourceIndex + 1, 0].concat(sameApps)); - } - - for (let i = 0, l = appIcons.length; i < l; ++i) { - let windows = []; - - if (!usingLaunchers || (!source.isLauncher && !appIcons[i].isLauncher)) { - windows = appIcons[i].window ? [appIcons[i].window] : getAppWindows(appIcons[i].app); - } - - windows.forEach(w => w._dtpPosition = position++); - - if (this.showFavorites && - ((usingLaunchers && appIcons[i].isLauncher) || - (!usingLaunchers && appFavorites.isFavorite(appIcons[i].app.get_id())))) { - ++favoritesCount; - } - } - - if (sourceIndex < favoritesCount) { - if (appIsFavorite) { - appFavorites.moveFavoriteToPos(sourceAppId, favoriteIndex); - } else { - appFavorites.addFavoriteAtPos(sourceAppId, favoriteIndex); - } - } else if (appIsFavorite && this.showFavorites && (!usingLaunchers || source.isLauncher)) { - appFavorites.removeFavorite(sourceAppId); - } - - appFavorites.emit('changed'); - - return true; - } - - _onShowAppsButtonToggled() { - // Sync the status of the default appButtons. Only if the two statuses are - // different, that means the user interacted with the extension provided - // application button, cutomize the behaviour. Otherwise the shell has changed the - // status (due to the _syncShowAppsButtonToggled function below) and it - // has already performed the desired action. - let selector = SearchController; - - if (selector._showAppsButton && - selector._showAppsButton.checked !== this.showAppsButton.checked) { - // find visible view - - if (this.showAppsButton.checked) { - //override escape key to return to the desktop when entering the overview using the showapps button - SearchController._onStageKeyPress = function(actor, event) { - if (Main.modalCount == 1 && event.get_key_symbol() === Clutter.KEY_Escape) { - this._searchActive ? this.reset() : Main.overview.hide(); - - return Clutter.EVENT_STOP; - } - - return Object.getPrototypeOf(this)._onStageKeyPress.call(this, actor, event); - }; - - let overviewHiddenId = Main.overview.connect('hidden', () => { - Main.overview.disconnect(overviewHiddenId); - delete SearchController._onStageKeyPress; - }); - - // force exiting overview if needed - if (!Main.overview._shown) { - this.forcedOverview = true; - } - - //temporarily use as primary the monitor on which the showapps btn was clicked, this is - //restored by the panel when exiting the overview - this.dtpPanel.panelManager.setFocusedMonitor(this.dtpPanel.monitor); - - // Finally show the overview - selector._showAppsButton.checked = true; - Main.overview.show(2 /*APP_GRID*/); - } - else { - if (this.forcedOverview) { - // force exiting overview if needed - Main.overview.hide(); - } else { - selector._showAppsButton.checked = false; - } - - this.forcedOverview = false; - } - } - } - - _syncShowAppsButtonToggled() { - let status = SearchController._showAppsButton.checked; - if (this.showAppsButton.checked !== status) - this.showAppsButton.checked = status; - } - - showShowAppsButton() { - this.showAppsButton.visible = true; - this.showAppsButton.set_width(-1); - this.showAppsButton.set_height(-1); - } - - popupFocusedAppSecondaryMenu() { - let appIcons = this._getAppIcons(); - let tracker = Shell.WindowTracker.get_default(); - - for(let i in appIcons) { - if(appIcons[i].app == tracker.focus_app) { - let appIcon = appIcons[i]; - if(appIcon._menu && appIcon._menu.isOpen) - appIcon._menu.close(); - else - appIcon.popupMenu(); - - appIcon.sync_hover(); - break; - } - } - } -}; - -export const TaskbarItemContainer = GObject.registerClass({ - -}, class TaskbarItemContainer extends Dash.DashItemContainer { - - _init() { - super._init() - this.x_expand = this.y_expand = false - } - - vfunc_allocate(box) { - if (this.child == null) - return; - - this.set_allocation(box); - - let availWidth = box.x2 - box.x1; - let availHeight = box.y2 - box.y1; - let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] = this.child.get_preferred_size(); - let [childScaleX, childScaleY] = this.child.get_scale(); - - let childWidth = Math.min(natChildWidth * childScaleX, availWidth); - let childHeight = Math.min(natChildHeight * childScaleY, availHeight); - let childBox = new Clutter.ActorBox(); - - childBox.x1 = (availWidth - childWidth) / 2; - childBox.y1 = (availHeight - childHeight) / 2; - childBox.x2 = childBox.x1 + childWidth; - childBox.y2 = childBox.y1 + childHeight; - - this.child.allocate(childBox); - } - - // In case appIcon is removed from the taskbar while it is hovered, - // restore opacity before dashItemContainer.animateOutAndDestroy does the destroy animation. - animateOutAndDestroy() { - if (this._raisedClone) { - this._raisedClone.source.opacity = 255; - this._raisedClone.destroy(); - } - - super.animateOutAndDestroy(); - } -}); - -const DragPlaceholderItem = GObject.registerClass({ -}, class DragPlaceholderItem extends St.Widget { - - _init(appIcon, iconSize, isVertical) { - super._init({ style: AppIcons.getIconContainerStyle(isVertical), layout_manager: new Clutter.BinLayout() }); - - this.child = { _delegate: appIcon }; - - this._clone = new Clutter.Clone({ - source: appIcon.icon._iconBin, - width: iconSize, - height: iconSize - }); - - this.add_child(this._clone); - } - - destroy() { - this._clone.destroy(); - super.destroy(); - } -}); - -export function getAppStableSequence(app, monitor) { - let windows = AppIcons.getInterestingWindows(app, monitor); - - return windows.reduce((prevWindow, window) => { - return Math.min(prevWindow, getWindowStableSequence(window)); - }, Infinity); -} - -export function sortWindowsCompareFunction(windowA, windowB) { - return getWindowStableSequence(windowA) - getWindowStableSequence(windowB); -} - -export function getWindowStableSequence(window) { - return ('_dtpPosition' in window ? window._dtpPosition : window.get_stable_sequence()); -} diff --git a/transparency.js b/transparency.js deleted file mode 100644 index 994299d..0000000 --- a/transparency.js +++ /dev/null @@ -1,200 +0,0 @@ -/* - * This file is part of the Zorin Taskbar extension for Zorin OS. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Credits: - * This file is based on code from the Dash to Panel extension - */ - -import GdkPixbuf from 'gi://GdkPixbuf'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import St from 'gi://St'; - -import * as Proximity from './proximity.js'; -import * as Utils from './utils.js'; -import {SETTINGS} from './extension.js'; - -const TRANS_DYNAMIC_DISTANCE = 20; - -export const DynamicTransparency = class { - - constructor(dtpPanel) { - this._dtpPanel = dtpPanel; - this._proximityManager = dtpPanel.panelManager.proximityManager; - this._proximityWatchId = 0; - this.currentBackgroundColor = 0; - - this._initialPanelStyle = dtpPanel.panel.get_style(); - - this._signalsHandler = new Utils.GlobalSignalsHandler(); - this._bindSignals(); - - this._updateAllAndSet(); - this._updateProximityWatch(); - } - - destroy() { - this._signalsHandler.destroy(); - this._proximityManager.removeWatch(this._proximityWatchId); - - this._dtpPanel.panel.set_style(this._initialPanelStyle); - } - - updateExternalStyle() { - this._updateComplementaryStyles(); - this._setBackground(); - } - - _bindSignals() { - this._signalsHandler.add( - [ - Utils.getStageTheme(), - 'changed', - () => this._updateAllAndSet() - ], - [ - Main.overview, - [ - 'showing', - 'hiding' - ], - () => this._updateAlphaAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-use-custom-opacity', - 'changed::trans-panel-opacity', - 'changed::trans-dynamic-anim-target', - 'changed::trans-use-dynamic-opacity' - ], - () => this._updateAlphaAndSet() - ], - [ - SETTINGS, - [ - 'changed::trans-dynamic-behavior', - 'changed::trans-use-dynamic-opacity' - ], - () => this._updateProximityWatch() - ] - ); - } - - _updateProximityWatch() { - this._proximityManager.removeWatch(this._proximityWatchId); - - if (SETTINGS.get_boolean('trans-use-dynamic-opacity')) { - let isVertical = this._dtpPanel.checkIfVertical(); - let threshold = TRANS_DYNAMIC_DISTANCE; - - this._windowOverlap = false; - this._updateAlphaAndSet() - - this._proximityWatchId = this._proximityManager.createWatch( - this._dtpPanel.panelBox.get_parent(), - this._dtpPanel.monitor.index, - Proximity.Mode[SETTINGS.get_string('trans-dynamic-behavior')], - isVertical ? threshold : 0, - isVertical ? 0 : threshold, - overlap => { - this._windowOverlap = overlap; - this._updateAlphaAndSet(); - } - ); - } - } - - _updateAllAndSet() { - let themeBackground = this._getThemeBackground(true); - - this._updateColor(themeBackground); - this._updateAlpha(themeBackground); - this._updateComplementaryStyles(); - this._setBackground(); - this._setActorStyle(); - } - - _updateAlphaAndSet() { - this._updateAlpha(); - this._setBackground(); - } - - _updateComplementaryStyles() { - let panelThemeNode = this._dtpPanel.panel.get_theme_node(); - - this._complementaryStyles = 'border-radius: ' + panelThemeNode.get_border_radius(0) + 'px;'; - } - - _updateColor(themeBackground) { - this.backgroundColorRgb = themeBackground || this._getThemeBackground(); - } - - _updateAlpha(themeBackground) { - if (this._windowOverlap && !Main.overview.visibleTarget && SETTINGS.get_boolean('trans-use-dynamic-opacity')) { - this.alpha = SETTINGS.get_double('trans-dynamic-anim-target'); - } else { - this.alpha = SETTINGS.get_boolean('trans-use-custom-opacity') ? - SETTINGS.get_double('trans-panel-opacity') : - (themeBackground || this._getThemeBackground()).alpha * 0.003921569; // 1 / 255 = 0.003921569 - } - } - - _setBackground() { - this.currentBackgroundColor = Utils.getrgbaColor(this.backgroundColorRgb, this.alpha); - - let transition = 'transition-duration: 300ms;'; - - this._dtpPanel.set_style('background-color: ' + this.currentBackgroundColor + transition + this._complementaryStyles); - } - - _setActorStyle() { - this._dtpPanel.panel.set_style( - 'background: none; ' + - 'border-image: none; ' + - 'background-image: none; ' + - 'transition-duration: 300ms;' - ); - } - - _getThemeBackground(reload) { - if (reload || !this._themeBackground) { - let fakePanel = new St.Bin({ name: 'panel' }); - Main.uiGroup.add_child(fakePanel); - let fakeTheme = fakePanel.get_theme_node() - this._themeBackground = this._getBackgroundImageColor(fakeTheme) || fakeTheme.get_background_color(); - Main.uiGroup.remove_child(fakePanel); - } - - return this._themeBackground; - } - - _getBackgroundImageColor(theme) { - let bg = null; - - try { - let imageFile = theme.get_background_image() || theme.get_border_image().get_file(); - - if (imageFile) { - let imageBuf = GdkPixbuf.Pixbuf.new_from_file(imageFile.get_path()); - let pixels = imageBuf.get_pixels(); - - bg = { red: pixels[0], green: pixels[1], blue: pixels[2], alpha: pixels[3] }; - } - } catch (error) {} - - return bg; - } -} diff --git a/ui/BoxIntellihideOptions.ui b/ui/BoxIntellihideOptions.ui index 80669ae..81eb2e1 100644 --- a/ui/BoxIntellihideOptions.ui +++ b/ui/BoxIntellihideOptions.ui @@ -1,19 +1,18 @@ - + + - + - vertical - 600 - 24 - 32 32 - 32 32 - + 32 + 32 + vertical + 24 + 600 - Only hide the panel when it is obstructed by windows @@ -24,7 +23,6 @@ - The panel hides from @@ -40,13 +38,10 @@ - - - Require pressure at the edge of the screen to reveal the panel @@ -57,13 +52,10 @@ - - - Allow the panel to be revealed while in fullscreen mode @@ -74,11 +66,10 @@ - - Only hide secondary panels (requires multi-monitors option) + Only hide secondary panels center @@ -86,24 +77,41 @@ - - Keyboard shortcut to reveal and hold the panel Syntax: &lt;Shift&gt;, &lt;Ctrl&gt;, &lt;Alt&gt;, &lt;Super&gt; + Keyboard shortcut to reveal and hold the panel + e.g. <Super>i + center + 12 + + + + + + + Persist state across restarts + + + center + + + + + + + (respects "Do Not Disturb" and requires show notification counter badge option) + Reveal and hold the panel on notification + + center - 12 - e.g. <Super>i - - - diff --git a/ui/BoxMiddleClickOptions.ui b/ui/BoxMiddleClickOptions.ui index 023845e..5ce94db 100644 --- a/ui/BoxMiddleClickOptions.ui +++ b/ui/BoxMiddleClickOptions.ui @@ -29,6 +29,7 @@ Cycle windows + minimize Toggle single / Preview multiple Toggle single / Cycle multiple + Toggle single / Spread multiple Quit @@ -57,6 +58,7 @@ Cycle windows + minimize Toggle single / Preview multiple Toggle single / Cycle multiple + Toggle single / Spread multiple Quit @@ -85,6 +87,7 @@ Cycle windows + minimize Toggle single / Preview multiple Toggle single / Cycle multiple + Toggle single / Spread multiple Quit @@ -97,4 +100,4 @@ - \ No newline at end of file + diff --git a/ui/BoxOverlayShortcut.ui b/ui/BoxOverlayShortcut.ui index 6ca0704..0f31552 100644 --- a/ui/BoxOverlayShortcut.ui +++ b/ui/BoxOverlayShortcut.ui @@ -1,23 +1,22 @@ - + + - + - vertical - 600 - 24 - 32 32 - 32 32 - + 32 + 32 + vertical + 24 + 600 - - Hotkeys prefix Hotkeys will either be Super+Number or Super+Alt+Num + Hotkeys prefix center @@ -29,11 +28,10 @@ - - Number overlay Temporarily show the application numbers over the icons when using the hotkeys. + Number overlay center @@ -46,37 +44,34 @@ - - Shortcut to show the overlay for 2 seconds Syntax: &lt;Shift&gt;, &lt;Ctrl&gt;, &lt;Alt&gt;, &lt;Super&gt; + Shortcut to show the overlay for 2 seconds + e.g. <Super>q center - 12 - e.g. <Super>q + 12 - - Show window previews on hotkey Show previews when the application have multiple instances + Show window previews on hotkey - - center - - + + center + + - - Hotkeys are activated with Select which keyboard number keys are used to activate the hotkeys + Hotkeys are activated with center @@ -89,9 +84,7 @@ - - diff --git a/ui/SettingsAction.ui b/ui/SettingsAction.ui index 758f6a2..a763d75 100644 --- a/ui/SettingsAction.ui +++ b/ui/SettingsAction.ui @@ -1,27 +1,25 @@ - + + - + + input-mouse-symbolic Action - view-pin-symbolic - - Click action - - Click action Behaviour when clicking on the icon of a running application. + Click action - True + True center - emblem-system-symbolic + emblem-system-symbolic