From 557250216ae97957329da4057ed4126cae84890f Mon Sep 17 00:00:00 2001 From: Artyom Zorin Date: Fri, 2 May 2025 14:22:16 +0100 Subject: [PATCH] Bump to version 68.1 --- .gitignore | 1 + Makefile | 16 +- appIcons.js | 1666 -------- debian/changelog | 18 + desktopIconsIntegration.js | 165 - extension.js | 132 - intellihide.js | 434 -- metadata.json | 4 +- overview.js | 455 --- panel.js | 1257 ------ panelManager.js | 756 ---- panelPositions.js | 64 - panelSettings.js | 111 - panelStyle.js | 161 - po/af.po | 3 - po/ar.po | 3 - po/be.po | 3 - po/bg.po | 3 - po/bn.po | 3 - po/ca.po | 3 - po/cs.po | 3 - po/da.po | 3 - po/de.po | 3 - po/el.po | 3 - po/es.po | 3 - po/et.po | 3 - po/fa.po | 3 - po/fi.po | 3 - po/fr.po | 1392 ++++--- po/ga.po | 3 - po/gl.po | 3 - po/gu.po | 3 - po/he.po | 3 - po/hi.po | 3 - po/hr.po | 3 - po/hu.po | 3 - po/id.po | 3 - po/is.po | 3 - po/it.po | 3 - po/ja.po | 3 - po/ka.po | 3 - po/kn.po | 3 - po/ko.po | 3 - po/lt.po | 3 - po/lv.po | 3 - po/mk.po | 3 - po/ms.po | 3 - po/ne.po | 3 - po/nl.po | 3 - po/pa.po | 3 - po/pl.po | 3514 ++++++++++++----- po/pt.po | 3 - po/pt_BR.po | 3 - po/ru.po | 3 - po/sk.po | 3 - po/sl.po | 3 - po/sq.po | 3 - po/sr.po | 3 - po/sv.po | 3 - po/ta.po | 3 - po/te.po | 3 - po/th.po | 3 - po/tr.po | 3 - po/uk.po | 1278 +++--- po/vi.po | 3 - po/zh_CN.po | 3 - po/zh_TW.po | 3 - prefs.js | 1170 ------ progress.js | 601 --- proximity.js | 260 -- ...shell.extensions.zorin-taskbar.gschema.xml | 40 +- src/appIcons.js | 2030 ++++++++++ src/desktopIconsIntegration.js | 171 + src/extension.js | 175 + src/intellihide.js | 529 +++ src/notificationsMonitor.js | 216 + src/overview.js | 518 +++ src/panel.js | 1544 ++++++++ src/panelManager.js | 1083 +++++ src/panelPositions.js | 70 + src/panelSettings.js | 308 ++ src/panelStyle.js | 168 + src/prefs.js | 1672 ++++++++ src/proximity.js | 285 ++ stylesheet.css => src/stylesheet.css | 87 +- src/taskbar.js | 1479 +++++++ src/transparency.js | 199 + src/utils.js | 969 +++++ src/windowPreview.js | 1362 +++++++ taskbar.js | 1282 ------ transparency.js | 200 - ui/BoxIntellihideOptions.ui | 58 +- ui/BoxMiddleClickOptions.ui | 5 +- ui/BoxOverlayShortcut.ui | 45 +- ui/SettingsAction.ui | 44 +- ui/SettingsBehavior.ui | 2 +- ui/SettingsPosition.ui | 363 +- ui/SettingsStyle.ui | 89 +- utils.js | 889 ----- windowPreview.js | 1144 ------ 100 files changed, 17281 insertions(+), 13350 deletions(-) delete mode 100644 appIcons.js delete mode 100644 desktopIconsIntegration.js delete mode 100644 extension.js delete mode 100644 intellihide.js delete mode 100644 overview.js delete mode 100644 panel.js delete mode 100644 panelManager.js delete mode 100644 panelPositions.js delete mode 100644 panelSettings.js delete mode 100644 panelStyle.js delete mode 100644 prefs.js delete mode 100644 progress.js delete mode 100644 proximity.js create mode 100644 src/appIcons.js create mode 100644 src/desktopIconsIntegration.js create mode 100644 src/extension.js create mode 100644 src/intellihide.js create mode 100644 src/notificationsMonitor.js create mode 100644 src/overview.js create mode 100644 src/panel.js create mode 100644 src/panelManager.js create mode 100644 src/panelPositions.js create mode 100644 src/panelSettings.js create mode 100644 src/panelStyle.js create mode 100644 src/prefs.js create mode 100644 src/proximity.js rename stylesheet.css => src/stylesheet.css (66%) create mode 100644 src/taskbar.js create mode 100644 src/transparency.js create mode 100644 src/utils.js create mode 100644 src/windowPreview.js delete mode 100644 taskbar.js delete mode 100644 transparency.js delete mode 100644 utils.js delete mode 100644 windowPreview.js 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